Download Game! Currently 82 players and visitors. Last logged in:SolvikGlaurungDescadGileon

Blitzer's Blog >> 72124

Back to blogs index
Posted: 06 Sep 2026 03:58 [ permalink ]
class VirtualDisplayManager {
  constructor() {
    this.displays = new Map();
    this.activeDisplayId = 'default';
    
    // Properties for the peek functionality
    this._peekTimeout = null;
    this._prePeekId = null;

    this._initDOM();
  }

  _initDOM() {
    const styleId = 'v-display-manager-styles';
    if (!document.getElementById(styleId)) {
      const style = document.createElement('style');
      style.id = styleId;
      style.textContent = `
        #v-display-root { position: fixed; top: 0; left: 0; width: 100vw;
height: 100vh; z-index: 999999; background: #ffffff; display: none; }
        #v-display-root.is-active { display: block; }
        .v-display-view { position: absolute; top: 0; left: 0; width: 100%;
height: 100%; display: none; overflow-y: auto; background: #ffffff; }
        .v-display-view.is-active { display: block; }
        .v-display-iframe { width: 100%; height: 100%; border: none; display:
block; }
      `;
      document.head.appendChild(style);
    }
    if (!document.getElementById('v-display-root')) {
      this.rootContainer = document.createElement('div');
      this.rootContainer.id = 'v-display-root';
      document.body.appendChild(this.rootContainer);
    } else {
      this.rootContainer = document.getElementById('v-display-root');
    }
  }

  add(id, content) {
    if (id === 'default') return;
    if (this.displays.has(id)) this.remove(id);
    const viewWrapper = document.createElement('div');
    viewWrapper.className = 'v-display-view';
    if (typeof content === 'string') viewWrapper.innerHTML = content;
    else if (content instanceof HTMLElement) viewWrapper.appendChild(content);
    this.rootContainer.appendChild(viewWrapper);
    this.displays.set(id, viewWrapper);
  }

  remove(id) {
    if (!this.displays.has(id)) return;
    const viewWrapper = this.displays.get(id);
    if (viewWrapper && viewWrapper.parentNode) viewWrapper.parentNode.removeChi
ld(viewWrapper);
    this.displays.delete(id);
    if (this.activeDisplayId === id) this.switchTo('default');
  }

  switchTo(id) {
    if (id !== 'default' && !this.displays.has(id)) {
      console.error(`Switch failed: Display "${id}" not found.`);
      return;
    }
    
    // If the user manually switches displays, cancel any active peek
countdown
    if (this._peekTimeout) {
      clearTimeout(this._peekTimeout);
      this._peekTimeout = null;
      this._prePeekId = null;
    }

    this.activeDisplayId = id;
    if (id === 'default') {
      this.rootContainer.classList.remove('is-active');
      this.displays.forEach(view => view.classList.remove('is-active'));
    } else {
      this.rootContainer.classList.add('is-active');
      this.displays.forEach((view, key) => view.classList.toggle('is-active',
key === id));
    }
  }

  list() { return ['default', ...Array.from(this.displays.keys())]; }
  getCurrent() { return this.activeDisplayId; }

  // =========================================
  // NEW HELPER ROUTINES
  // =========================================

  /**
   * Loads a URL into a virtual display in the background using an iframe.
   * If the ID already exists, it overwrites it.
   * 
   * @param {string} id - The named/numbered window ID
   * @param {string} url - The web address to load
   */
  loadUrl(id, url) {
    const iframe = document.createElement('iframe');
    iframe.src = url;
    iframe.className = 'v-display-iframe';
    
    this.add(id, iframe);
    console.info(`Loaded URL [${url}] into display "${id}" in the
background.`);
  }

  /**
   * Temporarily switches to a display for a given number of seconds,
   * then returns to whatever display was previously active.
   * 
   * @param {string} id - The display ID to peek at
   * @param {number} seconds - How long to stay before reverting
   */
  peek(id, seconds = 3) {
    if (id !== 'default' && !this.displays.has(id)) {
      console.error(`Cannot peek: Display "${id}" not found.`);
      return;
    }

    // If we are already in the middle of a peek, clear the old timer, 
    // but KEEP the original return destination.
    if (this._peekTimeout) {
      clearTimeout(this._peekTimeout);
    } else {
      // Record where we came from so we can go back
      this._prePeekId = this.getCurrent();
    }

    console.info(`Peeking at "${id}" for ${seconds} seconds...`);
    
    // Bypass the normal this.switchTo() to avoid clearing our prePeekId
    this.activeDisplayId = id;
    if (id === 'default') {
      this.rootContainer.classList.remove('is-active');
      this.displays.forEach(view => view.classList.remove('is-active'));
    } else {
      this.rootContainer.classList.add('is-active');
      this.displays.forEach((view, key) => view.classList.toggle('is-active',
key === id));
    }

    // Set the return timer
    this._peekTimeout = setTimeout(() => {
      console.info(`Peek finished. Returning to "${this._prePeekId}".`);
      this.switchTo(this._prePeekId);
    }, seconds * 1000);
  }
}