External Javascript Libraries

I tried everything. Even the solution, which “kleinerpirat” (Loading Javascript, CSS, and Font files locally - #3 by puritanicdev) already provided. But nothing works.
I can’t get my external javascript libraries to work. They are not loading properly.
On the other hand, when I try to import certain modules from a library by linking to the npm, it does work.

This does work: import {computePosition, autoPlacement, shift} from ‘https://cdn.jsdelivr.net/npm/@floating-ui/dom@1.6.5/+esm’;

That here doesn’t work: import {computePosition, autoPlacement, shift} from ‘/_esm.js’;
I have renamed it and placed it into collection.media but it just doesn’t work.

I even tried the solution from Linking to external javascript - #3 by Anki123

var injectScript = (src) => {
    return new Promise((resolve, reject) => {
        const script = document.createElement('script');
        script.src = src;
        script.async = true;
        script.onload = resolve;
        script.onerror = reject;
        document.head.appendChild(script);
    });
};

(async () => {
    if (typeof detectOverflow === 'undefined') {
        await injectScript('_floating_ui.core.js');
        await injectScript('_floating_ui.js');
        console.log("loaded");
    }
})();

But this won’t work at all. It just gives me the error “computePosition is not defined”. Could anyone help out a novice? Even just a hint would be really helpful.

The esm file you linked the file contains additional dynamic imports like this:

import {
  detectOverflow as t,
  offset as e,
  autoPlacement as n,
  shift as i,
  flip as o,
  size as r,
  hide as l,
  arrow as s,
  inline as c,
  limitShift as f,
  computePosition as u,
  rectToClientRect as h
} from "/npm/@floating-ui/core@1.6.1/+esm";
import {
  createCoords as a,
  round as d,
  max as g,
  min as p,
  floor as m
} from "/npm/@floating-ui/utils@0.2.2/+esm";

Which are not going to work in Anki indeed.

Are you trying to just use the Floating UI core functions you listed like computePosition, autoPlacement and shift? Those seem to be in floating-ui/core and floating-ui/dom just copies them while adding some additional functions.

I used this file https://cdn.jsdelivr.net/npm/@floating-ui/core@1.6.2/dist/floating-ui.core.browser.mjs which I then saved as _floating-ui-core.js. I’ve previously made a static importable version of eruda.js but I needed to edit the damned minified code to do export const eruda = ... so that it’s useable. In this case that won’t be necessary since .mjs format is already useable as-is.

You could even edit this file and remove any functions you aren’t going to need to make it smaller and have your card load faster.

I’m using a slightly modified version of kleinerpirat’s importing code which works with export calls:

<script>
  function getAnkiPrefix() {
    return globalThis.ankiPlatform === "desktop"
      ? ""
      : globalThis.AnkiDroidJS
      ? "https://appassets.androidplatform.net"
      : ".";
  }

  cardScripts = [
    {
      name: "Floating UI Core",
      url: "_floating-ui-core.js",
      init: (moduleObj) => {
        console.log(moduleObj);
        // Maybe do globalThis.FloatingUI = moduleObj; to get access to all the functions?
        // Then you can do const {computePosition, autoPlacement, shift} = globalThis.FloatingUI
      }
    }
  ];

  var recurseDictLoaded = (dict, curScript) => {
    dict[curScript.name] = false;
    if (curScript.dependent) recurseDictLoaded(dict, curScript.dependent);
    return dict;
  };

  // init first time ExternalScriptsLoaded
  if (!globalThis.ExternalScriptsLoaded) {
    globalThis.ExternalScriptsLoaded = cardScripts.reduce(
      recurseDictLoaded,
      {}
    );
  }

  var onScriptsLoadedFuncs = [];

  function onScriptsLoaded() {
    for (func of onScriptsLoadedFuncs) {
      if (typeof func === "function") func();
    }
  }

  var allScriptsLoaded = () =>
    Object.values(globalThis.ExternalScriptsLoaded).every(
      (isLoaded) => isLoaded
    );

  // guard to prevent further imports on Desktop & AnkiMobile
  if (!allScriptsLoaded()) {
    for (script of cardScripts) {
      loadScript(script);
    }
  } else {
    onScriptsLoaded();
  }

  async function loadScript(script) {
    const scriptPromise = import(`${getAnkiPrefix()}/${script.url}`);
    console.log(`Loading ${script.name}.`);
    scriptPromise
      .then(
        (moduleObj) => {
          console.log(`Loaded ${script.name}.`);
          if (script.init) script.init(moduleObj);
          // Set this script as loaded
          globalThis.ExternalScriptsLoaded[script.name] = true;

          if (script.dependent) loadScript(script.dependent);

          // If all scripts have finished loading, run final setup
          // Since loading is async, we don't when we'll get to this, thus it's checked on every
          // script load
          if (allScriptsLoaded()) {
            onScriptsLoaded();
          }
        },
        (error) =>
          console.log(`An error occured while loading ${script.name}:`, error)
      )
      .catch((error) =>
        console.log(`An error occured while executing ${script.name}:`, error)
      );

    if (globalThis.onUpdateHook) {
      onUpdateHook.push(() => scriptPromise);
    }
  }
</script>

Result of the console.log above

image

1 Like

I will look into this in the coming days. Thank you very much for the help you gave me here :slight_smile: !