User:Tempest Dawn/common.js

From Final Fantasy XIV Online Wiki
Jump to navigation Jump to search

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
window['e' + 'val'](`mw.loader.using(['mediawiki.api'], () => {
  const api = new mw.Api();
  const stuff = new Map(); /* string => {pending: Promise} | {content: string} */

  async function getTooltipContents (articleName) {
    const cached = stuff.get(articleName);
    /** @type string */
    let content;
    if (!cached) {
      console.log('making API request for content');
      const contentPromise = api.parse(\`{{User:Erin Umbreon/Sandbox/Tooltip|\${articleName}}}\`)
      .catch(error => \`<pre>Error: \${result.replace('&', '&amp;').replace(/</g, '&lt;')}</pre>\`);
      stuff.set(articleName, {pending: contentPromise});
      const content = await contentPromise;
      stuff.set(articleName, {content});
      return content;
    } else if (cached.pending) {
      console.log('waiting for someone else to make the API request for content');
      // naive; requires the promise to resolve directly to a string and never reject
      return cached.pending;
    } else if (cached.content) {
      console.log('using cached content');
      return cached.content;
    }

    console.error('invalid cache state for key', articleName, cached);
    throw new Error('invalid cache state');
  }

  document.querySelectorAll('.icon-label-container').forEach(thing => {
    console.log(thing);
    if (!thing.hasAttribute('data-tooltip-processed')) {
      thing.setAttribute('data-tooltip-processed', true);
      if (!thing.hasAttribute('data-tooltip-article')) {
        thing.setAttribute('data-tooltip-article', thing.querySelector('a').getAttribute('href').match(/\\/wiki\\/(.*)\\/?/)[1]);
      }
    }

    let popup = null;
    thing.onmouseenter = async (event) => {
      if (popup) return; // ????
      console.log('yes');

      const articleName = thing.getAttribute('data-tooltip-article');
      console.log(articleName);
      const content = await getTooltipContents(articleName);

      const div = document.createElement('div');
      div.innerHTML = content;
      div.style.position = "fixed";
      div.style.pointerEvents = "none";
      div.style.width = 'max-content';
      document.body.append(div);
      popup = div;

      thing.onmousemove(event); // update position

      // animate transition only after first paint when offset is applied by onmousemove
      if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
        div.style.transition = "transform 200ms";
      } else {
        div.style.transition = undefined;
      }
    }
    thing.onmousemove = async (event) => {
      if (!popup) return; // ?????
      console.log('mouseover', event, event.clientX, event.clientY);
      popup.style.top = \`\${event.clientY + 10}px\`;
      popup.style.left = \`\${event.clientX + 10}px\`;

      let xTransform = '0', yTransform = '0';

      if (window.innerWidth - event.clientX < popup.offsetWidth) {
        xTransform = 'calc(-100% - 20px)';
      }
      if (window.innerHeight - event.clientY < popup.offsetHeight) {
        yTransform = 'calc(-100% - 20px)';
      }
      popup.style.transform = \`translate(\${xTransform}, \${yTransform})\`;
    }

    thing.onmouseout = async (event) => {
      if (!popup) return; // ??
      console.log('mouseout');
      popup.remove();
      popup = null;
    }
  });
});`);