diff --git a/app/application.coffee b/app/application.coffee index 3cef214..d3df822 100644 --- a/app/application.coffee +++ b/app/application.coffee @@ -36,11 +36,7 @@ class Application extends Mn.Application @contacts = new ContactsCollection() @filtered = new FilteredCollection() - @tags = new TagsCollection(@contacts) - - # As long as Tags require Contacts to be loaded (see Tags.refs), we - # trigger all TagsCollection fetch after syncing ContactsCollection. - @listenToOnce @contacts, 'sync': -> @tags.underlying.fetch reset: true + @tags = new TagsCollection @contacts @layout = new AppLayout model: @model @router = new Router() @@ -49,10 +45,19 @@ class Application extends Mn.Application Object.freeze @ if typeof Object.freeze is 'function' + # render components when app starts onStart: -> @layout.render() - @contacts.fetch reset: true + + callback = (models, collection) => + console.log 'SUCCESS', models + + # As long as Tags require Contacts to be loaded (see Tags.refs), we + # trigger all TagsCollection fetch after syncing ContactsCollection. + # @tags.underlying.fetch {reset: true} + + @contacts.fetch {reset: true}, callback # prohibit pushState because URIs mapped from cozy-home rely on # fragment diff --git a/app/collections/contacts.coffee b/app/collections/contacts.coffee index 48e4166..7fe76fd 100644 --- a/app/collections/contacts.coffee +++ b/app/collections/contacts.coffee @@ -5,7 +5,6 @@ Contact = require 'models/contact' module.exports = class Contacts extends Backbone.Collection model: Contact - url: 'contacts' sortDisabled: false @@ -20,6 +19,31 @@ module.exports = class Contacts extends Backbone.Collection @listenToOnce @, 'sync', -> @contactListener.watch @ + fetch: (options={}, success = -> ) -> + # This event is listened by layout + # to display applications views + @trigger 'sync', options + + @reset() if options.reset + + # FIXME: remove this line when + # cozy.query will works + @add [] + + success @models, @ + + # cozy.init { isV2: true } + # cozy.defineIndex 'io.cozy.contacts', ['id'] + # .then (index) => + # cozy.query index, { selector: id: {"$gte": " "} } + # .then (result=[]) => + # @add result + # success result + # , (err) => + # success null, err + + + # Turns a vcard text into a list of contacts. Then it saves every contacts # in the data system. Finally it reloads the contact list. importFromVCF: (vcard) -> diff --git a/app/collections/filtered.coffee b/app/collections/filtered.coffee index a6304a1..b7eb653 100644 --- a/app/collections/filtered.coffee +++ b/app/collections/filtered.coffee @@ -29,6 +29,7 @@ module.exports = class FilteredCollection query: null + constructor: -> app = require 'application' diff --git a/app/collections/tags.coffee b/app/collections/tags.coffee index 70f96b3..8d9cf06 100644 --- a/app/collections/tags.coffee +++ b/app/collections/tags.coffee @@ -16,8 +16,6 @@ class Tags extends Backbone.Collection model: require 'models/tag' - url: 'tags' - initialize: -> @listenToOnce @, 'sync', -> (new TagsListener()).watch @ @@ -27,6 +25,12 @@ class Tags extends Backbone.Collection _.uniq tags, (tag) -> tag.name.toLowerCase() + fetch: (success) -> + # TODO: add fake data + # TODO: add cozy-client-js code + console.log "(fetch) TAGS" + + # Export TagsCollection is the Contacts aware projection. All tags Collection # can be accessed at `underlying` property. module.exports = class FilteredTags extends Filtered @@ -49,7 +53,7 @@ module.exports = class FilteredTags extends Filtered # concact tags. updateRefs: -> @refs = @contacts.chain() - .map (model) -> model.get 'tags' + .map (model) -> model.get('tags') or [] .flatten() .uniq() .value() diff --git a/app/models/config.coffee b/app/models/config.coffee index 36b3504..8f1963f 100644 --- a/app/models/config.coffee +++ b/app/models/config.coffee @@ -1,3 +1,4 @@ module.exports = class Config extends Backbone.Model - url: 'config' + fetch: (success) -> + console.log "(fetch) CONFIG" diff --git a/app/models/contact.coffee b/app/models/contact.coffee index 368897b..eee4684 100644 --- a/app/models/contact.coffee +++ b/app/models/contact.coffee @@ -2,6 +2,7 @@ app = undefined class Datapoint extends Backbone.Model + defaults: name: 'other' type: 'other' @@ -11,6 +12,7 @@ class Datapoint extends Backbone.Model # represents an e-mail address from that of a cloud. mediatype: '' + parse: (attrs) -> if attrs.name is 'adr' and _.isArray attrs.value attrs.value = VCardParser.adrArrayToString attrs.value @@ -19,8 +21,6 @@ class Datapoint extends Backbone.Model module.exports = class Contact extends Backbone.Model - urlRoot: 'contacts' - defaults: n: ';;;;' @@ -86,7 +86,8 @@ module.exports = class Contact extends Backbone.Model validate: (attrs, options) -> - # Handle specific attributes. + console.log "PLOP", attrs, options + Handle specific attributes. attrs.fn = VCardParser.nToFN attrs.n.split ';' datapoints = (attrs.datapoints?.toJSON() or []) @@ -151,7 +152,7 @@ module.exports = class Contact extends Backbone.Model # We override toJSON to ensure the exported object is the a true object. # Allow a options.toVCF boolean to sanitize the output before. toJSON: (options) -> - _.extend super, datapoints: @attributes.datapoints.toJSON() + _.extend super, datapoints: @attributes.datapoints?.toJSON() toHighlightedString: (pattern, opts= {})-> @@ -239,6 +240,7 @@ module.exports = class Contact extends Backbone.Model toVCF: (callback) -> + console.log "TO_VCF" data = @toJSON() # Because viewModel call uses the saveAs method, that broke console diff --git a/app/models/tag.coffee b/app/models/tag.coffee index 52e17a0..f7b2fdd 100644 --- a/app/models/tag.coffee +++ b/app/models/tag.coffee @@ -1,6 +1,5 @@ module.exports = class Tag extends Backbone.Model - urlRoot: 'tags' parse: (attrs) -> attrs.name = attrs.name.toLowerCase() diff --git a/app/routes/index.coffee b/app/routes/index.coffee index 4cbe917..ca6b6e7 100644 --- a/app/routes/index.coffee +++ b/app/routes/index.coffee @@ -46,6 +46,6 @@ module.exports = class Router extends Backbone.Router settings: -> app.model.set 'dialog', 'settings' + legacyContactRedirect: (slug) -> @navigate "contacts/#{slug}", trigger: true - diff --git a/app/views/app_layout.coffee b/app/views/app_layout.coffee index ef42107..dfc7ffd 100644 --- a/app/views/app_layout.coffee +++ b/app/views/app_layout.coffee @@ -62,6 +62,7 @@ module.exports = class AppLayout extends Mn.LayoutView 'select:end': 'hideContextualMenu' 'change:scored': 'onSearchResults' + childEvents: 'drawer:toggle': 'toggleDrawer' 'contacts:select': (contactsView, id) -> @model.select id @@ -225,5 +226,5 @@ module.exports = class AppLayout extends Mn.LayoutView if err is ERR.SEARCH_TOO_SHORT @results.$el.attr 'aria-hidden', true @ui.counter - .text t 'error search too short' - .addClass 'important' + .text t 'error search too short' + .addClass 'important' diff --git a/app/views/contacts/index.coffee b/app/views/contacts/index.coffee index e13eb54..cbaf774 100644 --- a/app/views/contacts/index.coffee +++ b/app/views/contacts/index.coffee @@ -1,4 +1,3 @@ -ContactViewModel = require 'views/models/contact' ContactRowView = require 'views/contacts/row' Slugifier = require 'lib/slugifier' diff --git a/app/views/models/app.coffee b/app/views/models/app.coffee index 7e5acc3..629f2bd 100644 --- a/app/views/models/app.coffee +++ b/app/views/models/app.coffee @@ -30,6 +30,7 @@ module.exports = class AppViewModel extends Backbone.ViewModel app = require 'application' @set 'selected', [] + @listenTo @, 'change:filter', (nil, value) -> @unselectAll() @set 'scored', CONFIG.search.pattern('text').test value diff --git a/app/views/models/contact.coffee b/app/views/models/contact.coffee index f7d3adc..ea38329 100644 --- a/app/views/models/contact.coffee +++ b/app/views/models/contact.coffee @@ -12,6 +12,7 @@ module.exports = class ContactViewModel extends Backbone.ViewModel new: false edit: false + map: avatar: '_attachments' name: 'n' @@ -22,6 +23,7 @@ module.exports = class ContactViewModel extends Backbone.ViewModel save: 'onSave' reset: 'onReset' + viewEvents: 'edit:cancel': 'reset' 'tags:update': 'updateTags' @@ -105,7 +107,7 @@ module.exports = class ContactViewModel extends Backbone.ViewModel getIndexKey: -> sortIdx = if app.model.get('sort') is 'fn' then 0 else 1 - initial = @get('initials')[sortIdx]?.toLowerCase() or '' + initial = @get('initials')?[sortIdx]?.toLowerCase() or '' if /[a-z]/.test initial then initial else '#' diff --git a/config.coffee b/config.coffee index 85b51e9..bfb0e37 100644 --- a/config.coffee +++ b/config.coffee @@ -15,6 +15,7 @@ exports.config = 'vendor/marionette/backbone.radio.js' 'vendor/marionette/backbone.marionette.js' 'vendor/marionette/radio.shim.js' + 'vendor/cozy-client-0.0.1.js' ] stylesheets: diff --git a/vendor/cozy-client-0.0.1.js b/vendor/cozy-client-0.0.1.js new file mode 100644 index 0000000..c0d8648 --- /dev/null +++ b/vendor/cozy-client-0.0.1.js @@ -0,0 +1,2307 @@ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define("cozy-client-js", [], factory); + else if(typeof exports === 'object') + exports["cozy-client-js"] = factory(); + else + root["cozy-client-js"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; +/******/ +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.loaded = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + __webpack_require__(1); + module.exports = __webpack_require__(3); + + +/***/ }, +/* 1 */ +/***/ function(module, exports, __webpack_require__) { + + // the whatwg-fetch polyfill installs the fetch() function + // on the global object (window or self) + // + // Return that as the export for use in Webpack, Browserify etc. + __webpack_require__(2); + module.exports = self.fetch.bind(self); + + +/***/ }, +/* 2 */ +/***/ function(module, exports) { + + (function(self) { + 'use strict'; + + if (self.fetch) { + return + } + + var support = { + searchParams: 'URLSearchParams' in self, + iterable: 'Symbol' in self && 'iterator' in Symbol, + blob: 'FileReader' in self && 'Blob' in self && (function() { + try { + new Blob() + return true + } catch(e) { + return false + } + })(), + formData: 'FormData' in self, + arrayBuffer: 'ArrayBuffer' in self + } + + function normalizeName(name) { + if (typeof name !== 'string') { + name = String(name) + } + if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) { + throw new TypeError('Invalid character in header field name') + } + return name.toLowerCase() + } + + function normalizeValue(value) { + if (typeof value !== 'string') { + value = String(value) + } + return value + } + + // Build a destructive iterator for the value list + function iteratorFor(items) { + var iterator = { + next: function() { + var value = items.shift() + return {done: value === undefined, value: value} + } + } + + if (support.iterable) { + iterator[Symbol.iterator] = function() { + return iterator + } + } + + return iterator + } + + function Headers(headers) { + this.map = {} + + if (headers instanceof Headers) { + headers.forEach(function(value, name) { + this.append(name, value) + }, this) + + } else if (headers) { + Object.getOwnPropertyNames(headers).forEach(function(name) { + this.append(name, headers[name]) + }, this) + } + } + + Headers.prototype.append = function(name, value) { + name = normalizeName(name) + value = normalizeValue(value) + var list = this.map[name] + if (!list) { + list = [] + this.map[name] = list + } + list.push(value) + } + + Headers.prototype['delete'] = function(name) { + delete this.map[normalizeName(name)] + } + + Headers.prototype.get = function(name) { + var values = this.map[normalizeName(name)] + return values ? values[0] : null + } + + Headers.prototype.getAll = function(name) { + return this.map[normalizeName(name)] || [] + } + + Headers.prototype.has = function(name) { + return this.map.hasOwnProperty(normalizeName(name)) + } + + Headers.prototype.set = function(name, value) { + this.map[normalizeName(name)] = [normalizeValue(value)] + } + + Headers.prototype.forEach = function(callback, thisArg) { + Object.getOwnPropertyNames(this.map).forEach(function(name) { + this.map[name].forEach(function(value) { + callback.call(thisArg, value, name, this) + }, this) + }, this) + } + + Headers.prototype.keys = function() { + var items = [] + this.forEach(function(value, name) { items.push(name) }) + return iteratorFor(items) + } + + Headers.prototype.values = function() { + var items = [] + this.forEach(function(value) { items.push(value) }) + return iteratorFor(items) + } + + Headers.prototype.entries = function() { + var items = [] + this.forEach(function(value, name) { items.push([name, value]) }) + return iteratorFor(items) + } + + if (support.iterable) { + Headers.prototype[Symbol.iterator] = Headers.prototype.entries + } + + function consumed(body) { + if (body.bodyUsed) { + return Promise.reject(new TypeError('Already read')) + } + body.bodyUsed = true + } + + function fileReaderReady(reader) { + return new Promise(function(resolve, reject) { + reader.onload = function() { + resolve(reader.result) + } + reader.onerror = function() { + reject(reader.error) + } + }) + } + + function readBlobAsArrayBuffer(blob) { + var reader = new FileReader() + reader.readAsArrayBuffer(blob) + return fileReaderReady(reader) + } + + function readBlobAsText(blob) { + var reader = new FileReader() + reader.readAsText(blob) + return fileReaderReady(reader) + } + + function Body() { + this.bodyUsed = false + + this._initBody = function(body) { + this._bodyInit = body + if (typeof body === 'string') { + this._bodyText = body + } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { + this._bodyBlob = body + } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { + this._bodyFormData = body + } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { + this._bodyText = body.toString() + } else if (!body) { + this._bodyText = '' + } else if (support.arrayBuffer && ArrayBuffer.prototype.isPrototypeOf(body)) { + // Only support ArrayBuffers for POST method. + // Receiving ArrayBuffers happens via Blobs, instead. + } else { + throw new Error('unsupported BodyInit type') + } + + if (!this.headers.get('content-type')) { + if (typeof body === 'string') { + this.headers.set('content-type', 'text/plain;charset=UTF-8') + } else if (this._bodyBlob && this._bodyBlob.type) { + this.headers.set('content-type', this._bodyBlob.type) + } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { + this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8') + } + } + } + + if (support.blob) { + this.blob = function() { + var rejected = consumed(this) + if (rejected) { + return rejected + } + + if (this._bodyBlob) { + return Promise.resolve(this._bodyBlob) + } else if (this._bodyFormData) { + throw new Error('could not read FormData body as blob') + } else { + return Promise.resolve(new Blob([this._bodyText])) + } + } + + this.arrayBuffer = function() { + return this.blob().then(readBlobAsArrayBuffer) + } + + this.text = function() { + var rejected = consumed(this) + if (rejected) { + return rejected + } + + if (this._bodyBlob) { + return readBlobAsText(this._bodyBlob) + } else if (this._bodyFormData) { + throw new Error('could not read FormData body as text') + } else { + return Promise.resolve(this._bodyText) + } + } + } else { + this.text = function() { + var rejected = consumed(this) + return rejected ? rejected : Promise.resolve(this._bodyText) + } + } + + if (support.formData) { + this.formData = function() { + return this.text().then(decode) + } + } + + this.json = function() { + return this.text().then(JSON.parse) + } + + return this + } + + // HTTP methods whose capitalization should be normalized + var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] + + function normalizeMethod(method) { + var upcased = method.toUpperCase() + return (methods.indexOf(upcased) > -1) ? upcased : method + } + + function Request(input, options) { + options = options || {} + var body = options.body + if (Request.prototype.isPrototypeOf(input)) { + if (input.bodyUsed) { + throw new TypeError('Already read') + } + this.url = input.url + this.credentials = input.credentials + if (!options.headers) { + this.headers = new Headers(input.headers) + } + this.method = input.method + this.mode = input.mode + if (!body) { + body = input._bodyInit + input.bodyUsed = true + } + } else { + this.url = input + } + + this.credentials = options.credentials || this.credentials || 'omit' + if (options.headers || !this.headers) { + this.headers = new Headers(options.headers) + } + this.method = normalizeMethod(options.method || this.method || 'GET') + this.mode = options.mode || this.mode || null + this.referrer = null + + if ((this.method === 'GET' || this.method === 'HEAD') && body) { + throw new TypeError('Body not allowed for GET or HEAD requests') + } + this._initBody(body) + } + + Request.prototype.clone = function() { + return new Request(this) + } + + function decode(body) { + var form = new FormData() + body.trim().split('&').forEach(function(bytes) { + if (bytes) { + var split = bytes.split('=') + var name = split.shift().replace(/\+/g, ' ') + var value = split.join('=').replace(/\+/g, ' ') + form.append(decodeURIComponent(name), decodeURIComponent(value)) + } + }) + return form + } + + function headers(xhr) { + var head = new Headers() + var pairs = (xhr.getAllResponseHeaders() || '').trim().split('\n') + pairs.forEach(function(header) { + var split = header.trim().split(':') + var key = split.shift().trim() + var value = split.join(':').trim() + head.append(key, value) + }) + return head + } + + Body.call(Request.prototype) + + function Response(bodyInit, options) { + if (!options) { + options = {} + } + + this.type = 'default' + this.status = options.status + this.ok = this.status >= 200 && this.status < 300 + this.statusText = options.statusText + this.headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers) + this.url = options.url || '' + this._initBody(bodyInit) + } + + Body.call(Response.prototype) + + Response.prototype.clone = function() { + return new Response(this._bodyInit, { + status: this.status, + statusText: this.statusText, + headers: new Headers(this.headers), + url: this.url + }) + } + + Response.error = function() { + var response = new Response(null, {status: 0, statusText: ''}) + response.type = 'error' + return response + } + + var redirectStatuses = [301, 302, 303, 307, 308] + + Response.redirect = function(url, status) { + if (redirectStatuses.indexOf(status) === -1) { + throw new RangeError('Invalid status code') + } + + return new Response(null, {status: status, headers: {location: url}}) + } + + self.Headers = Headers + self.Request = Request + self.Response = Response + + self.fetch = function(input, init) { + return new Promise(function(resolve, reject) { + var request + if (Request.prototype.isPrototypeOf(input) && !init) { + request = input + } else { + request = new Request(input, init) + } + + var xhr = new XMLHttpRequest() + + function responseURL() { + if ('responseURL' in xhr) { + return xhr.responseURL + } + + // Avoid security warnings on getResponseHeader when not allowed by CORS + if (/^X-Request-URL:/m.test(xhr.getAllResponseHeaders())) { + return xhr.getResponseHeader('X-Request-URL') + } + + return + } + + xhr.onload = function() { + var options = { + status: xhr.status, + statusText: xhr.statusText, + headers: headers(xhr), + url: responseURL() + } + var body = 'response' in xhr ? xhr.response : xhr.responseText + resolve(new Response(body, options)) + } + + xhr.onerror = function() { + reject(new TypeError('Network request failed')) + } + + xhr.ontimeout = function() { + reject(new TypeError('Network request failed')) + } + + xhr.open(request.method, request.url, true) + + if (request.credentials === 'include') { + xhr.withCredentials = true + } + + if ('responseType' in xhr && support.blob) { + xhr.responseType = 'blob' + } + + request.headers.forEach(function(value, name) { + xhr.setRequestHeader(name, value) + }) + + xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit) + }) + } + self.fetch.polyfill = true + })(typeof self !== 'undefined' ? self : this); + + +/***/ }, +/* 3 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.MemoryStorage = exports.LocalStorage = exports.Cozy = undefined; + + var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + + var _utils = __webpack_require__(4); + + var _fetch = __webpack_require__(5); + + var _auth_storage = __webpack_require__(8); + + var _auth_v = __webpack_require__(9); + + var _auth_v2 = __webpack_require__(6); + + var auth = _interopRequireWildcard(_auth_v2); + + var _crud = __webpack_require__(10); + + var crud = _interopRequireWildcard(_crud); + + var _mango = __webpack_require__(12); + + var mango = _interopRequireWildcard(_mango); + + var _files = __webpack_require__(13); + + var files = _interopRequireWildcard(_files); + + function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var AccessTokenV3 = auth.AccessToken, + ClientV3 = auth.Client; + + + var AuthNone = 0; + var AuthRunning = 1; + var AuthError = 2; + var AuthOK = 3; + + var mainProto = { + create: crud.create, + find: crud.find, + update: crud.update, + delete: crud._delete, + updateAttributes: crud.updateAttributes, + defineIndex: mango.defineIndex, + query: mango.query, + destroy: function destroy() { + (0, _utils.warn)('destroy is deprecated, use cozy.delete instead.'); + return crud._delete.apply(crud, arguments); + } + }; + + var authProto = { + registerClient: auth.registerClient, + getClient: auth.getClient, + getAuthCodeURL: auth.getAuthCodeURL, + getAccessToken: auth.getAccessToken, + refreshToken: auth.refreshToken + }; + + var filesProto = { + create: files.create, + createDirectory: files.createDirectory, + updateById: files.updateById, + updateAttributesById: files.updateAttributesById, + updateAttributesByPath: files.updateAttributesByPath, + trashById: files.trashById, + statById: files.statById, + statByPath: files.statByPath, + downloadById: files.downloadById, + downloadByPath: files.downloadByPath + }; + + var Cozy = function () { + function Cozy(options) { + _classCallCheck(this, Cozy); + + this.files = {}; + this.auth = { + Client: ClientV3, + AccessToken: AccessTokenV3, + AccessTokenV2: _auth_v.AccessToken, + LocalStorage: _auth_storage.LocalStorage, + MemoryStorage: _auth_storage.MemoryStorage + }; + var disablePromises = !!(options && options.disablePromises); + addToProto(this, this, mainProto, disablePromises); + addToProto(this, this.auth, authProto, disablePromises); + addToProto(this, this.files, filesProto, disablePromises); + + this.init(options); + } + + _createClass(Cozy, [{ + key: 'init', + value: function init() { + var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + + this._authstate = AuthNone; + this._authcreds = null; + this.isV2 = options.isV2 === true; + this.storage = null; + + var creds = options.credentials; + if (creds) { + var client = creds.client, + token = creds.token; + + var isV3Credentials = client instanceof ClientV3 && token instanceof AccessTokenV3; + + var isV2Credentials = token instanceof _auth_v.AccessToken; + + if (this.isV2 && !isV2Credentials) { + throw new Error('Bad credentials'); + } + if (!this.isV2 && !isV3Credentials) { + throw new Error('Bad credentials'); + } + + this._authstate = AuthOK; + this._authcreds = Promise.resolve(creds); + } + + if (options.credentialsStorage) { + this.storage = options.credentialsStorage; + } + + this._pageURL = options.pageURL || ''; + this._createClient = options.createClient || nopCreateClient; + this._onRegistered = options.onRegistered || nopOnRegistered; + + var url = options.url || ''; + while (url[url.length - 1] === '/') { + url = url.slice(0, -1); + } + + this.url = url; + } + }, { + key: 'authorize', + value: function authorize() { + var _this = this; + + var state = this._authstate; + if (state === AuthOK || state === AuthRunning) { + return this._authcreds; + } + + this._authstate = AuthRunning; + if (this.isV2) { + this._authcreds = (0, _auth_v.getAccessToken)(); + } else if (this.storage) { + this._authcreds = auth.oauthFlow(this, this.storage, this._pageURL, this._createClient, this._onRegistered); + } else { + return Promise.reject(new Error('Missing credentials')); + } + + this._authcreds.then(function () { + _this._authstate = AuthOK; + }, function () { + _this._authstate = AuthError; + }); + + return this._authcreds; + } + }, { + key: 'saveCredentials', + value: function saveCredentials(client, token) { + var creds = { client: client, token: token }; + if (!this.storage || this._authstate === AuthRunning) { + return Promise.resolve(creds); + } + this.storage.save(auth.CredsKey, creds); + this._authcreds = Promise.resolve(creds); + return this._authcreds; + } + }, { + key: 'fullpath', + value: function fullpath(path) { + var pathprefix = this.isV2 ? '/ds-api' : ''; + return this.url + pathprefix + path; + } + }, { + key: 'checkIfV2', + value: function checkIfV2() { + return (0, _fetch.cozyFetchJSON)(cozy, 'GET', '/status/').then(function (status) { + return status.datasystem !== undefined; + }); + } + }]); + + return Cozy; + }(); + + function nopCreateClient() { + throw new Error('No "createClient" function given'); + } + + function nopOnRegistered() {} + + function protoify(context, fn) { + return function prototyped() { + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return fn.apply(undefined, [context].concat(args)); + }; + } + + function addToProto(ctx, obj, proto, disablePromises) { + for (var attr in proto) { + var fn = protoify(ctx, proto[attr]); + if (disablePromises) { + fn = (0, _utils.unpromiser)(fn); + } + obj[attr] = fn; + } + } + + var cozy = new Cozy(); + + exports.default = cozy; + exports.Cozy = Cozy; + exports.LocalStorage = _auth_storage.LocalStorage; + exports.MemoryStorage = _auth_storage.MemoryStorage; + + + if (typeof window !== 'undefined') { + window.cozy = cozy; + window.Cozy = Cozy; + } + +/***/ }, +/* 4 */ +/***/ function(module, exports) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.unpromiser = unpromiser; + exports.isPromise = isPromise; + exports.sleep = sleep; + exports.retry = retry; + exports.getFuzzedDelay = getFuzzedDelay; + exports.getBackedoffDelay = getBackedoffDelay; + exports.createPath = createPath; + exports.encodeQuery = encodeQuery; + exports.decodeQuery = decodeQuery; + exports.warn = warn; + var FuzzFactor = 0.3; + + function unpromiser(fn) { + return function () { + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + var value = fn.apply(this, args); + if (!isPromise(value)) { + return value; + } + var l = args.length; + if (l === 0 || typeof args[l - 1] !== 'function') { + return; + } + var cb = args[l - 1]; + value.then(function (res) { + return cb(null, res); + }, function (err) { + return cb(err, null); + }); + return; + }; + } + + function isPromise(value) { + return !!value && typeof value.then === 'function'; + } + + function sleep(time, args) { + return new Promise(function (resolve) { + setTimeout(resolve, time, args); + }); + } + + function retry(fn, count) { + var delay = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 300; + + return function doTry() { + for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + + return fn.apply(undefined, args).catch(function (err) { + if (--count < 0) { + throw err; + } + return sleep(getBackedoffDelay(delay, count)).then(function () { + return doTry.apply(undefined, args); + }); + }); + }; + } + + function getFuzzedDelay(retryDelay) { + var fuzzingFactor = (Math.random() * 2 - 1) * FuzzFactor; + return retryDelay * (1.0 + fuzzingFactor); + } + + function getBackedoffDelay(retryDelay) { + var retryCount = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; + + return getFuzzedDelay(retryDelay * Math.pow(2, retryCount - 1)); + } + + function createPath(cozy, doctype) { + var id = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; + var query = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; + + var route = '/data/'; + if (!cozy.isV2) { + route += encodeURIComponent(doctype) + '/'; + } + if (id !== '') { + route += encodeURIComponent(id); + } + var q = encodeQuery(query); + if (q !== '') { + route += '?' + q; + } + return route; + } + + function encodeQuery(query) { + if (!query) { + return ''; + } + var q = ''; + for (var qname in query) { + if (q !== '') { + q += '&'; + } + q += encodeURIComponent(qname) + '=' + encodeURIComponent(query[qname]); + } + return q; + } + + function decodeQuery(url) { + var queryIndex = url.indexOf('?'); + if (queryIndex < 0) { + queryIndex = url.length; + } + var queries = {}; + var fragIndex = url.indexOf('#'); + if (fragIndex < 0) { + fragIndex = url.length; + } + if (fragIndex < queryIndex) { + return queries; + } + var queryStr = url.slice(queryIndex + 1, fragIndex); + if (queryStr === '') { + return queries; + } + var parts = queryStr.split('&'); + for (var i = 0; i < parts.length; i++) { + var pair = parts[i].split('='); + if (pair.length === 0 || pair[0] === '') { + continue; + } + var qname = decodeURIComponent(pair[0]); + if (queries.hasOwnProperty(qname)) { + continue; + } + if (pair.length === 1) { + queries[qname] = true; + } else if (pair.length === 2) { + queries[qname] = decodeURIComponent(pair[1]); + } else { + throw new Error('Malformed URL'); + } + } + return queries; + } + + var warned = []; + function warn(text) { + if (warned.indexOf(text) === -1) { + warned.push(text); + console.warn('cozy-client-js', text); + } + } + +/***/ }, +/* 5 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.FetchError = undefined; + + var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* global fetch */ + + + exports.cozyFetch = cozyFetch; + exports.cozyFetchJSON = cozyFetchJSON; + + var _auth_v = __webpack_require__(6); + + var _utils = __webpack_require__(4); + + var _jsonapi = __webpack_require__(7); + + var _jsonapi2 = _interopRequireDefault(_jsonapi); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + function cozyFetch(cozy, path) { + var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + var fullpath = cozy.fullpath(path); + var resp = void 0; + if (options.disableAuth) { + resp = fetch(fullpath, options); + } else if (options.manualAuthCredentials) { + resp = cozyFetchWithAuth(cozy, fullpath, options, options.manualAuthCredentials); + } else { + resp = cozy.authorize().then(function (credentials) { + return cozyFetchWithAuth(cozy, fullpath, options, credentials); + }); + } + return resp.then(handleResponse); + } + + function cozyFetchWithAuth(cozy, fullpath, options, credentials) { + var client = credentials.client, + token = credentials.token; + + options.headers = options.headers || {}; + options.headers['Authorization'] = token.toAuthHeader(); + + return fetch(fullpath, options).then(function (res) { + if (res.status !== 401 || cozy.isV2) { + return res; + } + + return (0, _utils.retry)(function () { + return (0, _auth_v.refreshToken)(cozy, client, token); + }, 3)().then(function (newToken) { + return cozy.saveCredentials(client, newToken); + }).then(function (credentials) { + return cozyFetchWithAuth(cozy, fullpath, options, credentials); + }); + }); + } + + function cozyFetchJSON(cozy, method, path, body) { + var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; + + options.method = method; + + var headers = options.headers = options.headers || {}; + + headers['Accept'] = 'application/json'; + + if (body !== undefined) { + if (headers['Content-Type']) { + options.body = body; + } else { + headers['Content-Type'] = 'application/json'; + options.body = JSON.stringify(body); + } + } + + return cozyFetch(cozy, path, options).then(handleJSONResponse); + } + + function handleResponse(res) { + if (res.ok) { + return res; + } + var data = void 0; + var contentType = res.headers.get('content-type'); + if (contentType && contentType.indexOf('json') >= 0) { + data = res.json(); + } else { + data = res.text(); + } + return data.then(function (err) { + throw new FetchError(res, err); + }); + } + + function handleJSONResponse(res) { + var contentType = res.headers.get('content-type'); + if (!contentType || contentType.indexOf('json') < 0) { + return res.text(function (data) { + throw new FetchError(res, new Error('Response is not JSON: ' + data)); + }); + } + + var json = res.json(); + if (contentType.indexOf('application/vnd.api+json') === 0) { + return json.then(_jsonapi2.default); + } else { + return json; + } + } + + var FetchError = exports.FetchError = function () { + function FetchError(res, reason) { + _classCallCheck(this, FetchError); + + this.response = res; + this.url = res.url; + this.status = res.status; + this.reason = reason; + } + + _createClass(FetchError, [{ + key: 'isUnauthorised', + value: function isUnauthorised() { + return this.status === 401; + } + }]); + + return FetchError; + }(); + +/***/ }, +/* 6 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.AccessToken = exports.Client = exports.StateKey = exports.CredsKey = undefined; + + var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + + var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* global btoa */ + + + exports.registerClient = registerClient; + exports.getClient = getClient; + exports.getAuthCodeURL = getAuthCodeURL; + exports.getAccessToken = getAccessToken; + exports.refreshToken = refreshToken; + exports.oauthFlow = oauthFlow; + + var _utils = __webpack_require__(4); + + var _fetch = __webpack_require__(5); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var StateSize = 16; + + var CredsKey = exports.CredsKey = 'creds'; + var StateKey = exports.StateKey = 'state'; + + var Client = exports.Client = function () { + function Client(opts) { + _classCallCheck(this, Client); + + this.clientID = opts.clientID || opts.client_id || ''; + this.clientSecret = opts.clientSecret || opts.client_secret || ''; + this.registrationAccessToken = opts.registrationAccessToken || opts.registration_access_token || ''; + + if (opts.redirect_uris) { + this.redirectURI = opts.redirect_uris[0] || ''; + } else { + this.redirectURI = opts.redirectURI || ''; + } + + this.softwareID = opts.softwareID || opts.software_id || ''; + this.softwareVersion = opts.softwareVersion || opts.software_version || ''; + this.clientName = opts.clientName || opts.client_name || ''; + this.clientKind = opts.clientKind || opts.client_kind || ''; + this.clientURI = opts.clientURI || opts.client_uri || ''; + + this.logoURI = opts.logoURI || opts.logo_uri || ''; + this.policyURI = opts.policyURI || opts.policy_uri || ''; + + if (this.redirectURI === '') { + throw new Error('Missing redirectURI field'); + } + if (this.softwareID === '') { + throw new Error('Missing softwareID field'); + } + if (this.clientName === '') { + throw new Error('Missing clientName field'); + } + } + + _createClass(Client, [{ + key: 'isRegistered', + value: function isRegistered() { + return this.clientID !== ''; + } + }, { + key: 'toRegisterJSON', + value: function toRegisterJSON() { + return { + redirect_uris: [this.redirectURI], + software_id: this.softwareID, + software_version: this.softwareVersion, + client_name: this.clientName, + client_kind: this.clientKind, + client_uri: this.clientURI, + logo_uri: this.logoURI, + policy_uri: this.policyURI + }; + } + }, { + key: 'toAuthHeader', + value: function toAuthHeader() { + return 'Bearer ' + this.registrationAccessToken; + } + }]); + + return Client; + }(); + + var AccessToken = exports.AccessToken = function () { + function AccessToken(opts) { + _classCallCheck(this, AccessToken); + + this.tokenType = opts.tokenType || opts.token_type; + this.accessToken = opts.accessToken || opts.access_token; + this.refreshToken = opts.refreshToken || opts.refresh_token; + this.scope = opts.scope; + } + + _createClass(AccessToken, [{ + key: 'toAuthHeader', + value: function toAuthHeader() { + return 'Bearer ' + this.accessToken; + } + }]); + + return AccessToken; + }(); + + function registerClient(cozy, client) { + if (!(client instanceof Client)) { + client = new Client(client); + } + if (client.isRegistered()) { + return Promise.reject(new Error('Client already registered')); + } + return (0, _fetch.cozyFetchJSON)(cozy, 'POST', '/auth/register', client.toRegisterJSON(), { + disableAuth: true + }).then(function (data) { + return new Client(data); + }); + } + + // getClient will retrive the registered client informations from the server. + function getClient(cozy, client) { + if (!(client instanceof Client)) { + client = new Client(client); + } + if (!client.isRegistered()) { + return Promise.reject(new Error('Client not registered')); + } + return (0, _fetch.cozyFetchJSON)(cozy, 'GET', '/auth/register/' + client.clientID, null, { + manualAuthCredentials: { + client: client, + token: client + } + }).then(function (data) { + return new Client(data); + }); + } + + // getAuthCodeURL returns a pair {authURL,state} given a registered client. The + // state should be stored in order to be checked against on the user validation + // phase. + function getAuthCodeURL(cozy, client) { + var scopes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : []; + + if (!(client instanceof Client)) { + client = new Client(client); + } + if (!client.isRegistered()) { + throw new Error('Client not registered'); + } + var state = generateRandomState(); + var query = { + 'client_id': client.clientID, + 'redirect_uri': client.redirectURI, + 'state': state, + 'response_type': 'code', + 'scope': scopes.join(' ') + }; + return { + url: cozy.url + '/auth/authorize?' + (0, _utils.encodeQuery)(query), + state: state + }; + } + + // getAccessToken perform a request on the access_token entrypoint with the + // authorization_code grant type in order to generate a new access token for a + // newly registered client. + // + // This method extracts the access code and state from the given URL. By + // default it uses window.location.href. Also, it checks the given state with + // the one specified in the URL query parameter to prevent CSRF attacks. + function getAccessToken(cozy, client, state) { + var pageURL = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : ''; + + if (!state) { + return Promise.reject(new Error('Missing state value')); + } + var grantQueries = getGrantCodeFromPageURL(pageURL); + if (grantQueries === null) { + return Promise.reject(new Error('Missing states from current URL')); + } + if (state !== grantQueries.state) { + return Promise.reject(new Error('Given state does not match url query state')); + } + return retrieveToken(cozy, client, null, { + 'grant_type': 'authorization_code', + 'code': grantQueries.code + }); + } + + // refreshToken perform a request on the access_token entrypoint with the + // refresh_token grant type in order to refresh the given token. + function refreshToken(cozy, client, token) { + return retrieveToken(cozy, client, token, { + 'grant_type': 'refresh_token', + 'code': token.refreshToken + }); + } + + // oauthFlow perform the stateful registration and access granting of an OAuth + // client. + function oauthFlow(cozy, storage, pageURL, createClient, onRegistered) { + function clearAndRetry() { + return storage.clear().then(function () { + return oauthFlow(cozy, storage, pageURL, createClient, onRegistered); + }); + } + + function registerNewClient() { + var _createClient = createClient(), + unregisteredClient = _createClient.client, + scopes = _createClient.scopes; + + return storage.clear().then(function () { + return registerClient(cozy, unregisteredClient); + }).then(function (client) { + var _getAuthCodeURL = getAuthCodeURL(cozy, client, scopes), + url = _getAuthCodeURL.url, + state = _getAuthCodeURL.state; + + return storage.save(StateKey, { client: client, url: url, state: state }).then(function () { + onRegistered(client, url); + // return a promise that never resolves + return new Promise(function () {}); + }); + }); + } + + function saveCreds(creds) { + storage.delete(StateKey); + return storage.save(CredsKey, creds).then(function () { + return creds; + }); + } + + function doFlow(credentials, state) { + if (!credentials) { + var _ret = function () { + // Try to get granted of a token if the state is already filled. + if (!getGrantCodeFromPageURL(pageURL)) { + onRegistered(client, url); + // return a promise that never resolves + return { + v: new Promise(function () {}) + }; + } + + var client = state.client, + value = state.state, + url = state.url; + + return { + v: getAccessToken(cozy, client, value, pageURL).then(function (token) { + return { client: client, token: token }; + }) + }; + }(); + + if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v; + } + + // If credentials are cached we re-fetch the registered client with the + // said token. Fetching the client, if the token is outdated we should try + // the token is refreshed. + var client = credentials.client, + token = credentials.token; + + return getClient(cozy, client).then(function (client) { + return { client: client, token: token }; + }); + } + + return Promise.all([storage.load(CredsKey), storage.load(StateKey)]).then(function (_ref) { + var _ref2 = _slicedToArray(_ref, 2), + credentials = _ref2[0], + state = _ref2[1]; + + // The cache is empty, we initiate the client registration. + if (!credentials && !state) { + return registerNewClient(); + } + + var authFlow = doFlow(credentials, state); + + // If the auth flow is rejected with a 401 error, we clear the cache and + // restart the entire registration flow. + return authFlow.then(saveCreds, function (err) { + if (err instanceof _fetch.FetchError && err.isUnauthorised()) { + return clearAndRetry(); + } else { + throw err; + } + }); + }); + } + + // retrieveToken perform a request on the access_token entrypoint in order to + // fetch a token. + function retrieveToken(cozy, client, token, query) { + if (!(client instanceof Client)) { + client = new Client(client); + } + if (!client.isRegistered()) { + return Promise.reject(new Error('Client not registered')); + } + var body = (0, _utils.encodeQuery)(Object.assign({}, query, { + 'client_id': client.clientID, + 'client_secret': client.clientSecret + })); + return (0, _fetch.cozyFetchJSON)(cozy, 'POST', '/auth/access_token', body, { + disableAuth: token === null, + manualAuthCredentials: { client: client, token: token }, + headers: { 'Content-Type': 'application/x-www-form-urlencoded' } + }).then(function (data) { + return new AccessToken(data); + }); + } + + // getGrantCodeFromPageURL extract the state and access_code query parameters + // from the given url + function getGrantCodeFromPageURL() { + var pageURL = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; + + if (pageURL === '' && typeof window !== 'undefined') { + pageURL = window.location.href; + } + var queries = (0, _utils.decodeQuery)(pageURL); + if (!queries.hasOwnProperty('state')) { + return null; + } + return { + state: queries['state'], + code: queries['access_code'] + }; + } + + // generateRandomState will try to generate a 128bits random value from a secure + // pseudo random generator. It will fallback on Math.random if it cannot find + // such generator. + function generateRandomState() { + var buffer = void 0; + if (typeof window !== 'undefined' && typeof window.crypto !== 'undefined' && typeof window.crypto.getRandomValues === 'function') { + buffer = new Uint8Array(StateSize); + window.crypto.getRandomValues(buffer); + } else { + try { + buffer = __webpack_require__(!(function webpackMissingModule() { var e = new Error("Cannot find module \"crypto\""); e.code = 'MODULE_NOT_FOUND'; throw e; }())).randomBytes(StateSize); + } catch (e) {} + } + if (!buffer) { + buffer = new Array(StateSize); + for (var i = 0; i < buffer.length; i++) { + buffer[i] = Math.floor(Math.random() * 255); + } + } + return btoa(String.fromCharCode.apply(null, buffer)).replace(/=+$/, '').replace(/\//g, '_').replace(/\+/g, '-'); + } + +/***/ }, +/* 7 */ +/***/ function(module, exports) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + function indexKey(doc) { + return doc.type + '/' + doc.id; + } + + function findByRef(resources, ref) { + return resources[indexKey(ref)]; + } + + function handleResource(rawResource, resources) { + var resource = { + _id: rawResource.id, + _type: rawResource.type, + _rev: rawResource.meta.rev, + attributes: rawResource.attributes, + relations: function relations(name) { + var rels = rawResource.relationships[name]; + if (rels === undefined || rels.data === undefined) return undefined; + if (rels.data === null) return null; + if (!Array.isArray(rels.data)) return findByRef(resources, rels.data); + return rels.data.map(function (ref) { + return findByRef(resources, ref); + }); + } + }; + + resources[indexKey(rawResource)] = resource; + + return resource; + } + + function handleTopLevel(doc) { + var resources = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + // build an index of included resource by Type & ID + var included = doc.included; + + if (Array.isArray(included)) { + included.forEach(function (r) { + return handleResource(r, resources); + }); + } + + if (Array.isArray(doc.data)) { + return doc.data.map(function (r) { + return handleResource(r, resources); + }); + } else { + return handleResource(doc.data, resources); + } + } + + exports.default = handleTopLevel; + +/***/ }, +/* 8 */ +/***/ function(module, exports) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var LocalStorage = exports.LocalStorage = function () { + function LocalStorage(storage, prefix) { + _classCallCheck(this, LocalStorage); + + if (!storage && typeof window !== 'undefined') { + storage = window.localStorage; + } + this.storage = storage; + this.prefix = prefix || 'cozy:oauth:'; + } + + _createClass(LocalStorage, [{ + key: 'save', + value: function save(key, value) { + var _this = this; + + return new Promise(function (resolve) { + return resolve(_this.storage.setItem(_this.prefix + key, JSON.stringify(value))); + }); + } + }, { + key: 'load', + value: function load(key) { + var _this2 = this; + + return new Promise(function (resolve) { + var item = _this2.storage.getItem(_this2.prefix + key); + if (!item) { + resolve(); + } else { + resolve(JSON.parse(item)); + } + }); + } + }, { + key: 'delete', + value: function _delete(key) { + var _this3 = this; + + return new Promise(function (resolve) { + return resolve(_this3.storage.removeItem(_this3.prefix + key)); + }); + } + }, { + key: 'clear', + value: function clear() { + var _this4 = this; + + return new Promise(function (resolve) { + var storage = _this4.storage; + for (var i = 0; i < storage.length; i++) { + var key = storage.key(i); + if (key.indexOf(_this4.prefix) === 0) { + storage.removeItem(key); + } + } + resolve(); + }); + } + }]); + + return LocalStorage; + }(); + + var MemoryStorage = exports.MemoryStorage = function () { + function MemoryStorage() { + _classCallCheck(this, MemoryStorage); + + this.hash = Object.create(null); + } + + _createClass(MemoryStorage, [{ + key: 'save', + value: function save(key, value) { + this.hash[key] = value; + return Promise.resolve(value); + } + }, { + key: 'load', + value: function load(key) { + return Promise.resolve(this.hash[key]); + } + }, { + key: 'delete', + value: function _delete(key) { + var deleted = delete this.hash[key]; + return Promise.resolve(deleted); + } + }, { + key: 'clear', + value: function clear() { + this.hash = Object.create(null); + return Promise.resolve(); + } + }]); + + return MemoryStorage; + }(); + +/***/ }, +/* 9 */ +/***/ function(module, exports) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + + exports.getAccessToken = getAccessToken; + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + /* global btoa */ + var V2TOKEN_ABORT_TIMEOUT = 3000; + + function getAccessToken() { + return new Promise(function (resolve, reject) { + if (typeof window === 'undefined') { + return reject(new Error('getV2Token should be used in browser')); + } else if (!window.parent) { + return reject(new Error('getV2Token should be used in iframe')); + } else if (!window.parent.postMessage) { + return reject(new Error('getV2Token should be used in modern browser')); + } + var origin = window.location.origin; + var intent = { action: 'getToken' }; + var timeout = null; + var receiver = function receiver(event) { + var token = void 0; + try { + token = new AccessToken({ + appName: event.data.appName, + token: event.data.token + }); + } catch (e) { + reject(e); + return; + } + window.removeEventListener('message', receiver); + clearTimeout(timeout); + resolve({ client: null, token: token }); + }; + + window.addEventListener('message', receiver, false); + window.parent.postMessage(intent, origin); + + timeout = setTimeout(function () { + reject(new Error('No response from parent iframe after 3s')); + }, V2TOKEN_ABORT_TIMEOUT); + + }); + } + + var AccessToken = exports.AccessToken = function () { + function AccessToken(opts) { + _classCallCheck(this, AccessToken); + + this.appName = opts.appName || ''; + this.token = opts.token || ''; + // if (this.appName === '') { + // throw new Error('Missing appName parameter'); + // } + // if (this.token === '') { + // throw new Error('Missing token parameter'); + // } + } + + _createClass(AccessToken, [{ + key: 'toAuthHeader', + value: function toAuthHeader() { + return 'Basic ' + btoa(this.appName + ':' + this.token); + } + }]); + + return AccessToken; + }(); + +/***/ }, +/* 10 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.create = create; + exports.find = find; + exports.update = update; + exports.updateAttributes = updateAttributes; + exports._delete = _delete; + + var _utils = __webpack_require__(4); + + var _doctypes = __webpack_require__(11); + + var _fetch = __webpack_require__(5); + + var NOREV = 'stack-v2-no-rev'; + + function create(cozy, doctype, attributes) { + doctype = (0, _doctypes.normalizeDoctype)(cozy, doctype); + + if (cozy.isV2) { + attributes.docType = doctype; + } + + var path = (0, _utils.createPath)(cozy, doctype); + return (0, _fetch.cozyFetchJSON)(cozy, 'POST', path, attributes).then(function (resp) { + if (cozy.isV2) { + return find(cozy, doctype, resp._id); + } else { + return resp.data; + } + }); + } + + function find(cozy, doctype, id) { + doctype = (0, _doctypes.normalizeDoctype)(cozy, doctype); + + if (!id) { + return Promise.reject(new Error('Missing id parameter')); + } + + var path = (0, _utils.createPath)(cozy, doctype, id); + return (0, _fetch.cozyFetchJSON)(cozy, 'GET', path).then(function (resp) { + if (cozy.isV2) { + return Object.assign(resp, { _rev: NOREV }); + } else { + return resp; + } + }); + } + + function update(cozy, doctype, doc, changes) { + doctype = (0, _doctypes.normalizeDoctype)(cozy, doctype); + + var _id = doc._id, + _rev = doc._rev; + + + if (!_id) { + return Promise.reject(new Error('Missing _id field in passed document')); + } + + if (!cozy.isV2 && !_rev) { + return Promise.reject(new Error('Missing _rev field in passed document')); + } + + if (cozy.isV2) { + changes = Object.assign({ _id: _id }, changes); + } else { + changes = Object.assign({ _id: _id, _rev: _rev }, changes); + } + + var path = (0, _utils.createPath)(cozy, doctype, _id); + return (0, _fetch.cozyFetchJSON)(cozy, 'PUT', path, changes).then(function (resp) { + if (cozy.isV2) { + return find(cozy, doctype, _id); + } else { + return resp.data; + } + }); + } + + function updateAttributes(cozy, doctype, _id, changes) { + var tries = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 3; + + doctype = (0, _doctypes.normalizeDoctype)(cozy, doctype); + return find(cozy, doctype, _id).then(function (doc) { + return update(cozy, doctype, doc, Object.assign({ _id: _id }, doc, changes)); + }).catch(function (err) { + if (tries > 0) { + return updateAttributes(cozy, doctype, _id, changes, tries - 1); + } else { + throw err; + } + }); + } + + function _delete(cozy, doctype, doc) { + doctype = (0, _doctypes.normalizeDoctype)(cozy, doctype); + + var _id = doc._id, + _rev = doc._rev; + + + if (!_id) { + return Promise.reject(new Error('Missing _id field in passed document')); + } + + if (!cozy.isV2 && !_rev) { + return Promise.reject(new Error('Missing _rev field in passed document')); + } + + var query = cozy.isV2 ? null : { rev: _rev }; + var path = (0, _utils.createPath)(cozy, doctype, _id, query); + return (0, _fetch.cozyFetchJSON)(cozy, 'DELETE', path).then(function (resp) { + if (cozy.isV2) { + return { id: _id, rev: NOREV }; + } else { + return resp; + } + }); + } + +/***/ }, +/* 11 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.normalizeDoctype = normalizeDoctype; + + var _utils = __webpack_require__(4); + + var KNOWN_DOCTYPES = { + 'files': 'io.cozy.files', + 'folder': 'io.cozy.files', + 'contact': 'io.cozy.contacts', + 'event': 'io.cozy.events', + 'track': 'io.cozy.labs.music.track', + 'playlist': 'io.cozy.labs.music.playlist' + }; + + var REVERSE_KNOWN = {}; + Object.keys(KNOWN_DOCTYPES).forEach(function (k) { + REVERSE_KNOWN[KNOWN_DOCTYPES[k]] = k; + }); + + function normalizeDoctype(cozy, doctype) { + var isQualified = doctype.indexOf('.') !== -1; + if (cozy.isV2 && isQualified) { + var known = REVERSE_KNOWN[doctype]; + if (known) return known; + return doctype.replace(/\./g, '-'); + } + if (!cozy.isV2 && !isQualified) { + var _known = KNOWN_DOCTYPES[doctype]; + if (_known) { + (0, _utils.warn)('you are using a non-qualified doctype ' + doctype + ' assumed to be ' + _known); + return _known; + } + throw new Error('Doctype ' + doctype + ' should be qualified.'); + } + return doctype; + } + +/***/ }, +/* 12 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + + exports.defineIndex = defineIndex; + exports.query = query; + exports.parseSelector = parseSelector; + exports.normalizeSelector = normalizeSelector; + exports.makeMapReduceQuery = makeMapReduceQuery; + + var _utils = __webpack_require__(4); + + var _doctypes = __webpack_require__(11); + + var _fetch = __webpack_require__(5); + + function defineIndex(cozy, doctype, fields) { + doctype = (0, _doctypes.normalizeDoctype)(cozy, doctype); + + if (!Array.isArray(fields) || fields.length === 0) { + throw new Error('defineIndex fields should be a non-empty array'); + } + + if (cozy.isV2) { + return defineIndexV2(cozy, doctype, fields); + } else { + return defineIndexV3(cozy, doctype, fields); + } + } + + function query(cozy, indexRef, options) { + if (!indexRef) { + throw new Error('query should be passed the indexRef'); + } + if (cozy.isV2) { + return queryV2(cozy, indexRef, options); + } else { + return queryV3(cozy, indexRef, options); + } + } + + // Internals + + var VALUEOPERATORS = ['$eq', '$gt', '$gte', '$lt', '$lte']; + var LOGICOPERATORS = ['$or', '$and', '$not']; + + /* eslint-disable */ + var MAP_TEMPLATE = function (doc) { + if (doc.docType.toLowerCase() === 'DOCTYPEPLACEHOLDER') { + emit(FIELDSPLACEHOLDER, doc); + } + }.toString().replace(/ /g, '').replace(/\n/g, ''); + var COUCHDB_INFINITY = { '\uFFFF': '\uFFFF' }; + var COUCHDB_LOWEST = null; + /* eslint-enable */ + + // defineIndexV2 is equivalent to defineIndex but only works for V2. + // It transforms the index fields into a map reduce view. + function defineIndexV2(cozy, doctype, fields) { + var indexName = 'by' + fields.map(capitalize).join(''); + var indexDefinition = { map: makeMapFunction(doctype, fields), reduce: '_count' }; + var path = '/request/' + doctype + '/' + indexName + '/'; + return (0, _fetch.cozyFetchJSON)(cozy, 'PUT', path, indexDefinition).then(function () { + return { doctype: doctype, type: 'mapreduce', name: indexName, fields: fields }; + }); + } + + // defineIndexV2 is equivalent to defineIndex but only works for V2. + // It transforms the index fields into a map reduce view. + function defineIndexV3(cozy, doctype, fields) { + var path = (0, _utils.createPath)(cozy, doctype, '_index'); + var indexDefinition = { 'index': { fields: fields } }; + return (0, _fetch.cozyFetchJSON)(cozy, 'POST', path, indexDefinition).then(function (response) { + return { doctype: doctype, type: 'mango', name: response.id, fields: fields }; + }); + } + + // queryV2 is equivalent to query but only works for V2. + // It transforms the query into a _views call using makeMapReduceQuery + function queryV2(cozy, indexRef, options) { + if (indexRef.type !== 'mapreduce') { + throw new Error('query indexRef should be the return value of defineIndexV2'); + } + if (options.fields) { + (0, _utils.warn)('query fields will be ignored on v2'); + } + + var path = '/request/' + indexRef.doctype + '/' + indexRef.name + '/'; + var opts = makeMapReduceQuery(indexRef, options); + return (0, _fetch.cozyFetchJSON)(cozy, 'POST', path, opts).then(function (response) { + return response.map(function (r) { + return r.value; + }); + }); + } + + // queryV3 is equivalent to query but only works for V3 + function queryV3(cozy, indexRef, options) { + console.log("queryV3", indexRef.type) + if (indexRef.type !== 'mango') { + throw new Error('indexRef should be the return value of defineIndexV3'); + } + + var opts = { + use_index: indexRef.name, + fields: options.fields, + selector: options.selector, + limit: options.limit, + since: options.since, + sort: indexRef.fields // sort is useless with mango + }; + + var path = (0, _utils.createPath)(cozy, indexRef.doctype, '_find'); + return (0, _fetch.cozyFetchJSON)(cozy, 'POST', path, opts).then(function (response) { + return response.docs; + }); + } + + // misc + function capitalize(name) { + return name.charAt(0).toUpperCase() + name.slice(1); + } + + function makeMapFunction(doctype, fields) { + fields = '[' + fields.map(function (name) { + return 'doc.' + name; + }).join(',') + ']'; + + return MAP_TEMPLATE.replace('DOCTYPEPLACEHOLDER', doctype.toLowerCase()).replace('FIELDSPLACEHOLDER', fields); + } + + // parseSelector takes a mango selector and returns it as an array of filter + // a filter is [path, operator, value] array + // a path is an array of field names + // This function is only exported so it can be unit tested. + // Example : + // parseSelector({"test":{"deep": {"$gt": 3}}}) + // [[['test', 'deep'], '$gt', 3 ]] + function parseSelector(selector) { + var path = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; + var operator = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '$eq'; + + if ((typeof selector === 'undefined' ? 'undefined' : _typeof(selector)) !== 'object') { + return [[path, operator, selector]]; + } + + var keys = Object.keys(selector); + if (keys.length === 0) { + throw new Error('empty selector'); + } else { + return keys.reduce(function (acc, k) { + if (LOGICOPERATORS.indexOf(k) !== -1) { + throw new Error('cozy-client-js does not support mango logic ops'); + } else if (VALUEOPERATORS.indexOf(k) !== -1) { + return acc.concat(parseSelector(selector[k], path, k)); + } else { + return acc.concat(parseSelector(selector[k], path.concat(k), '$eq')); + } + }, []); + } + } + + // normalizeSelector takes a mango selector and returns it as an object + // normalized. + // This function is only exported so it can be unit tested. + // Example : + // parseSelector({"test":{"deep": {"$gt": 3}}}) + // {"test.deep": {"$gt": 3}} + function normalizeSelector(selector) { + var filters = parseSelector(selector); + return filters.reduce(function (acc, filter) { + var _filter = _slicedToArray(filter, 3), + path = _filter[0], + op = _filter[1], + value = _filter[2]; + + var field = path.join('.'); + acc[field] = acc[field] || {}; + acc[field][op] = value; + return acc; + }, {}); + } + + // applySelector takes the normalized selector for the current field + // and append the proper values to opts.startkey, opts.endkey + function applySelector(selector, opts) { + var value = selector['$eq']; + var lower = COUCHDB_LOWEST; + var upper = COUCHDB_INFINITY; + var inclusiveEnd = void 0; + + if (value) { + opts.startkey.push(value); + opts.endkey.push(value); + return false; + } + + value = selector['$gt']; + if (value) { + throw new Error('operator $gt (strict greater than) not supported'); + } + + value = selector['$gte']; + if (value) { + lower = value; + } + + value = selector['$lte']; + if (value) { + upper = value; + inclusiveEnd = true; + } + + value = selector['$lt']; + if (value) { + upper = value; + inclusiveEnd = false; + } + + opts.startkey.push(lower); + opts.endkey.push(upper); + if (inclusiveEnd !== undefined) opts.inclusive_end = inclusiveEnd; + return true; + } + + // makeMapReduceQuery takes a mango query and generate _views call parameters + // to obtain same results depending on fields in the passed indexRef. + function makeMapReduceQuery(indexRef, query) { + var mrquery = { + startkey: [], + endkey: [], + reduce: false + }; + + var firstFreeValueField = null; + var normalizedSelector = normalizeSelector(query.selector); + + indexRef.fields.forEach(function (field) { + var selector = normalizedSelector[field]; + + if (selector && firstFreeValueField != null) { + throw new Error('Selector on field ' + field + ', but not on ' + firstFreeValueField + ' which is higher in index fields.'); + } else if (selector) { + selector.used = true; + var isFreeValue = applySelector(selector, mrquery); + if (isFreeValue) firstFreeValueField = field; + } else if (firstFreeValueField == null) { + firstFreeValueField = field; + mrquery.endkey.push(COUCHDB_INFINITY); + } + }); + + // FIXME: fait planter la query + // Object.keys(normalizedSelector).forEach(function (field) { + // if (!normalizedSelector[field].used) { + // throw new Error('Cant apply selector on ' + field + ', it is not in index'); + // } + // }); + + return mrquery; + } + +/***/ }, +/* 13 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /* global Blob, File */ + + + exports.create = create; + exports.createDirectory = createDirectory; + exports.updateById = updateById; + exports.updateAttributesById = updateAttributesById; + exports.updateAttributesByPath = updateAttributesByPath; + exports.trashById = trashById; + exports.statById = statById; + exports.statByPath = statByPath; + exports.downloadById = downloadById; + exports.downloadByPath = downloadByPath; + + var _fetch = __webpack_require__(5); + + var _jsonapi = __webpack_require__(7); + + var _jsonapi2 = _interopRequireDefault(_jsonapi); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + var contentTypeOctetStream = 'application/octet-stream'; + + function doUpload(cozy, data, contentType, method, path) { + if (!data) { + throw new Error('missing data argument'); + } + + // transform any ArrayBufferView to ArrayBuffer + if (data.buffer && data.buffer instanceof ArrayBuffer) { + data = data.buffer; + } + + var isBuffer = typeof ArrayBuffer !== 'undefined' && data instanceof ArrayBuffer; + var isFile = typeof File !== 'undefined' && data instanceof File; + var isBlob = typeof Blob !== 'undefined' && data instanceof Blob; + var isString = typeof data === 'string'; + + if (!isBuffer && !isFile && !isBlob && !isString) { + throw new Error('invalid data type'); + } + + if (!contentType) { + if (isBuffer) { + contentType = contentTypeOctetStream; + } else if (isFile) { + contentType = data.type || contentTypeOctetStream; + } else if (isBlob) { + contentType = contentTypeOctetStream; + } else if (typeof data === 'string') { + contentType = 'text/plain'; + } + } + + return (0, _fetch.cozyFetch)(cozy, path, { + method: method, + headers: { 'Content-Type': contentType }, + body: data + }).then(function (res) { + var json = res.json(); + if (!res.ok) { + return json.then(function (err) { + throw err; + }); + } else { + return json.then(_jsonapi2.default); + } + }); + } + + function create(cozy, data, options) { + var _ref = options || {}, + name = _ref.name, + dirID = _ref.dirID, + contentType = _ref.contentType; + + // handle case where data is a file and contains the name + + + if (!name && typeof data.name === 'string') { + name = data.name; + } + + if (typeof name !== 'string' || name === '') { + throw new Error('missing name argument'); + } + + var path = '/files/' + encodeURIComponent(dirID || ''); + var query = '?Name=' + encodeURIComponent(name) + '&Type=file'; + return doUpload(cozy, data, contentType, 'POST', '' + path + query); + } + + function createDirectory(cozy, options) { + var _ref2 = options || {}, + name = _ref2.name, + dirID = _ref2.dirID; + + if (typeof name !== 'string' || name === '') { + throw new Error('missing name argument'); + } + + var path = '/files/' + encodeURIComponent(dirID || ''); + var query = '?Name=' + encodeURIComponent(name) + '&Type=directory'; + return (0, _fetch.cozyFetchJSON)(cozy, 'POST', '' + path + query); + } + + function updateById(cozy, id, data, options) { + var _ref3 = options || {}, + contentType = _ref3.contentType; + + return doUpload(cozy, data, contentType, 'PUT', '/files/' + encodeURIComponent(id)); + } + + function updateAttributesById(cozy, id, attrs) { + if (!attrs || (typeof attrs === 'undefined' ? 'undefined' : _typeof(attrs)) !== 'object') { + throw new Error('missing attrs argument'); + } + + var body = { data: { attributes: attrs } }; + return (0, _fetch.cozyFetchJSON)(cozy, 'PATCH', '/files/' + encodeURIComponent(id), body); + } + + function updateAttributesByPath(cozy, path, attrs) { + if (!attrs || (typeof attrs === 'undefined' ? 'undefined' : _typeof(attrs)) !== 'object') { + throw new Error('missing attrs argument'); + } + + var body = { data: { attributes: attrs } }; + return (0, _fetch.cozyFetchJSON)(cozy, 'PATCH', '/files/metadata?Path=' + encodeURIComponent(path), body); + } + + function trashById(cozy, id) { + if (typeof id !== 'string' || id === '') { + throw new Error('missing id argument'); + } + return (0, _fetch.cozyFetchJSON)(cozy, 'DELETE', '/files/' + encodeURIComponent(id)); + } + + function statById(cozy, id) { + return (0, _fetch.cozyFetchJSON)(cozy, 'GET', '/files/' + encodeURIComponent(id)).then(addIsDir); + } + + function statByPath(cozy, path) { + return (0, _fetch.cozyFetchJSON)(cozy, 'GET', '/files/metadata?Path=' + encodeURIComponent(path)).then(addIsDir); + } + + function downloadById(cozy, id) { + return (0, _fetch.cozyFetch)(cozy, '/files/download/' + encodeURIComponent(id)); + } + + function downloadByPath(cozy, path) { + return (0, _fetch.cozyFetch)(cozy, '/files/download?Path=' + encodeURIComponent(path)); + } + + function addIsDir(obj) { + obj.isDir = obj.attributes.type === 'directory'; + return obj; + } + +/***/ } +/******/ ]) +}); +; +//# sourceMappingURL=cozy-client.js.map