Anki Forums

Linking to external javascript

Has anybody had any luck linking to external javascript in Anki 2.1.29? I know it’s not officially supported but I saw some old posts on the web describing how to do it. But it hasn’t worked for me.

I previously posted on a thread on r/Anki how to load external libraries dynamically with a loader using Promise API. This method still works for me on Anki 2.1.29/30beta. For example, in my card template, I load clipboard.js, Marked.js, and Prism with the loader, and then syntax-highlight code block in markdown-formatted string. See this screenshot:

4 Likes

Thanks. That looks promising. I’m trying to implement something based on this example.
But my JavaScript skills are minimal.

I either get an error from Anki saying Invalid HTML on card: ReferenceError: keyman is not defined or it just doesn’t work. Do you see any obvious errors below?

Here’s the code I’m trying:

{{type:Back}}

<script>
function injectScript(src) {
return new Promise((resolve, reject) => {
    const script = document.createElement('script');
    script.src = src;
    script.addEventListener('load', resolve);
    script.addEventListener('error', e => reject(e.error));
    document.head.appendChild(script);
});
}

injectScript('https://s.keyman.com/kmw/engine/13.0.108/keymanweb.js');
injectScript('https://s.keyman.com/kmw/engine/13.0.108/kmwuitoggle.js');

  (function(kmw) {
      kmw.init({attachType:'auto'});
      kmw.addKeyboards('@eng'); // Loads default English keyboard from Keyman Cloud (CDN)
      kmw.addKeyboards('@tha'); // Loads default Thai keyboard from Keyman Cloud (CDN)
  })(keyman);
  </script>
1 Like

I looked a bit into keyman. Seems like some work is needed to get it working on Anki’s card template. Try the following:

  1. Install Cookie monster add-on. You cannot use cookies on vanilla Anki, but keyman requires cookies.
  2. git clone from https://github.com/keymanapp/keyman , or download repository zip file from there.
  3. Place osk and ui folder, which exist in web/source/resources folder of the downloaded repository, into your profile’s collection.media folder, as follows:
    collection.media\osk\
    collection.media\ui\
    
  4. Add the following code at the end of Front and/or Back template:
    <script>
        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 keyman === 'undefined') {
                await injectScript('https://s.keyman.com/kmw/engine/13.0.108/keymanweb.js');
                await injectScript('https://s.keyman.com/kmw/engine/13.0.108/kmwuitoggle.js');
            }
            keyman.init({ attachType: 'auto' });
            keyman.addKeyboards('@en'); // Loads default English keyboard from Keyman Cloud (CDN)
            keyman.addKeyboards('@th'); // Loads default Thai keyboard from Keyman Cloud (CDN)
        })();
    </script>
    

Screenshot:
keyman

This injectScript() function returns a Promise object, so async/await or then() is needed to work properly. See the following links for more details:

It might be better for performance if you do the followings:

  • Download keymanweb.js and kmwuitoggle.js.
  • Move them to collection.media, then add an underscore at the beginning of each file name in order to prevent Anki from deleting them as unused media when checking media files.
  • Modify the script as follows:
    await injectScript('_keymanweb.js');
    await injectScript('_kmwuitoggle.js');
    

Note that it does not work in the preview area on the right side of the card layout dialog. It may be very difficult (or impossible) to get keyman working on AnkiDroid. Sorry if I’m not clear, English is not my first language.

5 Likes

Thank you! It works. I really appreciate the time you took to help me solve this problem.

1 Like