window.RP = (function() {
  var exports = {};
  
  function alertError(err) {
    alert(err);
    throw err;
  }

  var ID = (function() {
    var counter = 0;
    return function ID(prefix) {
      prefix = prefix || '';
      var date = Date.now().toString(36).padStart(9, 0);
      var num = (counter++ % 36**6).toString(36).padStart(6,0)
      var rand = Math.random().toString(36).slice(2);
      return prefix + date + num + rand;
    }
  }());

  exports.initialize = function initialize(roomid, page, callbacks) {
    var onready = callbacks.ready;
    var onmsg = callbacks.msg;
    var onchara = callbacks.chara;
    var ontitle = callbacks.title;
    var onuser = callbacks.user;
    var onpagecount = callbacks.pageCount;
    var onerror = callbacks.error;

    userbase.init({ appId: '8dcdb794-f6c1-488d-966d-0058b5889c93' })
    .then(function () {
      var isFirstUpdate = true;
      // TODO: hopefully at some point, the API for Userbase will change
      // so that we get notified of the exact changes, rather than having
      // to figure them out ourselves. At that point, we probably won't
      // need this Map anymore
      // Also if we ever implement Deletes then this will fail to notice
      // them soooo
      var previousObjectReferences = new Map();

      userbase.openDatabase({
        databaseName: roomid,
        changeHandler(changes) {
          try {
            changes.forEach(function (change) {
              if (previousObjectReferences.get(change.itemId) === change.item) {
                return;
              }
              if (change.itemId === 'title') {
                ontitle(change.item);
              } else if (change.itemId.startsWith('m-')) {
                var obj = Object.assign({ _id: change.itemId }, change.item);
                onmsg(obj, isFirstUpdate);
              } else if (change.itemId.startsWith('c-')) {
                var obj = Object.assign({ _id: change.itemId }, change.item);
                onchara(obj, isFirstUpdate);
              }
              previousObjectReferences.set(change.itemId, change.item);
            })
            if (isFirstUpdate) {
              onpagecount(changes.filter(c => c.itemId.startsWith('m-')).length || 1);
              onready(isFirstUpdate);
              isFirstUpdate = false;
            }
          } catch (err) {
            onerror(err);
          }
        }
      })
      .then(function() {
        // connected!
        if (!previousObjectReferences.has('title')) {
          userbase.insertItem({
            databaseName: roomid,
            itemId: 'title',
            item: 'Untitled',
          })
        }
      })
      .catch(function (err) {
        // window.location.replace('/auth');
        onerror(err);
        // onerror(err, true);
      })
    })
  }

  function upsert(collection, data, callback) {
    var isUpdate = !!data._id;

    var itemId = data._id || ID(collection);

    var item = Object.assign({}, data);
    delete item._id;

    var method = isUpdate ? 'updateItem' : 'insertItem';

    var params = {
      databaseName: roomid,
      itemId: itemId,
      item: item,
    }

    userbase[method](params)
    .catch(alertError)
    .then(callback.bind(null, data))
  }

  exports.sendMessage = function sendMessage(data, callback) {
    upsert('m-', data, callback);
  }

  exports.sendChara = function sendChara(data, callback) {
    upsert('c-', data, callback);
  }

  exports.getMessageHistory = function getMessageHistory(_id, callback) {
    return request('GET', `api/msgs/${_id}/history`)
    .catch(alertError)
    .then(callback)
  }

  exports.changeTitle = function changeTitle(title) {
    userbase.updateItem({
      databaseName: roomid,
      itemId: 'title',
      item: title,
    })
    .catch(alertError)
  }

  exports.changeMyUsername = function changeMyUsername(name, callback) {
    return requestWithJSON('PUT', 'api/username', { name: name })
    .catch(alertError)
    .then(callback)
  }

  exports.addWebhook = function addWebhook(webhook, callback) {
    return requestWithJSON('PUT', 'api/webhook', { webhook: webhook })
    .catch(alertError)
    .then(callback)
  }
  
  exports.checkForUpdates = function checkForUpdates() {
    return request('GET', '/version')
    .then(function (versions) {
      if (versions.current === versions.latest) {
        return new Promise(function() {}); // never return
      } else {
        return versions;
      }
    })
  }
  
  Object.defineProperty(exports, 'myUserID', { get: function() {
    var cookieName = 'userid';
    var cookieMatch = document.cookie.match('(^|[^;]+)\\s*' + cookieName + '\\s*=\\s*([^;]+)');
    return cookieMatch ? cookieMatch.pop() : '';
  } });
  
  return exports;
  
}());

@@ -3445,183 +3445,8 @@
  %7D%0A%0A
-  exports.getMessageHistory = function getMessageHistory(_id, callback) %7B%0A    return request('GET', %60api/msgs/$%7B_id%7D/history%60)%0A    .catch(alertError)%0A    .then(callback)%0A  %7D%0A%0A
   ex
@@ -3640,659 +3640,174 @@
 rts.
-changeMyUsername = function changeMyUsername(name, callback) %7B%0A    return requestWithJSON('PUT', 'api/username', %7B name: name %7D)%0A    .catch(alertError)%0A    .then(callback)%0A  %7D%0A%0A  exports.addWebhook = function addWebhook(webhook, callback) %7B%0A    return requestWithJSON('PUT', 'api/webhook', %7B webhook: webhook %7D)%0A    .catch(alertError)%0A    .then(callback)%0A  %7D%0A  %0A  exports.checkForUpdates = function checkForUpdates() %7B%0A    return request('GET', '/version')%0A    .then(function (versions) %7B%0A      if (versions.current === versions.latest) %7B%0A        return new Promise(function() %7B%7D); // never return%0A      %7D else %7B%0A        return versions;%0A      %7D%0A    %7D
+addWebhook = function addWebhook(webhook, callback) %7B%0A    return requestWithJSON('PUT', 'api/webhook', %7B webhook: webhook %7D)%0A    .catch(alertError)%0A    .then(callback
 )%0A  

remove RP functions that aren't meant to be (yet)