"use strict"; /* Cloud provider implementations (Dropbox only as now) provider Interface: name: name of the provider async auth(): authenticate and get access tokens from provider async save(filename): save map file to provider as filename async load(filename): load filename from provider async list(): list available filenames at provider async getLink(filePath): get shareable link for file restore(): restore access tokens from storage if possible */ window.Cloud = (function () { // helpers to use in providers for token handling const lSKey = x => `auth-${x}`; const setToken = (prov, key) => localStorage.setItem(lSKey(prov), key); const getToken = prov => localStorage.getItem(lSKey(prov)); /**********************************************************/ /* Dropbox provider */ /**********************************************************/ const DBP = { name: "dropbox", clientId: "pdr9ae64ip0qno4", authWindow: undefined, token: null, // Access token api: null, restore() { this.token = getToken(this.name); if (this.token) this.connect(this.token); }, async call(name, param) { try { return await this.api[name](param); } catch (e) { if (e.name !== "DropboxResponseError") throw e; // retry with auth await this.auth(); return await this.api[name](param); } }, connect(token) { const clientId = this.clientId; const auth = new Dropbox.DropboxAuth({clientId}); auth.setAccessToken(token); this.api = new Dropbox.Dropbox({auth}); }, async save(fileName, contents) { if (!this.api) await this.auth(); const resp = this.call("filesUpload", {path: "/" + fileName, contents}); DEBUG && console.log("Dropbox response:", resp); return true; }, async load(path) { if (!this.api) await this.auth(); const resp = await this.call("filesDownload", {path}); const blob = resp.result.fileBlob; if (!blob) throw new Error("Invalid response from dropbox."); return blob; }, async list() { if (!this.api) return null; const resp = await this.call("filesListFolder", {path: ""}); return resp.result.entries.map(e => ({name: e.name, path: e.path_lower})); }, auth() { const url = window.location.origin + window.location.pathname + "dropbox.html"; this.authWindow = window.open(url, "auth", "width=640,height=480"); // child window expected to call // window.opener.Cloud.providers.dropbox.setDropBoxToken (see below) return new Promise((resolve, reject) => { const watchDog = () => { this.authWindow.close(); reject(new Error("Timeout. No auth for dropbox.")); }; setTimeout(watchDog, 120 * 1000); window.addEventListener("dropboxauth", e => { clearTimeout(watchDog); resolve(); }); }); }, // Callback function for auth window. setDropBoxToken(token) { DEBUG && console.log("Access token:", token); setToken(this.name, token); this.connect(token); this.authWindow.close(); window.dispatchEvent(new Event("dropboxauth")); }, async getLink(path) { if (!this.api) await this.auth(); let resp; // already exists? resp = await this.call("sharingListSharedLinks", {path}); if (resp.result.links.length) return resp.result.links[0].url; // create new resp = await this.call("sharingCreateSharedLinkWithSettings", { path, settings: { require_password: false, audience: "public", access: "viewer", requested_visibility: "public", allow_download: true } }); DEBUG && console.log("Dropbox link object:", resp.result); return resp.result.url; } }; // register providers here: const providers = { dropbox: DBP }; // restore all providers at startup for (const p of Object.values(providers)) p.restore(); return {providers}; })();