matthayes / anki_cloze_anything

Add cloze deletions to any existing Anki notes without any modification to Anki
https://ankiweb.net/shared/info/330680661
Apache License 2.0
52 stars 10 forks source link

Installation via collection.media #12

Closed ghost closed 3 years ago

ghost commented 4 years ago

Problem

The current installation seems very cumbersome, having to copy-paste the script to 7 different cards.

Feature request

I suggest that the add-on adds a script: $XDG_DATA_HOME/Anki2/$profile/collection.media/_anki_cloze_anything.js that implements the script. Of course, cards still need to include the script, but it's reduced to a one-liner <script src="_anki_cloze_anything.js"></script> instead of the whole script in a card template.

The least thing that can be done is to suggest this installation method in the documents, since it's pretty easy to do it anyways.

matthayes commented 4 years ago

I agree it's a bit cumbersome, however I haven't found an alternative that I like. The last time I looked into using scripts in the media folder, it seemed that this was either finicky, required a plugin to work correctly, or compatibility across all versions of Anki (Desktop, Mobile, Android) was unclear. I figured a more advanced user could move the code to a script in the media folder if they were inclined to do so.

What I would really love is the ability to have shared fields, which for me would be the ideal solution. The idea is that you could define a field (or fields) on the note type and reference if from the card template. Then you only have to paste the script once.

ghost commented 4 years ago

Then I suggest that this method of installation be documented since it's much, much more manageable than placing the script at the end of every card.

EDIT: If it makes you feel more confident, I've tested this with stock Anki Desktop and Ankidroid. It works well, I'm pretty sure that if it doesn't work, it's considered a bug as the files under collection.media must be sync'd across all clients otherwise there would be glitchy/missing/broken cards. This is also used by syntax highlighting addon, which places the file _styles_for_syntax_highlighting.css.

Another solution i would love to see be implemented in stock Anki is a type of template where the front-back template and styling is dropped. This type of template would use Jinja2 to generate all cards from a single template (as opposed to juggling front-back templates * number of cards). This would mean that card generation becomes programmable which is exactly what many addons have been trying to solve.

rwmpelstilzchen commented 3 years ago

When installed using <script src="_anki_cloze_anything.js"></script>, a bug is introduced in the desktop version (but not on AnkiDroid): when the card is rendered on the screen, for a brief moment the cloze field shows as it is (with ((c1:foobar)) and all), revealing the answers. I guess the page is rendered asynchronically before the script gets to run. I wonder if there is a way to force the renderer to wait for the external script to load and run.

In case it is version- or OS-specific, here is some info:

matthayes commented 3 years ago

I guess the page is rendered asynchronically before the script gets to run.

The script tag would be rendered in the body of the HTML page of the card, and these external scripts are typically deferred until after page rendering, which explains what you are seeing. A simple solution to this is to apply a style display: none to the body or somewhere within the card so that the contents are initially not visible. You could then append some JavaScript to the end of the _anki_cloze_anything.js script that removes this style once cloze rendering has completed.

rwmpelstilzchen commented 3 years ago

I tried to set body {display: none} in the style section and at the end of _anki_cloze_anything.js I added document.getElementsByTagName("body")[0].style.display = "initial";, but it still shows the field as it is for a blink of an eye. Thinking that maybe the CSS section is loaded after the card, I added <style>body {display: none}</style> on the card itself, but to no avail.

matthayes commented 3 years ago

@rwmpelstilzchen that's pretty strange. Are you testing this with Anki Desktop? I would try something like below and see if it helps. Perhaps the changes made to the innerHTML of the element haven't taken effect yet by the time the body is allowed to be displayed.

setTimeout(
function() {
    document.getElementsByTagName("body")[0].style.display = "initial";
}, 0);

I'm testing out using _anki_cloze_anything.js in collections.media and will send an update here on the outcome.

matthayes commented 3 years ago

@rwmpelstilzchen @iliekprogrammar I have a candidate solution pushed to a branch if you'd like to test it. I've been using it for the past day and am happy with it so far. You can check out the updated instructions in the branch. The new script is here. I haven't updated the shared deck yet so if you're interested in testing you'll have to make the necessary updates manually. There aren't many changes so it should be easy to test.

I've made a few changes:

  1. The JavaScript is now stored in a versioned file in collections.media, currently _cloze_anything_0.3.js.
  2. I use <script defer src="_cloze_anything_0.3.js"></script> early in the card template. This allows the script to load right away. Using defer causes it to be executed at the end after the page is parsed. I'm not sure how much this helps but it seems like a good idea nonetheless.
  3. I've wrapped the content in a div with class clozed-content. I've updated the CSS rules to make .clozed-content and #cloze initially hidden. After the JavaScript finishes rendering the clozes I make these visible. This prevents the issue where the cloze markup could temporarily appear.

Wrapping the content in a clozed-content div is actually optional considering I make the #cloze element not visible initially. But using clozed-content can make the card rendering nicer because otherwise the contents could shift around due to #cloze div being not shown initially, which is visually jarring.

matthayes commented 3 years ago

I merged a fix for this in 792c58adf7ed3a52e4bbe4b45fe58e40f3213aff.

I made some minor tweaks to the approach I described above. I don't use a clozed-content class anymore and I use visibility: hidden instead of display: none on the #cloze element. This avoids any flashing or shifting of the content. Visually it looks indistinguishable to me from the approach of having the script content embedded in the page.

matthayes commented 3 years ago

Released in v0.3, which you can download here. I also updated the sample deck.