text: "use strict";
  
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  
  Object.defineProperty(exports, "__esModule", {
    value: true
  });
  exports.getIdb = getIdb;
  exports.createDatabase = createDatabase;
  exports.writeMessage = writeMessage;
  exports.getAllMessages = getAllMessages;
  exports.getMessagesHigherThan = getMessagesHigherThan;
  exports.removeMessageById = removeMessageById;
  exports.getOldMessages = getOldMessages;
  exports.cleanOldMessages = cleanOldMessages;
  exports.create = create;
  exports.close = close;
  exports.postMessage = postMessage;
  exports.onMessage = onMessage;
  exports.canBeUsed = canBeUsed;
  exports.averageResponseTime = averageResponseTime;
  exports["default"] = exports.type = exports.microSeconds = void 0;
  
  var _util = require("../util.js");
  
  var _obliviousSet = _interopRequireDefault(require("../oblivious-set"));
  
  var _options = require("../options");
  
  /**
   * this method uses indexeddb to store the messages
   * There is currently no observerAPI for idb
   * @link https://github.com/w3c/IndexedDB/issues/51
   */
  var microSeconds = _util.microSeconds;
  exports.microSeconds = microSeconds;
  var DB_PREFIX = 'pubkey.broadcast-channel-0-';
  var OBJECT_STORE_ID = 'messages';
  var type = 'idb';
  exports.type = type;
  
  function getIdb() {
    if (typeof indexedDB !== 'undefined') return indexedDB;
  
    if (typeof window !== 'undefined') {
      if (typeof window.mozIndexedDB !== 'undefined') return window.mozIndexedDB;
      if (typeof window.webkitIndexedDB !== 'undefined') return window.webkitIndexedDB;
      if (typeof window.msIndexedDB !== 'undefined') return window.msIndexedDB;
    }
  
    return false;
  }
  
  function createDatabase(channelName) {
    var IndexedDB = getIdb(); // create table
  
    var dbName = DB_PREFIX + channelName;
    var openRequest = IndexedDB.open(dbName, 1);
  
    openRequest.onupgradeneeded = function (ev) {
      var db = ev.target.result;
      db.createObjectStore(OBJECT_STORE_ID, {
        keyPath: 'id',
        autoIncrement: true
      });
    };
  
    var dbPromise = new Promise(function (res, rej) {
      openRequest.onerror = function (ev) {
        return rej(ev);
      };
  
      openRequest.onsuccess = function () {
        res(openRequest.result);
      };
    });
    return dbPromise;
  }
  /**
   * writes the new message to the database
   * so other readers can find it
   */
  
  
  function writeMessage(db, readerUuid, messageJson) {
    var time = new Date().getTime();
    var writeObject = {
      uuid: readerUuid,
      time: time,
      data: messageJson
    };
    var transaction = db.transaction([OBJECT_STORE_ID], 'readwrite');
    return new Promise(function (res, rej) {
      transaction.oncomplete = function () {
        return res();
      };
  
      transaction.onerror = function (ev) {
        return rej(ev);
      };
  
      var objectStore = transaction.objectStore(OBJECT_STORE_ID);
      objectStore.add(writeObject);
    });
  }
  
  function getAllMessages(db) {
    var objectStore = db.transaction(OBJECT_STORE_ID).objectStore(OBJECT_STORE_ID);
    var ret = [];
    return new Promise(function (res) {
      objectStore.openCursor().onsuccess = function (ev) {
        var cursor = ev.target.result;
  
        if (cursor) {
          ret.push(cursor.value); //alert("Name for SSN " + cursor.key + " is " + cursor.value.name);
  
          cursor["continue"]();
        } else {
          res(ret);
        }
      };
    });
  }
  
  function getMessagesHigherThan(db, lastCursorId) {
    var objectStore = db.transaction(OBJECT_STORE_ID).objectStore(OBJECT_STORE_ID);
    var ret = [];
  
    function openCursor() {
      // Occasionally Safari will fail on IDBKeyRange.bound, this
      // catches that error, having it open the cursor to the first
      // item. When it gets data it will advance to the desired key.
      try {
        var keyRangeValue = IDBKeyRange.bound(lastCursorId + 1, Infinity);
        return objectStore.openCursor(keyRangeValue);
      } catch (e) {
        return objectStore.openCursor();
      }
    }
  
    return new Promise(function (res) {
      openCursor().onsuccess = function (ev) {
        var cursor = ev.target.result;
  
        if (cursor) {
          if (cursor.value.id < lastCursorId + 1) {
            cursor["continue"](lastCursorId + 1);
          } else {
            ret.push(cursor.value);
            cursor["continue"]();
          }
        } else {
          res(ret);
        }
      };
    });
  }
  
  function removeMessageById(db, id) {
    var request = db.transaction([OBJECT_STORE_ID], 'readwrite').objectStore(OBJECT_STORE_ID)["delete"](id);
    return new Promise(function (res) {
      request.onsuccess = function () {
        return res();
      };
    });
  }
  
  function getOldMessages(db, ttl) {
    var olderThen = new Date().getTime() - ttl;
    var objectStore = db.transaction(OBJECT_STORE_ID).objectStore(OBJECT_STORE_ID);
    var ret = [];
    return new Promise(function (res) {
      objectStore.openCursor().onsuccess = function (ev) {
        var cursor = ev.target.result;
  
        if (cursor) {
          var msgObk = cursor.value;
  
          if (msgObk.time < olderThen) {
            ret.push(msgObk); //alert("Name for SSN " + cursor.key + " is " + cursor.value.name);
  
            cursor["continue"]();
          } else {
            // no more old messages,
            res(ret);
            return;
          }
        } else {
          res(ret);
        }
      };
    });
  }
  
  function cleanOldMessages(db, ttl) {
    return getOldMessages(db, ttl).then(function (tooOld) {
      return Promise.all(tooOld.map(function (msgObj) {
        return removeMessageById(db, msgObj.id);
      }));
    });
  }
  
  function create(channelName, options) {
    options = (0, _options.fillOptionsWithDefaults)(options);
    return createDatabase(channelName).then(function (db) {
      var state = {
        closed: false,
        lastCursorId: 0,
        channelName: channelName,
        options: options,
        uuid: (0, _util.randomToken)(),
  
        /**
         * emittedMessagesIds
         * contains all messages that have been emitted before
         * @type {ObliviousSet}
         */
        eMIs: new _obliviousSet["default"](options.idb.ttl * 2),
        // ensures we do not read messages in parrallel
        writeBlockPromise: Promise.resolve(),
        messagesCallback: null,
        readQueuePromises: [],
        db: db
      };
      /**
       * Handle abrupt closes that do not originate from db.close().
       * This could happen, for example, if the underlying storage is
       * removed or if the user clears the database in the browser's
       * history preferences.
       */
  
      db.onclose = function () {
        state.closed = true;
        if (options.idb.onclose) options.idb.onclose();
      };
      /**
       * if service-workers are used,
       * we have no 'storage'-event if they post a message,
       * therefore we also have to set an interval
       */
  
  
      _readLoop(state);
  
      return state;
    });
  }
  
  function _readLoop(state) {
    if (state.closed) return;
    readNewMessages(state).then(function () {
      return (0, _util.sleep)(state.options.idb.fallbackInterval);
    }).then(function () {
      return _readLoop(state);
    });
  }
  
  function _filterMessage(msgObj, state) {
    if (msgObj.uuid === state.uuid) return false; // send by own
  
    if (state.eMIs.has(msgObj.id)) return false; // already emitted
  
    if (msgObj.data.time < state.messagesCallbackTime) return false; // older then onMessageCallback
  
    return true;
  }
  /**
   * reads all new messages from the database and emits them
   */
  
  
  function readNewMessages(state) {
    // channel already closed
    if (state.closed) return Promise.resolve(); // if no one is listening, we do not need to scan for new messages
  
    if (!state.messagesCallback) return Promise.resolve();
    return getMessagesHigherThan(state.db, state.lastCursorId).then(function (newerMessages) {
      var useMessages = newerMessages
      /**
       * there is a bug in iOS where the msgObj can be undefined some times
       * so we filter them out
       * @link https://github.com/pubkey/broadcast-channel/issues/19
       */
      .filter(function (msgObj) {
        return !!msgObj;
      }).map(function (msgObj) {
        if (msgObj.id > state.lastCursorId) {
          state.lastCursorId = msgObj.id;
        }
  
        return msgObj;
      }).filter(function (msgObj) {
        return _filterMessage(msgObj, state);
      }).sort(function (msgObjA, msgObjB) {
        return msgObjA.time - msgObjB.time;
      }); // sort by time
  
      useMessages.forEach(function (msgObj) {
        if (state.messagesCallback) {
          state.eMIs.add(msgObj.id);
          state.messagesCallback(msgObj.data);
        }
      });
      return Promise.resolve();
    });
  }
  
  function close(channelState) {
    channelState.closed = true;
    channelState.db.close();
  }
  
  function postMessage(channelState, messageJson) {
    channelState.writeBlockPromise = channelState.writeBlockPromise.then(function () {
      return writeMessage(channelState.db, channelState.uuid, messageJson);
    }).then(function () {
      if ((0, _util.randomInt)(0, 10) === 0) {
        /* await (do not await) */
        cleanOldMessages(channelState.db, channelState.options.idb.ttl);
      }
    });
    return channelState.writeBlockPromise;
  }
  
  function onMessage(channelState, fn, time) {
    channelState.messagesCallbackTime = time;
    channelState.messagesCallback = fn;
    readNewMessages(channelState);
  }
  
  function canBeUsed() {
    if (_util.isNode) return false;
    var idb = getIdb();
    if (!idb) return false;
    return true;
  }
  
  function averageResponseTime(options) {
    return options.idb.fallbackInterval * 2;
  }
  
  var _default = {
    create: create,
    close: close,
    onMessage: onMessage,
    postMessage: postMessage,
    canBeUsed: canBeUsed,
    type: type,
    averageResponseTime: averageResponseTime,
    microSeconds: microSeconds
  };
  exports["default"] = _default;