Better bateworld.com

21/10/2025, 20:41:33

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @namespace   Circlejerk Scripts
// @name        Better bateworld.com
// @version     1.3.4
// @author      Thick Bro
// @match       https://bateworld.com//html5-chat/chat2/*
// @grant       GM_addStyle
// @grant       GM_getValue
// @grant       GM_getValues
// @grant       GM_setValue
// @grant       GM_setValues
// @grant       GM_listValues
// @grant       GM_addElement
// @require     https://unpkg.com/[email protected]/dist/preact.min.js#sha512-iMvQ2nmrBGovAh+dbYrh8gttIQ4Xa/aYwXhrgYdlhNHDXdyQA4tM0JyI81xJEm+laaTWnIhWGgawYgpEihg7AQ==
// @require     https://unpkg.com/[email protected]/hooks/dist/hooks.umd.js#sha512-lLsbsdkj5qskElcWFvsifiWNJH4y8GeUtEdI3a1KZoVF+TCDvk/UGCyf/2mKpJtwI1XKJh89hVCVI2J3rEytQQ==
// @require     https://unpkg.com/[email protected]/jsx-runtime/dist/jsxRuntime.umd.js#sha512-IRTpXYSw0jUJELE+zE319bPSVme0v6QV3LVh/SD5jljvKW4hb+LL6TRpMfkTEQvccsC5N7+6YusE6ol3yFuhLw==
// @description 21/10/2025, 20:41:33
// @license GPL-3.0-or-later
// ==/UserScript==

var __webpack_modules__ = ({
"./src/betterbw/styles/openPanels.css?inline": (function (module, __webpack_exports__, __webpack_require__) {
__webpack_require__.d(__webpack_exports__, {
  A: () => (__rspack_default_export)
});
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0 = __webpack_require__("./node_modules/@rsbuild/core/compiled/css-loader/noSourceMaps.js");
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0_default = /*#__PURE__*/__webpack_require__.n(_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0);
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1 = __webpack_require__("./node_modules/@rsbuild/core/compiled/css-loader/api.js");
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1_default = /*#__PURE__*/__webpack_require__.n(_node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1);
// Imports

var ___CSS_LOADER_EXPORT___ = _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1_default()((_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0_default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, `[data-username="I_am_watching"] {
  #userList &.userItem .webcamBtn {
    background: #50ce85 !important;
  }

  #tabs &.userItem:before, #tabs & .watchCam:before {
    content: "";
    border: 1px solid #b2b2b2;
    border-top-width: 3px;
    border-radius: 2px;
    width: 12px;
    height: 11px;
    display: block;
    position: absolute;
    top: 50%;
    transform: translateY(-50%)translateX(-130%);
  }
}

.watchingMe, .user_watching_me {
  --gridGap: 4px;
  transition: box-shadow .2s ease-out;
  box-shadow: gold 0px 0px 1px var(--gridGap) !important;

  & .jsPanel-headerbar {
    background: linear-gradient(gold 0%, #0000 30% 70%, gold 85%);
  }

  & .jsPanel-title:before {
    content: "👁️";
    content: "";
    color: gold;
    z-index: 1;
    text-shadow: 1px 1px #daa520;
    font-family: "Font Awesome 5 Free";
    font-size: 14px;
    font-weight: 400;
    position: absolute;
    top: -5px;
    left: -2px;
  }
}
`, ""]);
// Exports
/* export default */ const __rspack_default_export = (___CSS_LOADER_EXPORT___.toString());

}),
"./src/betterbw/styles/static.css?inline": (function (module, __webpack_exports__, __webpack_require__) {
__webpack_require__.d(__webpack_exports__, {
  A: () => (__rspack_default_export)
});
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0 = __webpack_require__("./node_modules/@rsbuild/core/compiled/css-loader/noSourceMaps.js");
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0_default = /*#__PURE__*/__webpack_require__.n(_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0);
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1 = __webpack_require__("./node_modules/@rsbuild/core/compiled/css-loader/api.js");
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1_default = /*#__PURE__*/__webpack_require__.n(_node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1);
// Imports

var ___CSS_LOADER_EXPORT___ = _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1_default()((_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0_default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, `body > div:first-child {
  height: calc(100% - 4px);
}

body:not([data-algo]) button[data-sort-order=""], body[data-algo=""] button[data-sort-order=""], body[data-algo="new"] button[data-sort-order="new"], body[data-algo="top"] button[data-sort-order="top"] {
  color: #fff;
  background-color: #50c180;
}

input.numberOfCams {
  width: 4em;
}

.panel-action {
  position: absolute;
  left: 3px;
}

.panel-action-btn {
  cursor: pointer;
  color: inherit;
  float: right;
  opacity: 1;
  background: #fffc;
  border: 1px solid #ddd;
  height: 15px;
  margin-left: 8px;
  font-size: 14px;
  line-height: 0;
  transition: opacity .2s ease-out;
  position: relative;
  top: 2px;
}

.panel-action-btn:where([data-status-value="--"], [data-status-value="-"], [data-status-value="+"], [data-status-value="++"]) {
  margin-left: 0;
}

.jsPanel[data-grid-index][data-actions-attached]:not(.ui-draggable-dragging) {
  transition: top .15s linear, right .15s linear, bottom .15s linear, left .15s linear, box-shadow .2s ease-out;
}

.jsPanel:not(:hover) .panel-action-btn {
  opacity: 0;
}

.jsPanel .speaks {
  width: 3px;
  top: 0;
  bottom: 0;
  left: 0;
  rotate: 180deg;

  & .volume {
    background: #ff0;
    box-shadow: 1px 1px 1px #000, 2px 2px 1px #fff;
  }
}

.jsPanel .jsPanel-content {
  background: #111;
}

:where(#userMenu, .jsPanel)[data-status="--"] [data-status-value="--"] {
  color: #fff;
  background: #cd0000;
}

:where(#userMenu, .jsPanel)[data-status="-"] [data-status-value="-"] {
  color: #fff;
  background: #ed9200;
}

:where(#userMenu, .jsPanel)[data-status="+"] [data-status-value="+"] {
  color: #fff;
  background: #00a7e4;
}

:where(#userMenu, .jsPanel)[data-status="++"] [data-status-value="++"] {
  color: #fff;
  background: #9532ff;
}

:where(#userMenu, .jsPanel)[data-is-cooldown="true"] [data-status-value="⏱"] {
  color: #fff;
  background: #323232;
}

#userMenu .menuUserItem[data-action="whisper"] {
  display: block !important;
}

#userMenu[data-private-cam="true"] [data-action="viewWebcam"] {
  color: red;

  & .fa.fa-video-camera:after {
    color: red;
    content: " ";
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
  }
}

#tabs .addPrivateMessage {
  & .mention, & .userLabelBBW.receiver {
    pointer-events: none;
  }

  & .name-time {
    flex-wrap: wrap;

    & .content {
      flex-basis: 100%;
    }
  }

  & .whisper:after {
    width: initial;
    opacity: .75;
    background: linear-gradient(90deg, #0000 0%, red 25% 75%, #0000 100%);
    height: 10px;
    padding-inline: 25px;
    font-size: 8px;
    left: 0;
    right: auto;
  }
}

#tabs .serverMessage[data-private-cam="true"]:after, #userMenu[data-private-cam="true"] [data-action="viewWebcam"].fa:after {
  content: " ";
  color: red;
  font-family: "Font Awesome 5 Free";
  font-weight: 900;
}

#tabs .serverMessage[data-private-cam="true"]:after {
  margin-left: 20px;
}

#tabs .webcamOpened .watchCam {
  position: relative;
}

.jsPanel .userAvatar {
  cursor: pointer;
}

.jsPanel[data-status="undefined"] .jsPanel-title {
  text-decoration: underline 2px orange;
}

.jsPanel[data-status="undefined"] button:where([data-status-value="++"]), .jsPanel[data-status="--"] button:where([data-status-value="++"]), .jsPanel[data-status="-"] button:where([data-status-value="++"]), .jsPanel[data-status="+"] button:where([data-status-value="--"]), .jsPanel[data-status="++"] button:where([data-status-value="--"]) {
  display: none;
}

.jsPanel:where([data-rotation=""], [data-rotation="0"]) video {
  rotate: none;
}

.jsPanel[data-rotation="90"] video {
  rotate: 90deg;
}

.jsPanel[data-rotation="180"] video {
  rotate: 180deg;
}

.jsPanel[data-rotation="270"] video {
  rotate: 270deg;
}

.jsPanel[data-rotation="90"] video, .jsPanel[data-rotation="270"] video {
  margin-left: 7.5%;
  width: 85% !important;
}

.slide_block {
  width: 14px;
}

#usersContainer {
  width: 240px;

  &.leftLayout #slide_block {
    z-index: 101;
    height: 37px;
    top: 50px;
    left: 31px;
  }
}

#myWebcamContainer .btn {
  text-wrap: inherit;
  line-height: 1 !important;
}

#userList .userItem {
  border-bottom: none;
  margin-left: 1px;
}

#userList .userLabel {
  border-top-left-radius: 9px;
  border-bottom-left-radius: 9px;
  margin-left: 2px;
  padding-left: 8px;
  top: 0;

  & .userSince {
    left: unset;
  }
}

.webcamBtn {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  margin: 0;
  padding-inline: 1rem;
}

.webcamBtn i.lock {
  &.fa-unlock {
    display: none;
  }

  &.fa-lock {
    text-shadow: -2px 2px #fff;
    top: 1px;
    right: -25px;
  }

  font-size: 16px;
  position: absolute;
  top: 5px;
  left: -11px;
}

.webcamBtn i.fa-volume-down {
  padding-inline: 4px;
}

.eye-icon .fa-eye.isWatching {
  text-shadow: -1px -1px 1px #daa520;
  position: absolute;
  bottom: 5px;
  right: 42px;
  color: gold !important;
}

#userList .userItem:has(.webcamBtn.visible i.lock.fa-lock) {
  --lock-color: #cd000054;
  --v-padding: 0px;

  & .userLabel {
    box-shadow: inset 3px 0 1px 2px var(--lock-color);
  }

  & .webcamBtn.visible {
    box-shadow: inset -3px 0 1px 2px var(--lock-color);
    border: none;
  }
}

.jsPanel {
  box-shadow: none;
  border-color: #888 !important;
}

#roomsBtn {
  line-height: 1 !important;
}

#header {
  height: 30px !important;
}

#tabsAndFooter, #footer {
  width: min(50%, 100% - 1107px);
}

#tabsAndFooter {
  resize: horizontal;
  overflow: hidden;
}

#footer {
  width: 100%;
  position: relative;
}

video.mobile.mobile {
  max-width: 100% !important;
  max-height: 100% !important;
}

.jsPanel-headerbar {
  min-height: 18px;
}

.jsPanel-titlebar {
  min-height: 16px;
}

.jsPanel-titlebar h3 {
  margin-block: 1px;
}
`, ""]);
// Exports
/* export default */ const __rspack_default_export = (___CSS_LOADER_EXPORT___.toString());

}),
"./src/betterbw/styles/tiers.css?inline": (function (module, __webpack_exports__, __webpack_require__) {
__webpack_require__.d(__webpack_exports__, {
  A: () => (__rspack_default_export)
});
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0 = __webpack_require__("./node_modules/@rsbuild/core/compiled/css-loader/noSourceMaps.js");
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0_default = /*#__PURE__*/__webpack_require__.n(_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0);
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1 = __webpack_require__("./node_modules/@rsbuild/core/compiled/css-loader/api.js");
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1_default = /*#__PURE__*/__webpack_require__.n(_node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1);
// Imports

var ___CSS_LOADER_EXPORT___ = _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1_default()((_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0_default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, `#userList .userItem:where(.group_minus_minus) {
  opacity: .3 !important;
}

#tabs .userLabelBBW:where(.group_minus_minus) {
  text-decoration: line-through 1.8rem #cd000026;
}

#tabs .userLabelBBW:where(.group_minus) {
  text-decoration: line-through 1.8rem #ed920033;
}

#userList .userItem:where(.group_minus) .userLabel {
  background: #ed920033;
}

#tabs .userLabelBBW:where(.group_plus) {
  text-decoration: line-through 1.8rem #0064ff33;
}

#userList .userItem:where(.group_plus) .userLabel {
  background: #0064ff33;
}

#tabs .userLabelBBW:where(.group_plus_plus) {
  text-decoration: line-through 1.8rem #9532ff33;
}

#userList .userItem:where(.group_plus_plus) .userLabel {
  background: #9532ff33;
}
`, ""]);
// Exports
/* export default */ const __rspack_default_export = (___CSS_LOADER_EXPORT___.toString());

}),
"./node_modules/@rsbuild/core/compiled/css-loader/api.js": (function (module) {

/*
  MIT License http://www.opensource.org/licenses/mit-license.php
  Author Tobias Koppers @sokra
*/
module.exports = function (cssWithMappingToString) {
  var list = [];

  // return the list of modules as css string
  list.toString = function toString() {
    return this.map(function (item) {
      var content = "";
      var needLayer = typeof item[5] !== "undefined";
      if (item[4]) {
        content += "@supports (".concat(item[4], ") {");
      }
      if (item[2]) {
        content += "@media ".concat(item[2], " {");
      }
      if (needLayer) {
        content += "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {");
      }
      content += cssWithMappingToString(item);
      if (needLayer) {
        content += "}";
      }
      if (item[2]) {
        content += "}";
      }
      if (item[4]) {
        content += "}";
      }
      return content;
    }).join("");
  };

  // import a list of modules into the list
  list.i = function i(modules, media, dedupe, supports, layer) {
    if (typeof modules === "string") {
      modules = [[null, modules, undefined]];
    }
    var alreadyImportedModules = {};
    if (dedupe) {
      for (var k = 0; k < this.length; k++) {
        var id = this[k][0];
        if (id != null) {
          alreadyImportedModules[id] = true;
        }
      }
    }
    for (var _k = 0; _k < modules.length; _k++) {
      var item = [].concat(modules[_k]);
      if (dedupe && alreadyImportedModules[item[0]]) {
        continue;
      }
      if (typeof layer !== "undefined") {
        if (typeof item[5] === "undefined") {
          item[5] = layer;
        } else {
          item[1] = "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {").concat(item[1], "}");
          item[5] = layer;
        }
      }
      if (media) {
        if (!item[2]) {
          item[2] = media;
        } else {
          item[1] = "@media ".concat(item[2], " {").concat(item[1], "}");
          item[2] = media;
        }
      }
      if (supports) {
        if (!item[4]) {
          item[4] = "".concat(supports);
        } else {
          item[1] = "@supports (".concat(item[4], ") {").concat(item[1], "}");
          item[4] = supports;
        }
      }
      list.push(item);
    }
  };
  return list;
};

}),
"./node_modules/@rsbuild/core/compiled/css-loader/noSourceMaps.js": (function (module) {

module.exports = function (i) {
  return i[1];
};

}),

});
// The module cache
var __webpack_module_cache__ = {};

// The require function
function __webpack_require__(moduleId) {

// Check if module is in cache
var cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
// Create a new module (and put it into the cache)
var module = (__webpack_module_cache__[moduleId] = {
id: moduleId,
exports: {}
});
// Execute the module function
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);

// Return the exports of the module
return module.exports;

}

// webpack/runtime/compat_get_default_export
(() => {
// getDefaultExport function for compatibility with non-ESM modules
__webpack_require__.n = (module) => {
	var getter = module && module.__esModule ?
		() => (module['default']) :
		() => (module);
	__webpack_require__.d(getter, { a: getter });
	return getter;
};

})();
// webpack/runtime/define_property_getters
(() => {
__webpack_require__.d = (exports, definition) => {
	for(var key in definition) {
        if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
            Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
        }
    }
};
})();
// webpack/runtime/has_own_property
(() => {
__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
})();
var __webpack_exports__ = {};

;// file://./src/betterbw/utils/sortFunctions.ts
const topRandom = (a, b) => b.bias - a.bias || Math.random() - 0.5;

// EXTERNAL MODULE: ./src/betterbw/styles/static.css?inline
var staticinline = __webpack_require__("./src/betterbw/styles/static.css?inline");
;// file://./src/betterbw/features/cooldown.ts
function cooldownIt({ username, minutes = 15, panel }) {
  if (!username) return;
  const expiry = Date.now() + 60000 * minutes;
  setCooldown(username, expiry);
  console.log(
    `User ${username} put on cooldown until`,
    new Date(expiry).toISOString(),
  );
  if (panel) jsPanel.activePanels.getPanel(panel.id)?.close();
}
function setCooldown(id, expiryMs) {
  GM_setValue(`${id}_cooldown`, expiryMs);
}
function getCooldownExpiry(id) {
  const val = GM_getValue(`${id}_cooldown`);
  return val ? Number(val) : null;
}
function isOnCooldown(id) {
  const expiry = getCooldownExpiry(id);
  return expiry && Date.now() < expiry;
}

;// file://./src/betterbw/utils/organizePanels.ts

const base = { my: "right-top", at: "right-top" };
const panelRatio = 318 / 365;
const gridconf = {
  MARGIN_TOP: 30,
  MARGIN_RIGHT: 5,
  GAP_X: 4,
  GAP_Y: 4,
  WIDTH: GM_getValue("config.webcamWidth", 365),
  HEIGHT: (318 * GM_getValue("config.webcamWidth", 365)) / 365,
};
const gridPlace = (c, r, offset = 0) => ({
  ...base,
  offsetX: -(gridconf.MARGIN_RIGHT + c * (gridconf.WIDTH + gridconf.GAP_X)),
  offsetY:
    gridconf.MARGIN_TOP + r * (gridconf.HEIGHT + gridconf.GAP_Y) + offset,
});
const organizePanels_offset = 65 - gridconf.MARGIN_TOP;
const positions3x3plus1 = () => [
  gridPlace(0, 0),
  gridPlace(0, 1),
  gridPlace(1, 0),
  gridPlace(1, 1),
  gridPlace(2, 0),
  gridPlace(2, 1),
  gridPlace(0, 2),
  gridPlace(1, 2),
  gridPlace(2, 2),
  gridPlace(3, 0, organizePanels_offset),
  gridPlace(3, 1, organizePanels_offset),
  gridPlace(3, 2, organizePanels_offset),
  gridPlace(4, 0, organizePanels_offset),
  gridPlace(4, 1, organizePanels_offset),
  gridPlace(4, 2, organizePanels_offset),
];
function organizePanels(getPositions = positions3x3plus1) {
  const opened = queryPanels();
  if (!opened.length) return;
  let lastIndex = 0;
  const groups = { prePositioned: [], newlyCreated: [] };
  for (const panel of opened) {
    const gridIndex = panel.dataset.gridIndex;
    if (gridIndex) {
      groups.prePositioned.push(panel);
      const gridIndexNumber = +gridIndex;
      if (gridIndexNumber > lastIndex) lastIndex = gridIndexNumber;
    } else groups.newlyCreated.push(panel);
  }
  const grid = Array(
    Math.max(+chatHTML5.roles.user.webcamMax, opened.length, lastIndex),
  ).fill(null);
  for (const panel of groups.prePositioned)
    if (null != panel.dataset.gridIndex && "" !== panel.dataset.gridIndex)
      if (grid[+panel.dataset.gridIndex]) {
        panel.dataset.gridIndex = "";
        groups.newlyCreated.unshift(panel);
      } else grid[+panel.dataset.gridIndex] = panel;
  for (let i = grid.length - 1; i >= 9; i--) {
    if (!grid[i]) continue;
    const nextAvailableSlot = grid.indexOf(null);
    if (!(nextAvailableSlot < 0) && !(nextAvailableSlot >= i)) {
      grid[i].dataset.gridIndex = nextAvailableSlot.toString();
      grid[nextAvailableSlot] = grid[i];
      grid[i] = null;
    }
  }
  for (const panel of groups.newlyCreated) {
    const nextAvailableSlot = grid.indexOf(null);
    if (nextAvailableSlot < 0) {
      panel.dataset.gridIndex = grid.length.toString();
      grid.push(panel);
    } else {
      panel.dataset.gridIndex = nextAvailableSlot.toString();
      grid[nextAvailableSlot] = panel;
    }
  }
  const positions = getPositions();
  let idx = -1;
  for (const place of grid) {
    idx++;
    const position = positions[idx];
    if (!place || !position) continue;
    const panel = jsPanel.activePanels.getPanel(place.id);
    if (panel)
      panel
        .resize({ width: gridconf.WIDTH, height: gridconf.HEIGHT })
        .reposition(position);
  }
}

;// file://./src/betterbw/utils/scrappers.ts
function scrappers_getUsername(panel) {
  const id = panel.id.split("_")[2] || $("[data-id]", panel)[0]?.dataset.id;
  return id && getUserById(id)?.username;
}
function getUserObjectById(userId) {
  return chatHTML5.users[userId];
}
function getUserById(userId) {
  const user = getUserObjectById(userId);
  if (!user) return;
  return { obj: user, username: user.username.split("_")[0] };
}

;// file://./src/betterbw/utils/openPanel.ts

const PANEL_SELECTOR = ".jsPanel.jsPanel-theme-default";
const queryPanels = () => document.querySelectorAll(PANEL_SELECTOR);
const queryPanel = target => target.querySelector(PANEL_SELECTOR);
function tryToOpenPanel(candidate) {
  $(".webcamBtn", candidate.item).trigger("click");
}
const getCandidates = (compareFn = topRandom, _biases = {}) => {
  const biases = { "-": 1, undefined: 2, "+": 3, "++": 4, ..._biases };
  const userItems = document.querySelectorAll(
    '#userList [data-status="online"][data-webcam="true"]:not(:has(:is(.fa.fa-lock, .fa.fa-eye-slash)))',
  );
  const entries = [];
  for (const item of userItems.values()) {
    const id = item.dataset.username?.split("_")[0];
    if (!id) continue;
    const status = GM_getValue(`${id}_status`);
    if ("--" !== status) {
      if (!isOnCooldown(id))
        entries.push({
          item,
          id,
          status,
          bias: biases[status],
          onlineSince: parseInt(
            item.querySelector(".userLabel [data-date]")?.dataset.date || "0",
          ),
        });
    }
  }
  entries.sort(compareFn);
  return entries;
};
function openCandidates(candidates) {
  const opened = document.querySelectorAll(PANEL_SELECTOR);
  let openedLength = opened.length || 0;
  const maxToOpen = +chatHTML5.roles.user.webcamMax;
  if (openedLength >= maxToOpen) {
    organizePanels();
    return console.log(`Max number of panels (${maxToOpen}) already open`);
  }
  const openedIds = new Set(
    Array.from(opened).map(panel => scrappers_getUsername(panel)),
  );
  while (openedLength < maxToOpen && candidates.length > 0) {
    const candidate = candidates.shift();
    if (openedIds.has(candidate.id)) continue;
    tryToOpenPanel(candidate);
    openedLength++;
  }
  organizePanels();
}

;// file://./src/betterbw/utils/waitToBe.ts
function waitToBe(
  selector,
  attributeFilter = ["aria-hidden"],
  predicate = el => "false" !== el.getAttribute("aria-hidden"),
) {
  return new Promise(resolve => {
    let attrObserver = null;
    let domObserver = null;
    function cleanup() {
      if (attrObserver) {
        attrObserver.disconnect();
        attrObserver = null;
      }
      if (domObserver) {
        domObserver.disconnect();
        domObserver = null;
      }
    }
    function attachAttrObserver(el) {
      if (!el) return false;
      if (predicate(el)) {
        cleanup();
        resolve(el);
        return true;
      }
      (attrObserver = new MutationObserver(muts => {
        for (const m of muts)
          if (
            "attributes" === m.type
            && m.attributeName
            && attributeFilter.includes(m.attributeName)
          ) {
            if (predicate(el)) {
              cleanup();
              resolve(el);
              return;
            }
          }
      })).observe(el, { attributes: true, attributeFilter });
      return false;
    }
    const existing = document.querySelector(selector);
    if (attachAttrObserver(existing)) return;
    (domObserver = new MutationObserver(muts => {
      for (const m of muts)
        for (const node of m.addedNodes) {
          if (!(node instanceof HTMLElement)) continue;
          const found = node.matches(selector)
            ? node
            : node.querySelector(selector);
          if (found) {
            if (attachAttrObserver(found)) {
              if (domObserver) {
                domObserver.disconnect();
                domObserver = null;
              }
              return;
            }
          }
        }
    })).observe(document.body, { childList: true, subtree: true });
  });
}

;// CONCATENATED MODULE: external "jsxRuntime"
const external_jsxRuntime_namespaceObject = window.jsxRuntime;
;// CONCATENATED MODULE: external "preact"
const external_preact_namespaceObject = window.preact;
;// file://./src/betterbw/utils/formatters.ts
const dataUsername = id =>
  `[data-username="${id}"],[data-username^="${id}_"]`;

// EXTERNAL MODULE: ./src/betterbw/styles/tiers.css?inline
var tiersinline = __webpack_require__("./src/betterbw/styles/tiers.css?inline");
;// file://./src/betterbw/dynamicStyle.ts

const dataUserItems = group => group.map(dataUsername).join(",");
async function getCSS() {
  const values = GM_getValues(
    GM_listValues().filter(key => key.match(/\.*?_status/)),
  );
  const groups = { "--": [], "-": [], "+": [], "++": [] };
  for (const [key, value] of Object.entries(values)) {
    const id = key.split("_")[0];
    groups[value].push(id);
  }
  const selectors = {
    group_minus_minus: dataUserItems(groups["--"]),
    group_minus: dataUserItems(groups["-"]),
    group_plus: dataUserItems(groups["+"]),
    group_plus_plus: dataUserItems(groups["++"]),
  };
  return tiersinline/* ["default"].replace */.A.replace(
    /\.(group_\w+)/gm,
    (_match, key, openStyle) => selectors[key] + openStyle,
  );
}
const dynamicStyle = GM_addStyle("");
const refreshDynamicStyle = async () =>
  requestAnimationFrame(async () => (dynamicStyle.innerHTML = await getCSS()));
refreshDynamicStyle();

;// file://./src/betterbw/features/rotateCam.tsx
function getRotation(element) {
  if (!element || !element.dataset.rotation) return 0;
  return parseInt(element.dataset.rotation, 10) || 0;
}
function rotateCam({ id, panel }) {
  const newRotation = (getRotation(panel) + 90) % 360;
  panel.dataset.rotation = newRotation;
  GM_setValue(`${id}_rotation`, newRotation);
}

;// file://./src/betterbw/features/panelActions.tsx

const BtnClassify = ({ getUsername, panel, value, title, onClick }) =>
  (0,external_jsxRuntime_namespaceObject.jsx)("button", {
    "data-status-value": value,
    title: title,
    className: "panel-action-btn",
    onClick: e => {
      const username = getUsername();
      if (username) {
        GM_setValue(`${username}_status`, value);
        refreshDynamicStyle();
      }
      if (panel) panel.dataset.status = value;
      onClick?.(e);
    },
    children: value,
  });
const BtnMinus2 = ({ getUsername, panel }) =>
  (0,external_jsxRuntime_namespaceObject.jsx)(BtnClassify, {
    getUsername: getUsername,
    panel: panel,
    value: "--",
    title: "Nope\nDo not suggest this person.",
    onClick: () => {
      if (panel) jsPanel.activePanels.getPanel(panel.id)?.close();
    },
  });
const BtnMinus1 = props =>
  (0,external_jsxRuntime_namespaceObject.jsx)(BtnClassify, {
    value: "-",
    title: "So so\nIt depends on the day, on the mood...",
    ...props,
  });
const BtnPlus1 = props =>
  (0,external_jsxRuntime_namespaceObject.jsx)(BtnClassify, {
    value: "+",
    title: "Yeah\nI liked you, buddy",
    ...props,
  });
const BtnPlus2 = props =>
  (0,external_jsxRuntime_namespaceObject.jsx)(BtnClassify, {
    value: "++",
    title: "Ohhh Yeah!\nI liked you a lot, buddy!",
    ...props,
  });
const BtnRotate = ({ panel, getUsername }) =>
  (0,external_jsxRuntime_namespaceObject.jsx)("button", {
    title: "Rotate",
    className: "panel-action-btn",
    onClick: () => {
      if (panel) rotateCam({ id: getUsername(), panel });
    },
    children: "⟳",
  });
const BtnCooldown = ({ panel, getUsername }) =>
  (0,external_jsxRuntime_namespaceObject.jsx)("button", {
    title: "Cooldown 15m\nDo not suggest this person for the next 15 minutes",
    className: "panel-action-btn",
    onClick: () => cooldownIt({ username: getUsername(), panel }),
    children: "⏱",
  });
const PanelActions = ({ panel, username }) => {
  const props = { panel, getUsername: () => username };
  return (0,external_jsxRuntime_namespaceObject.jsxs)(external_jsxRuntime_namespaceObject.Fragment, {
    children: [
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnCooldown, { ...props }),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnRotate, { ...props }),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnPlus2, { ...props }),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnPlus1, { ...props }),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnMinus1, { ...props }),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnMinus2, { ...props }),
    ],
  });
};
const MenuActions = () => {
  const props = { getUsername: getLatestUser };
  return (0,external_jsxRuntime_namespaceObject.jsxs)("div", {
    children: [
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnCooldown, { ...props }),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnPlus2, { ...props }),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnPlus1, { ...props }),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnMinus1, { ...props }),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnMinus2, { ...props }),
    ],
  });
};
function getLatestUser() {
  try {
    return chatHTML5.myUser.id.split("_")[0];
  } catch (e) {
    console.error("Oooops...");
    const muteItem = $('#userMenu [data-action="mute"]')[0];
    return muteItem?.textContent.split(" ").at(-1)?.split("_").at(0);
  }
}
function resizePanel(panelJS) {
  panelJS.resize({ width: gridconf.WIDTH, height: gridconf.HEIGHT });
}
function monitorVideoReadiness(video, panel, panelJS) {
  let timeoutId;
  const cleanup = () => {
    clearTimeout(timeoutId);
    video.removeEventListener("loadeddata", onVideoReady);
  };
  const onVideoReady = () => {
    cleanup();
  };
  const originalClose = panelJS.close.bind(panelJS);
  panelJS.close = function () {
    cleanup();
    return originalClose();
  };
  timeoutId = setTimeout(() => {
    cooldownIt({ username: scrappers_getUsername(panel), minutes: 5 });
    panelJS.close();
  }, 25000);
  video.addEventListener("loadeddata", onVideoReady);
}
function attachPanelActions(panel) {
  if ("1" === panel.dataset.actionsAttached) return;
  const panelJS = jsPanel.activePanels.getPanel(panel.id);
  if (!panelJS) return console.warn("Panel not found for", panel.id);
  resizePanel(panelJS);
  const header = $(".jsPanel-hdr .jsPanel-title", panel)[0];
  if (!header) return;
  const username = scrappers_getUsername(panel);
  if (!username) return;
  panel.dataset.username = username;
  panel.dataset.status = GM_getValue(`${username}_status`);
  const actions = document.createElement("div");
  actions.className = "panel-action";
  header.appendChild(actions);
  (0,external_preact_namespaceObject.render)((0,external_jsxRuntime_namespaceObject.jsx)(PanelActions, { username: username, panel: panel }), actions);
  panel.dataset.actionsAttached = "1";
  panel.dataset.rotation = GM_getValue(`${username}_rotation`);
  $(".jsPanel-btn.jsPanel-btn-close", panel).on("click", () => {
    cooldownIt({ username: username, minutes: 1 });
  });
  $(".userAvatar", panel).on("click", event => {
    event.stopPropagation();
    if (!("pageX" in event && "pageY" in event)) return;
    if ($("#userMenu").is(":visible")) return void $("#userMenu").hide();
    $(
      `#userList .userItem[data-id=${JSON.stringify(panel.id.split("_")[2])}]`,
    ).trigger(
      new jQuery.Event("click", { pageX: event.pageX + 3, pageY: event.pageY }),
    );
  });
  const video = panel.querySelector("video");
  if (video) {
    video.volume = 0.08;
    monitorVideoReadiness(video, panel, panelJS);
  }
}
function cleanupPanel(panel) {}

;// file://./src/betterbw/utils/spyOn.ts
function spyOn(obj, watchers) {
  return Proxy.revocable(obj, {
    set(target, prop, value) {
      if (prop in watchers && target[prop] !== value) watchers[prop](value);
      target[prop] = value;
      return true;
    },
  });
}

// EXTERNAL MODULE: ./src/betterbw/styles/openPanels.css?inline
var openPanelsinline = __webpack_require__("./src/betterbw/styles/openPanels.css?inline");
;// file://./src/betterbw/dynamicOpenedStyle.ts

const dynamicOpenedStyle = GM_addStyle("");
function updateCssForOpenedPanels() {
  const opened = queryPanels();
  if (!opened?.length) return;
  const usernames = Array.from(opened)
    .map(panel => scrappers_getUsername(panel))
    .filter(v => null != v);
  const selectorsIamWatching = usernames.map(dataUsername).join(",");
  const selectorsWatchingMe = usernames
    .map(
      id =>
        `body:has(#userList .userItem:where(${dataUsername(id)}) .eye-icon .isWatching) .jsPanel[data-username="${id}"]`,
    )
    .join(", ");
  dynamicOpenedStyle.innerHTML = openPanelsinline/* ["default"].replace */.A.replace(/\[data-username="I_am_watching"\]/g, selectorsIamWatching)
    .replace(/\.user_watching_me/g, selectorsWatchingMe);
}

;// file://./src/betterbw/features/tabFocus.ts
const tabFocused = () => "visible" === document.visibilityState;
const whenTabFocused = callback => {
  document.addEventListener(
    "visibilitychange",
    () => {
      "visible" === document.visibilityState && callback();
    },
    { once: true, passive: true },
  );
};

;// file://./src/betterbw/utils/observeIt.ts
function iterate(nodes, selector, fn) {
  let found = false;
  for (const node of nodes) {
    if (!(node instanceof HTMLElement)) continue;
    const panels = node.matches(selector)
      ? [node]
      : node.querySelectorAll(selector);
    if (panels.length) {
      if (fn) panels.forEach(fn);
      found = true;
    }
  }
  return found;
}
function observeIt({
  target,
  selector,
  forEachAddedNode,
  forEachRemovedNode,
  cleanup,
}) {
  const observerPanels = new MutationObserver(mutations => {
    let nodesAdded = false;
    let nodesRemoved = false;
    for (const mutation of mutations) {
      if (iterate(mutation.addedNodes, selector, forEachAddedNode))
        nodesAdded = true;
      if (iterate(mutation.removedNodes, selector, forEachRemovedNode))
        nodesRemoved = true;
    }
    cleanup?.({ nodesAdded, nodesRemoved });
  });
  observerPanels.observe(target, { childList: true, subtree: false });
  return {
    teardown: () => {
      observerPanels.disconnect();
    },
  };
}

;// CONCATENATED MODULE: external "preactHooks"
const external_preactHooks_namespaceObject = window.preactHooks;
;// file://./src/betterbw/algo.ts
let algo_algo = "";
const getAlgo = () => algo_algo;
const setAlgo = val => {
  document.body.dataset.algo = algo_algo = val;
};

;// file://./src/betterbw/utils/debounce.ts
function debounce(func, wait) {
  let timeout;
  return function (...args) {
    const context = this;
    clearTimeout(timeout);
    timeout = setTimeout(() => func.apply(context, args), wait);
  };
}

;// file://./src/betterbw/features/globalActions.tsx

const btnDisableClick = () => {
  organizePanels();
  setAlgo("");
};
const BtnDisable = () =>
  (0,external_jsxRuntime_namespaceObject.jsx)("button", {
    "data-sort-order": "",
    title: "Organize open panels and stop the algorithm",
    onClick: btnDisableClick,
    children: "\xf8",
  });
const btnNewClick = () => {
  setAlgo("new");
  openCandidates(getCandidates(topRandom, { undefined: 9, "+": 8 }));
};
const BtnNew = () =>
  (0,external_jsxRuntime_namespaceObject.jsx)("button", {
    "data-sort-order": "new",
    title: "Prioritize users you haven't liked/disliked before",
    onClick: btnNewClick,
    children: "New",
  });
const btnTopClick = () => {
  setAlgo("top");
  openCandidates(getCandidates(topRandom));
};
const BtnTop = () =>
  (0,external_jsxRuntime_namespaceObject.jsx)("button", {
    "data-sort-order": "top",
    title: "Prioritize users you liked more",
    onClick: btnTopClick,
    children: "Top",
  });
const clickOnCurrentAlgoButton = debounce(() => {
  const algo = getAlgo();
  if ("new" === algo) btnNewClick();
  else if ("top" === algo) btnTopClick();
  else if ("" === algo) organizePanels();
}, 200);
const NumberOfCams = () => {
  const [value, setValue] = (0,external_preactHooks_namespaceObject.useState)(() => GM_getValue("user.webcamMax", 10));
  (0,external_preactHooks_namespaceObject.useEffect)(() => {
    const maxWebcamreached = chatHTML5.maxWebcamreached;
    chatHTML5.maxWebcamreached = function () {
      if (!maxWebcamreached()) return false;
      chatHTML5.roles.user.webcamMax = +chatHTML5.roles.user.webcamMax + 1;
      return maxWebcamreached();
    };
    const { proxy, revoke } = spyOn(chatHTML5.roles.user, {
      webcamMax: n => {
        setValue(n);
        GM_setValue("user.webcamMax", n);
      },
    });
    chatHTML5.roles.user = proxy;
    return () => {
      chatHTML5.maxWebcamreached = maxWebcamreached;
      revoke();
    };
  }, []);
  (0,external_preactHooks_namespaceObject.useEffect)(() => {
    clickOnCurrentAlgoButton();
  }, [value]);
  return (0,external_jsxRuntime_namespaceObject.jsxs)("label", {
    children: [
      (0,external_jsxRuntime_namespaceObject.jsx)("input", {
        title: "# of open cams",
        type: "number",
        className: "numberOfCams",
        value: value,
        onChange: e => {
          chatHTML5.roles.user.webcamMax = +e.currentTarget.value;
        },
      }),
      " cams",
    ],
  });
};
const globalActions_onChangeEffect = value => {
  gridconf.WIDTH = +value;
  gridconf.HEIGHT = value * panelRatio;
  clickOnCurrentAlgoButton();
};
const PanelSizeInput = () =>
  (0,external_jsxRuntime_namespaceObject.jsx)(MemoInput, {
    storageKey: "config.webcamWidth",
    title: "Size of the panel",
    type: "number",
    className: "numberOfCams",
    suffix: "px",
    min: "150",
    step: "5",
    defaultValue: gridconf.WIDTH,
    onChangeEffect: globalActions_onChangeEffect,
  });
const MemoInput = ({
  storageKey,
  prefix,
  suffix,
  defaultValue,
  onChangeEffect,
  ...props
}) => {
  const [value, setValue] = (0,external_preactHooks_namespaceObject.useState)(() =>
    GM_getValue(storageKey, defaultValue),
  );
  (0,external_preactHooks_namespaceObject.useEffect)(() => {
    onChangeEffect(value);
  }, [value]);
  return (0,external_jsxRuntime_namespaceObject.jsxs)("label", {
    children: [
      prefix,
      (0,external_jsxRuntime_namespaceObject.jsx)("input", {
        ...props,
        value: value,
        onChange: e => {
          const value = e.currentTarget.value;
          setValue(value);
          GM_setValue(storageKey, value);
        },
      }),
      suffix,
    ],
  });
};
const AlgoControls = () =>
  (0,external_jsxRuntime_namespaceObject.jsxs)(external_jsxRuntime_namespaceObject.Fragment, {
    children: [
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnTop, {}),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnNew, {}),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnDisable, {}),
      (0,external_jsxRuntime_namespaceObject.jsx)(NumberOfCams, {}),
      " @ ",
      (0,external_jsxRuntime_namespaceObject.jsx)(PanelSizeInput, {}),
    ],
  });

;// file://./src/betterbw/features/observeOpenPanels.ts

let observeOpenPanels_nextUser = null;
const setNextCam = val => (observeOpenPanels_nextUser = val);
let clickRequested = false;
function observePanels() {
  return observeIt({
    target: document.body,
    selector: PANEL_SELECTOR,
    forEachAddedNode: attachPanelActions,
    forEachRemovedNode: cleanupPanel,
    cleanup: ({ nodesAdded, nodesRemoved }) => {
      if (!nodesAdded && !nodesRemoved) return;
      organizePanels();
      if (nodesRemoved) {
        if (observeOpenPanels_nextUser) {
          $(".webcamBtn.visible", observeOpenPanels_nextUser).trigger("click");
          observeOpenPanels_nextUser = null;
        } else if (tabFocused()) clickOnCurrentAlgoButton();
        else if (!clickRequested) {
          clickRequested = true;
          whenTabFocused(() => {
            clickRequested = false;
            clickOnCurrentAlgoButton();
          });
        }
      }
      updateCssForOpenedPanels();
    },
  });
}

;// file://./src/betterbw/features/spyOnConfig.ts

function spyOnConfig(watchers = {}) {
  const { proxy, revoke } = spyOn(chatHTML5.config, watchers);
  chatHTML5.config = proxy;
  return { proxy, revoke, watchers };
}

;// file://./src/betterbw/setupTools.tsx

function setupHeader() {
  const header = $("#header .header-custom-btns")[0];
  if (!header) return;
  const controls = document.createElement("div");
  controls.id = "bbw_controls";
  header.prepend(controls);
  (0,external_preact_namespaceObject.render)((0,external_jsxRuntime_namespaceObject.jsx)(AlgoControls, {}), controls);
}
function setupUserMenu() {
  const userMenu = $("#userMenu")[0];
  if (!userMenu) return;
  const controls = document.createElement("div");
  controls.id = "bbw_controls";
  userMenu.appendChild(controls);
  (0,external_preact_namespaceObject.render)((0,external_jsxRuntime_namespaceObject.jsx)(MenuActions, {}), controls);
  chatHTML5.myUser = spyOn(chatHTML5.myUser, {
    selectedUserid: selectedUserid => {
      const nuser = getUserById(selectedUserid);
      if (!nuser) return;
      userMenu.dataset.username = nuser.username;
      userMenu.dataset.status = GM_getValue(`${nuser.username}_status`);
      userMenu.dataset.isCooldown = Boolean(
        isOnCooldown(nuser.username),
      ).toString();
      userMenu.dataset.privateCam = Boolean(!nuser.obj.webcamPublic).toString();
    },
  }).proxy;
}
function setupSidebar() {
  const userList = document.getElementById("userList");
  if (!userList) return;
  $(userList).on("click", ".webcamBtn", function (event) {
    const opened = queryPanels();
    const webcamMax = +chatHTML5.roles.user.webcamMax;
    if (opened.length < webcamMax) return;
    const nextUser = $(this).closest(".userItem")[0];
    if (nextUser) {
      event.stopPropagation();
      setNextCam(nextUser);
      const panel = $(`[data-grid-index="${webcamMax - 1}"]`)[0];
      if (panel) jsPanel.activePanels.getPanel(panel.id)?.close();
    }
  });
  $(userList).on("click", ".fa-eye.fa-2x", function (e) {
    if (e.shiftKey) return;
    e.preventDefault();
    e.stopPropagation();
    e.stopImmediatePropagation();
  });
}
const ConfigWatchers = {};
function setupTools() {
  chatHTML5.config.timeBeforeWatchingCamAgain = "1000";
  chatHTML5.config.checkOwnStream = "1";
  chatHTML5.config.showCountryFlag = "1";
  chatHTML5.roles.user.webcamMax = GM_getValue("user.webcamMax", 10);
  const { revoke } = spyOnConfig(ConfigWatchers);
  chatHTML5.amIMuted = () => chatHTML5.myUser.mutedUntil > Date.now();
  setupHeader();
  setupUserMenu();
  setupSidebar();
  $("#sortWebcamtBtn").trigger("click");
  return revoke;
}

;// file://./src/betterbw/features/observeChat.tsx

function handleWebcamOpened(message) {
  const span = $("[data-id]", message)[0];
  if (!span) return;
  const user = getUserById(span.dataset.id);
  if (!user) return;
  message.dataset.username = user.username;
  const privateCam = Boolean(!user.obj.webcamPublic);
  message.dataset.privateCam = privateCam.toString();
  span.innerHTML = span.innerHTML.replace(
    /(User )(.*?)( has opened his webcam)/gi,
    `$1<span class="userLabelBBW" data-username=${JSON.stringify(user.username)}>$2</span>$3`,
  );
  if (!privateCam) clickOnCurrentAlgoButton();
}
function handleRegularMessage(message) {
  const msgHeader = $("[data-ip]", message)[0];
  if (!msgHeader) return;
  msgHeader.classList.add("userLabelBBW", "sender");
}
function handleWhisperOrPrivateMessage(message) {
  const msgHeader = $("[data-ip]", message)[0];
  if (!msgHeader || !msgHeader.childNodes.length) return;
  {
    const firstChild = msgHeader.childNodes[0];
    const span = document.createElement("span");
    span.dataset.username = msgHeader.dataset.username;
    span.classList.add("userLabelBBW", "sender");
    msgHeader.insertBefore(span, firstChild);
    span.appendChild(firstChild);
  }
  {
    const thirdChild = msgHeader.childNodes[2];
    const span = document.createElement("span");
    span.dataset.username = thirdChild.textContent?.trim();
    span.classList.add("userLabelBBW", "receiver");
    msgHeader.insertBefore(span, thirdChild);
    span.appendChild(thirdChild);
  }
}
function handlePrivateRequested(message) {
  const textNode = Array.from(message.childNodes).find(
    n => 3 === n.nodeType && n.textContent?.trim(),
  );
  if (!textNode || !textNode.textContent) return;
  const textContent = textNode.textContent;
  const match = textContent.match(
    /\s*(\S+?)\s+has invited you to watch his cam/,
  );
  if (!match) return;
  const username = match[1];
  if (!username) return;
  const span = document.createElement("span");
  span.className = "userLabelBBW";
  span.dataset.username = username;
  span.textContent = username;
  message.insertBefore(span, textNode);
  textNode.textContent = textContent.replace(/(^\s*\S+?)(\s+.*$)/, "$2");
}
function handleWebcamRequest(message) {}
function handleChatMessage(message) {
  if (message.matches(".serverMessage.webcamOpened"))
    handleWebcamOpened(message);
  else if (message.matches(".message.msg-box:not(.whisper)"))
    handleRegularMessage(message);
  else if (
    message.matches(".message.addPrivateMessage,.message.msg-box.whisper")
  )
    handleWhisperOrPrivateMessage(message);
  else if (message.matches(".serverMessage.privateRequested"))
    handlePrivateRequested(message);
  else if (message.matches(".serverMessage.webcamRequest"))
    handleWebcamRequest(message);
}
function observeChat(room) {
  return observeIt({
    target: room,
    selector: ".message,.serverMessage",
    forEachAddedNode: handleChatMessage,
    forEachRemovedNode: message => {
      console.log("Chat message removed in", room.id, message);
    },
  });
}
function observeChatNav() {
  const tabs = {};
  return observeIt({
    target: $("#tabs .tab-content")[0],
    selector: ".tab-pane",
    forEachAddedNode: room => {
      tabs[room.id] = observeChat(room);
    },
    forEachRemovedNode: room => {
      if (room.id in tabs) {
        tabs[room.id]?.teardown?.();
        delete tabs[room.id];
      }
    },
  });
}

;// file://./src/betterbw/betterbw.user.ts

async function main() {
  GM_addStyle(staticinline/* ["default"] */.A);
  observeChatNav();
  observePanels();
  await waitToBe(
    "#roomsModal",
    ["aria-hidden"],
    el => "false" === el.getAttribute("aria-hidden"),
  );
  await waitToBe(
    "#roomsModal",
    ["aria-hidden"],
    el => "false" !== el.getAttribute("aria-hidden"),
  );
  setupTools();
  openCandidates(getCandidates(topRandom));
}

;// file://./src/betterbw/index.ts

chatHTML5.myUser.mutedUsers = chatHTML5.myUser.mutedUsers || "";
main();