text: import { __awaiter, __extends, __generator } from "tslib";
  import { WebPlugin } from './index';
  import { FilesystemDirectory } from '../core-plugin-definitions';
  var FilesystemPluginWeb = /** @class */ (function (_super) {
      __extends(FilesystemPluginWeb, _super);
      function FilesystemPluginWeb() {
          var _this = _super.call(this, {
              name: 'Filesystem',
              platforms: ['web']
          }) || this;
          _this.DEFAULT_DIRECTORY = FilesystemDirectory.Data;
          _this.DB_VERSION = 1;
          _this.DB_NAME = 'Disc';
          _this._writeCmds = ['add', 'put', 'delete'];
          return _this;
      }
      FilesystemPluginWeb.prototype.initDb = function () {
          return __awaiter(this, void 0, void 0, function () {
              var _this = this;
              return __generator(this, function (_a) {
                  if (this._db !== undefined) {
                      return [2 /*return*/, this._db];
                  }
                  if (!('indexedDB' in window)) {
                      throw new Error('This browser doesn\'t support IndexedDB');
                  }
                  return [2 /*return*/, new Promise(function (resolve, reject) {
                          var request = indexedDB.open(_this.DB_NAME, _this.DB_VERSION);
                          request.onupgradeneeded = FilesystemPluginWeb.doUpgrade;
                          request.onsuccess = function () {
                              _this._db = request.result;
                              resolve(request.result);
                          };
                          request.onerror = function () { return reject(request.error); };
                          request.onblocked = function () {
                              console.warn('db blocked');
                          };
                      })];
              });
          });
      };
      FilesystemPluginWeb.doUpgrade = function (event) {
          var eventTarget = event.target;
          var db = eventTarget.result;
          switch (event.oldVersion) {
              case 0:
              case 1:
              default:
                  if (db.objectStoreNames.contains('FileStorage')) {
                      db.deleteObjectStore('FileStorage');
                  }
                  var store = db.createObjectStore('FileStorage', { keyPath: 'path' });
                  store.createIndex('by_folder', 'folder');
          }
      };
      FilesystemPluginWeb.prototype.dbRequest = function (cmd, args) {
          return __awaiter(this, void 0, void 0, function () {
              var readFlag;
              return __generator(this, function (_a) {
                  readFlag = this._writeCmds.indexOf(cmd) !== -1 ? 'readwrite' : 'readonly';
                  return [2 /*return*/, this.initDb()
                          .then(function (conn) {
                          return new Promise(function (resolve, reject) {
                              var tx = conn.transaction(['FileStorage'], readFlag);
                              var store = tx.objectStore('FileStorage');
                              var req = store[cmd].apply(store, args);
                              req.onsuccess = function () { return resolve(req.result); };
                              req.onerror = function () { return reject(req.error); };
                          });
                      })];
              });
          });
      };
      FilesystemPluginWeb.prototype.dbIndexRequest = function (indexName, cmd, args) {
          return __awaiter(this, void 0, void 0, function () {
              var readFlag;
              return __generator(this, function (_a) {
                  readFlag = this._writeCmds.indexOf(cmd) !== -1 ? 'readwrite' : 'readonly';
                  return [2 /*return*/, this.initDb()
                          .then(function (conn) {
                          return new Promise(function (resolve, reject) {
                              var tx = conn.transaction(['FileStorage'], readFlag);
                              var store = tx.objectStore('FileStorage');
                              var index = store.index(indexName);
                              var req = index[cmd].apply(index, args);
                              req.onsuccess = function () { return resolve(req.result); };
                              req.onerror = function () { return reject(req.error); };
                          });
                      })];
              });
          });
      };
      FilesystemPluginWeb.prototype.getPath = function (directory, uriPath) {
          directory = directory || this.DEFAULT_DIRECTORY;
          var cleanedUriPath = uriPath !== undefined ? uriPath.replace(/^[/]+|[/]+$/g, '') : '';
          var fsPath = '/' + directory;
          if (uriPath !== '')
              fsPath += '/' + cleanedUriPath;
          return fsPath;
      };
      FilesystemPluginWeb.prototype.clear = function () {
          return __awaiter(this, void 0, void 0, function () {
              var conn, tx, store;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0: return [4 /*yield*/, this.initDb()];
                      case 1:
                          conn = _a.sent();
                          tx = conn.transaction(['FileStorage'], 'readwrite');
                          store = tx.objectStore('FileStorage');
                          store.clear();
                          return [2 /*return*/, {}];
                  }
              });
          });
      };
      /**
       * Read a file from disk
       * @param options options for the file read
       * @return a promise that resolves with the read file data result
       */
      FilesystemPluginWeb.prototype.readFile = function (options) {
          return __awaiter(this, void 0, void 0, function () {
              var path, entry;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          path = this.getPath(options.directory, options.path);
                          return [4 /*yield*/, this.dbRequest('get', [path])];
                      case 1:
                          entry = _a.sent();
                          if (entry === undefined)
                              throw Error('File does not exist.');
                          return [2 /*return*/, { data: entry.content }];
                  }
              });
          });
      };
      /**
       * Write a file to disk in the specified location on device
       * @param options options for the file write
       * @return a promise that resolves with the file write result
       */
      FilesystemPluginWeb.prototype.writeFile = function (options) {
          return __awaiter(this, void 0, void 0, function () {
              var path, data, doRecursive, occupiedEntry, encoding, parentPath, parentEntry, subDirIndex, parentArgPath, now, pathObj;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          path = this.getPath(options.directory, options.path);
                          data = options.data;
                          doRecursive = options.recursive;
                          return [4 /*yield*/, this.dbRequest('get', [path])];
                      case 1:
                          occupiedEntry = _a.sent();
                          if (occupiedEntry && occupiedEntry.type === 'directory')
                              throw ('The supplied path is a directory.');
                          encoding = options.encoding;
                          parentPath = path.substr(0, path.lastIndexOf('/'));
                          return [4 /*yield*/, this.dbRequest('get', [parentPath])];
                      case 2:
                          parentEntry = _a.sent();
                          if (!(parentEntry === undefined)) return [3 /*break*/, 4];
                          subDirIndex = parentPath.indexOf('/', 1);
                          if (!(subDirIndex !== -1)) return [3 /*break*/, 4];
                          parentArgPath = parentPath.substr(subDirIndex);
                          return [4 /*yield*/, this.mkdir({ path: parentArgPath, directory: options.directory, recursive: doRecursive })];
                      case 3:
                          _a.sent();
                          _a.label = 4;
                      case 4:
                          now = Date.now();
                          pathObj = {
                              path: path,
                              folder: parentPath,
                              type: 'file',
                              size: data.length,
                              ctime: now,
                              mtime: now,
                              content: !encoding && data.indexOf(',') >= 0 ? data.split(',')[1] : data,
                          };
                          return [4 /*yield*/, this.dbRequest('put', [pathObj])];
                      case 5:
                          _a.sent();
                          return [2 /*return*/, {
                                  uri: pathObj.path
                              }];
                  }
              });
          });
      };
      /**
       * Append to a file on disk in the specified location on device
       * @param options options for the file append
       * @return a promise that resolves with the file write result
       */
      FilesystemPluginWeb.prototype.appendFile = function (options) {
          return __awaiter(this, void 0, void 0, function () {
              var path, data, parentPath, now, ctime, occupiedEntry, parentEntry, parentArgPathIndex, parentArgPath, pathObj;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          path = this.getPath(options.directory, options.path);
                          data = options.data;
                          parentPath = path.substr(0, path.lastIndexOf('/'));
                          now = Date.now();
                          ctime = now;
                          return [4 /*yield*/, this.dbRequest('get', [path])];
                      case 1:
                          occupiedEntry = _a.sent();
                          if (occupiedEntry && occupiedEntry.type === 'directory')
                              throw ('The supplied path is a directory.');
                          return [4 /*yield*/, this.dbRequest('get', [parentPath])];
                      case 2:
                          parentEntry = _a.sent();
                          if (!(parentEntry === undefined)) return [3 /*break*/, 4];
                          parentArgPathIndex = parentPath.indexOf('/', 1);
                          parentArgPath = parentArgPathIndex !== -1 ? parentPath.substr(parentArgPathIndex) : '/';
                          return [4 /*yield*/, this.mkdir({ path: parentArgPath, directory: options.directory, recursive: true })];
                      case 3:
                          _a.sent();
                          _a.label = 4;
                      case 4:
                          if (occupiedEntry !== undefined) {
                              data = occupiedEntry.content + data;
                              ctime = occupiedEntry.ctime;
                          }
                          pathObj = {
                              path: path,
                              folder: parentPath,
                              type: 'file',
                              size: data.length,
                              ctime: ctime,
                              mtime: now,
                              content: data
                          };
                          return [4 /*yield*/, this.dbRequest('put', [pathObj])];
                      case 5:
                          _a.sent();
                          return [2 /*return*/, {}];
                  }
              });
          });
      };
      /**
       * Delete a file from disk
       * @param options options for the file delete
       * @return a promise that resolves with the deleted file data result
       */
      FilesystemPluginWeb.prototype.deleteFile = function (options) {
          return __awaiter(this, void 0, void 0, function () {
              var path, entry, entries;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          path = this.getPath(options.directory, options.path);
                          return [4 /*yield*/, this.dbRequest('get', [path])];
                      case 1:
                          entry = _a.sent();
                          if (entry === undefined)
                              throw Error('File does not exist.');
                          return [4 /*yield*/, this.dbIndexRequest('by_folder', 'getAllKeys', [IDBKeyRange.only(path)])];
                      case 2:
                          entries = _a.sent();
                          if (entries.length !== 0)
                              throw Error('Folder is not empty.');
                          return [4 /*yield*/, this.dbRequest('delete', [path])];
                      case 3:
                          _a.sent();
                          return [2 /*return*/, {}];
                  }
              });
          });
      };
      /**
       * Create a directory.
       * @param options options for the mkdir
       * @return a promise that resolves with the mkdir result
       */
      FilesystemPluginWeb.prototype.mkdir = function (options) {
          return __awaiter(this, void 0, void 0, function () {
              var path, doRecursive, parentPath, depth, parentEntry, occupiedEntry, parentArgPath, now, pathObj;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          path = this.getPath(options.directory, options.path);
                          doRecursive = options.recursive;
                          parentPath = path.substr(0, path.lastIndexOf('/'));
                          depth = (path.match(/\//g) || []).length;
                          return [4 /*yield*/, this.dbRequest('get', [parentPath])];
                      case 1:
                          parentEntry = _a.sent();
                          return [4 /*yield*/, this.dbRequest('get', [path])];
                      case 2:
                          occupiedEntry = _a.sent();
                          if (depth === 1)
                              throw Error('Cannot create Root directory');
                          if (occupiedEntry !== undefined)
                              throw Error('Current directory does already exist.');
                          if (!doRecursive && depth !== 2 && parentEntry === undefined)
                              throw Error('Parent directory must exist');
                          if (!(doRecursive && depth !== 2 && parentEntry === undefined)) return [3 /*break*/, 4];
                          parentArgPath = parentPath.substr(parentPath.indexOf('/', 1));
                          return [4 /*yield*/, this.mkdir({
                                  path: parentArgPath,
                                  directory: options.directory,
                                  recursive: doRecursive
                              })];
                      case 3:
                          _a.sent();
                          _a.label = 4;
                      case 4:
                          now = Date.now();
                          pathObj = { path: path, folder: parentPath, type: 'directory', size: 0, ctime: now, mtime: now };
                          return [4 /*yield*/, this.dbRequest('put', [pathObj])];
                      case 5:
                          _a.sent();
                          return [2 /*return*/, {}];
                  }
              });
          });
      };
      /**
       * Remove a directory
       * @param options the options for the directory remove
       */
      FilesystemPluginWeb.prototype.rmdir = function (options) {
          return __awaiter(this, void 0, void 0, function () {
              var path, directory, recursive, fullPath, entry, readDirResult, _i, _a, entry_1, entryPath, entryObj;
              return __generator(this, function (_b) {
                  switch (_b.label) {
                      case 0:
                          path = options.path, directory = options.directory, recursive = options.recursive;
                          fullPath = this.getPath(directory, path);
                          return [4 /*yield*/, this.dbRequest('get', [fullPath])];
                      case 1:
                          entry = _b.sent();
                          if (entry === undefined)
                              throw Error('Folder does not exist.');
                          if (entry.type !== 'directory')
                              throw Error('Requested path is not a directory');
                          return [4 /*yield*/, this.readdir({ path: path, directory: directory })];
                      case 2:
                          readDirResult = _b.sent();
                          if (readDirResult.files.length !== 0 && !recursive)
                              throw Error('Folder is not empty');
                          _i = 0, _a = readDirResult.files;
                          _b.label = 3;
                      case 3:
                          if (!(_i < _a.length)) return [3 /*break*/, 9];
                          entry_1 = _a[_i];
                          entryPath = path + "/" + entry_1;
                          return [4 /*yield*/, this.stat({ path: entryPath, directory: directory })];
                      case 4:
                          entryObj = _b.sent();
                          if (!(entryObj.type === 'file')) return [3 /*break*/, 6];
                          return [4 /*yield*/, this.deleteFile({ path: entryPath, directory: directory })];
                      case 5:
                          _b.sent();
                          return [3 /*break*/, 8];
                      case 6: return [4 /*yield*/, this.rmdir({ path: entryPath, directory: directory, recursive: recursive })];
                      case 7:
                          _b.sent();
                          _b.label = 8;
                      case 8:
                          _i++;
                          return [3 /*break*/, 3];
                      case 9: return [4 /*yield*/, this.dbRequest('delete', [fullPath])];
                      case 10:
                          _b.sent();
                          return [2 /*return*/, {}];
                  }
              });
          });
      };
      /**
       * Return a list of files from the directory (not recursive)
       * @param options the options for the readdir operation
       * @return a promise that resolves with the readdir directory listing result
       */
      FilesystemPluginWeb.prototype.readdir = function (options) {
          return __awaiter(this, void 0, void 0, function () {
              var path, entry, entries, names;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          path = this.getPath(options.directory, options.path);
                          return [4 /*yield*/, this.dbRequest('get', [path])];
                      case 1:
                          entry = _a.sent();
                          if (options.path !== '' && entry === undefined)
                              throw Error('Folder does not exist.');
                          return [4 /*yield*/, this.dbIndexRequest('by_folder', 'getAllKeys', [IDBKeyRange.only(path)])];
                      case 2:
                          entries = _a.sent();
                          names = entries.map(function (e) {
                              return e.substring(path.length + 1);
                          });
                          return [2 /*return*/, { files: names }];
                  }
              });
          });
      };
      /**
       * Return full File URI for a path and directory
       * @param options the options for the stat operation
       * @return a promise that resolves with the file stat result
       */
      FilesystemPluginWeb.prototype.getUri = function (options) {
          return __awaiter(this, void 0, void 0, function () {
              var path, entry;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          path = this.getPath(options.directory, options.path);
                          return [4 /*yield*/, this.dbRequest('get', [path])];
                      case 1:
                          entry = _a.sent();
                          if (!(entry === undefined)) return [3 /*break*/, 3];
                          return [4 /*yield*/, this.dbRequest('get', [path + '/'])];
                      case 2:
                          entry = (_a.sent());
                          _a.label = 3;
                      case 3:
                          if (entry === undefined)
                              throw Error('Entry does not exist.');
                          return [2 /*return*/, {
                                  uri: entry.path
                              }];
                  }
              });
          });
      };
      /**
       * Return data about a file
       * @param options the options for the stat operation
       * @return a promise that resolves with the file stat result
       */
      FilesystemPluginWeb.prototype.stat = function (options) {
          return __awaiter(this, void 0, void 0, function () {
              var path, entry;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          path = this.getPath(options.directory, options.path);
                          return [4 /*yield*/, this.dbRequest('get', [path])];
                      case 1:
                          entry = _a.sent();
                          if (!(entry === undefined)) return [3 /*break*/, 3];
                          return [4 /*yield*/, this.dbRequest('get', [path + '/'])];
                      case 2:
                          entry = (_a.sent());
                          _a.label = 3;
                      case 3:
                          if (entry === undefined)
                              throw Error('Entry does not exist.');
                          return [2 /*return*/, {
                                  type: entry.type,
                                  size: entry.size,
                                  ctime: entry.ctime,
                                  mtime: entry.mtime,
                                  uri: entry.path
                              }];
                  }
              });
          });
      };
      /**
       * Rename a file or directory
       * @param options the options for the rename operation
       * @return a promise that resolves with the rename result
       */
      FilesystemPluginWeb.prototype.rename = function (options) {
          return __awaiter(this, void 0, void 0, function () {
              return __generator(this, function (_a) {
                  return [2 /*return*/, this._copy(options, true)];
              });
          });
      };
      /**
       * Copy a file or directory
       * @param options the options for the copy operation
       * @return a promise that resolves with the copy result
       */
      FilesystemPluginWeb.prototype.copy = function (options) {
          return __awaiter(this, void 0, void 0, function () {
              return __generator(this, function (_a) {
                  return [2 /*return*/, this._copy(options, false)];
              });
          });
      };
      /**
       * Function that can perform a copy or a rename
       * @param options the options for the rename operation
       * @param doRename whether to perform a rename or copy operation
       * @return a promise that resolves with the result
       */
      FilesystemPluginWeb.prototype._copy = function (options, doRename) {
          if (doRename === void 0) { doRename = false; }
          return __awaiter(this, void 0, void 0, function () {
              var to, from, fromDirectory, toDirectory, fromPath, toPath, toObj, e_1, toPathComponents, toPath_1, toParentDirectory, fromObj, updateTime, _a, file, e_2, contents, _i, contents_1, filename;
              var _this = this;
              return __generator(this, function (_b) {
                  switch (_b.label) {
                      case 0:
                          to = options.to, from = options.from, fromDirectory = options.directory, toDirectory = options.toDirectory;
                          if (!to || !from) {
                              throw Error('Both to and from must be provided');
                          }
                          // If no "to" directory is provided, use the "from" directory
                          if (!toDirectory) {
                              toDirectory = fromDirectory;
                          }
                          fromPath = this.getPath(fromDirectory, from);
                          toPath = this.getPath(toDirectory, to);
                          // Test that the "to" and "from" locations are different
                          if (fromPath === toPath) {
                              return [2 /*return*/, {}];
                          }
                          if (toPath.startsWith(fromPath)) {
                              throw Error('To path cannot contain the from path');
                          }
                          _b.label = 1;
                      case 1:
                          _b.trys.push([1, 3, , 6]);
                          return [4 /*yield*/, this.stat({
                                  path: to,
                                  directory: toDirectory
                              })];
                      case 2:
                          toObj = _b.sent();
                          return [3 /*break*/, 6];
                      case 3:
                          e_1 = _b.sent();
                          toPathComponents = to.split('/');
                          toPathComponents.pop();
                          toPath_1 = toPathComponents.join('/');
                          if (!(toPathComponents.length > 0)) return [3 /*break*/, 5];
                          return [4 /*yield*/, this.stat({
                                  path: toPath_1,
                                  directory: toDirectory,
                              })];
                      case 4:
                          toParentDirectory = _b.sent();
                          if (toParentDirectory.type !== 'directory') {
                              throw new Error('Parent directory of the to path is a file');
                          }
                          _b.label = 5;
                      case 5: return [3 /*break*/, 6];
                      case 6:
                          // Cannot overwrite a directory
                          if (toObj && toObj.type === 'directory') {
                              throw new Error('Cannot overwrite a directory with a file');
                          }
                          return [4 /*yield*/, this.stat({
                                  path: from,
                                  directory: fromDirectory,
                              })];
                      case 7:
                          fromObj = _b.sent();
                          updateTime = function (path, ctime, mtime) { return __awaiter(_this, void 0, void 0, function () {
                              var fullPath, entry;
                              return __generator(this, function (_a) {
                                  switch (_a.label) {
                                      case 0:
                                          fullPath = this.getPath(toDirectory, path);
                                          return [4 /*yield*/, this.dbRequest('get', [fullPath])];
                                      case 1:
                                          entry = _a.sent();
                                          entry.ctime = ctime;
                                          entry.mtime = mtime;
                                          return [4 /*yield*/, this.dbRequest('put', [entry])];
                                      case 2:
                                          _a.sent();
                                          return [2 /*return*/];
                                  }
                              });
                          }); };
                          _a = fromObj.type;
                          switch (_a) {
                              case 'file': return [3 /*break*/, 8];
                              case 'directory': return [3 /*break*/, 15];
                          }
                          return [3 /*break*/, 28];
                      case 8: return [4 /*yield*/, this.readFile({
                              path: from,
                              directory: fromDirectory
                          })];
                      case 9:
                          file = _b.sent();
                          if (!doRename) return [3 /*break*/, 11];
                          return [4 /*yield*/, this.deleteFile({
                                  path: from,
                                  directory: fromDirectory
                              })];
                      case 10:
                          _b.sent();
                          _b.label = 11;
                      case 11: 
                      // Write the file to the new location
                      return [4 /*yield*/, this.writeFile({
                              path: to,
                              directory: toDirectory,
                              data: file.data
                          })];
                      case 12:
                          // Write the file to the new location
                          _b.sent();
                          if (!doRename) return [3 /*break*/, 14];
                          return [4 /*yield*/, updateTime(to, fromObj.ctime, fromObj.mtime)];
                      case 13:
                          _b.sent();
                          _b.label = 14;
                      case 14: 
                      // Resolve promise
                      return [2 /*return*/, {}];
                      case 15:
                          if (toObj) {
                              throw Error('Cannot move a directory over an existing object');
                          }
                          _b.label = 16;
                      case 16:
                          _b.trys.push([16, 20, , 21]);
                          // Create the to directory
                          return [4 /*yield*/, this.mkdir({
                                  path: to,
                                  directory: toDirectory,
                                  recursive: false,
                              })];
                      case 17:
                          // Create the to directory
                          _b.sent();
                          if (!doRename) return [3 /*break*/, 19];
                          return [4 /*yield*/, updateTime(to, fromObj.ctime, fromObj.mtime)];
                      case 18:
                          _b.sent();
                          _b.label = 19;
                      case 19: return [3 /*break*/, 21];
                      case 20:
                          e_2 = _b.sent();
                          return [3 /*break*/, 21];
                      case 21: return [4 /*yield*/, this.readdir({
                              path: from,
                              directory: fromDirectory,
                          })];
                      case 22:
                          contents = (_b.sent()).files;
                          _i = 0, contents_1 = contents;
                          _b.label = 23;
                      case 23:
                          if (!(_i < contents_1.length)) return [3 /*break*/, 26];
                          filename = contents_1[_i];
                          // Move item from the from directory to the to directory
                          return [4 /*yield*/, this._copy({
                                  from: from + "/" + filename,
                                  to: to + "/" + filename,
                                  directory: fromDirectory,
                                  toDirectory: toDirectory,
                              }, doRename)];
                      case 24:
                          // Move item from the from directory to the to directory
                          _b.sent();
                          _b.label = 25;
                      case 25:
                          _i++;
                          return [3 /*break*/, 23];
                      case 26:
                          if (!doRename) return [3 /*break*/, 28];
                          return [4 /*yield*/, this.rmdir({
                                  path: from,
                                  directory: fromDirectory
                              })];
                      case 27:
                          _b.sent();
                          _b.label = 28;
                      case 28: return [2 /*return*/, {}];
                  }
              });
          });
      };
      FilesystemPluginWeb._debug = true;
      return FilesystemPluginWeb;
  }(WebPlugin));
  export { FilesystemPluginWeb };
  var Filesystem = new FilesystemPluginWeb();
  export { Filesystem };
  //# sourceMappingURL=filesystem.js.map