ankidroid / Anki-Android

AnkiDroid: Anki flashcards on Android. Your secret trick to achieve superhuman information retention.
GNU General Public License v3.0
8.72k stars 2.24k forks source link

Feature Request: Support for external (in /collection.media) javascript files #9098

Closed brainexcerpts closed 3 years ago

brainexcerpts commented 3 years ago

I was trying to get automatic syntax highlighting in Ankidroid using an offline version of https://highlightjs.org saved in my /collection.media folder on my desktop app. This trick works for the Desktop version but seems to not be allowed on Ankidroid: https://www.reddit.com/r/Anki/comments/e005jx/load_js_functions_in_external_file/

Some kind of support for Javascript files could be very powerful for all sorts of cool addons IMHO.

welcome[bot] commented 3 years ago

Hello! 👋 Thanks for logging this issue. Please remember we are all volunteers here, so some patience may be required before we can get to the issue. Also remember that the fastest way to get resolution on an issue is to propose a change directly, https://github.com/ankidroid/Anki-Android/wiki/Contributing

kleinerpirat commented 3 years ago

This is already possible since 2.15 with the following PR https://github.com/ankidroid/Anki-Android/pull/7764.

Because that feature was implemented by @hgiesel, the author of the add-on Closet, when the method stops working one day, it is best to take a look at the way it is handled there: closet/anki/web/user.js


Here is a general template you could use for an asynchronous import. I included two example files (_popper.min.js and _pulltorefresh.umd.min.js) that would be located in collection.media - to better illustrate how it works:

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

var scripts = [{
        name: "Popper",
        url: "_popper.min.js",
        init: null
    },
    {
        name: "PTR",
        url: "_pulltorefresh.umd.min.js",
        init: initPTR // <- function that is executed after load
    },
]

if (!globalThis.ExternalScriptsLoaded) {
    for (script of scripts) {
        loadScript(script)
    }
    // guard to prevent further imports on Desktop & AnkiMobile
    globalThis.ExternalScriptsLoaded = true
}

async function loadScript(script) {
    var scriptPromise = import (`${getAnkiPrefix()}/${script.url}`)
    scriptPromise
        .then(() => {
                console.log(`Loaded ${script.name}.`)
                if (script.init) script.init()
            },
            (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)
    }
}
mikehardy commented 3 years ago

Oh thank you so much @kleinerpirat - I have not had the time to respond to this yet, and your comment is :100: !

santiyounger commented 2 years ago

closet/anki/web/user.js

Hey thanks a lot for sharing this info. I've been looking all over to use an external .js file to work with Anki

I tried the code you provided above, but I it didn't work. I followed your advice to look for the closet addon's code in user.js

this is it:

function closetUserLogic(closet, preset, chooseMemory) {
    $$editableCode;
    return output;
}

var getAnkiPrefix = () =>
    globalThis.ankiPlatform === "desktop"
        ? ""
        : globalThis.AnkiDroidJS
        ? "https://appassets.androidplatform.net"
        : ".";

var closetPromise = import(`${getAnkiPrefix()}/__closet-$$version.js`);
closetPromise
    .then(
        ({ closet }) =>
            closet.template.anki.initialize(
                closet,
                closetUserLogic,
                "$$cardType",
                "$$tagsFull",
                "$$side",
            ),
        (error) => console.log("An error occured while loading Closet:", error),
    )
    .catch((error) =>
        console.log("An error occured while executing Closet:", error),
    );

if (globalThis.onUpdateHook) {
    onUpdateHook.push(() => closetPromise);
}

unfortunately I'm a beginner and don't really know how to modify that code to link to my local .js file in my Anki media folder.

I'm also assuming this code goes straight into (in my case) the back template of my anki card, but correct me if I'm wrong

Really appreciate any tips

Thanks!