最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

JavaScript works in 2.0 version of Anki but not in 2.1 version - Stack Overflow

programmeradmin3浏览0评论

I'm using Anki to help my 5th grade son study Math. My son has reached a point where he needs to review how to approach specific problems and not simply memorize facts.

For example imagine you are trying to learn how to multiply fractions... .45 X .61 = ?

I needed a way to put random math questions into an Anki card. This means that one day the card would display .45 X .61 = ? The next time the card is displayed the question might be.. .34 X .12 = ?

After scouring the internet I was able to find an article that explains how to do this...

.html

I'm not a JavaScript developer, but I was able to get this technique to work after banging my head on my keyboard for a week.

Let me explain how you do this and then I'll explain where the problem is.

Install the 2.0 version of Anki.

Open Anki and create a new deck.

Add a new card to the deck.

The add card dialog box appears. At the very top are two long buttons... "Type" & "Deck". Click on the "Type" button to the left.

The Choose note type dialog box appears.

Click on the "Manage" button at the bottom of the dialog box.

Select the Basic note type and click the "Add" button to the left.

The first option should be "Add Basic". Click the "ok" button at the bottom of the dialog box.

Name your new note type.. "Scripting Note Type" then click "OK"

Hit escape.

Now select your new note type ("Scripting Note Type") then click "Choose".

Now we want to add an additional field. By default this note type has fields named "Front" & "Back". We want to add a third field named "Script".

There is a button labeled "Fields" near the top left. Click on this button.

Another dialog box appears. To the left is an Add button. Click it.

Enter Script in the dialog box that appears and click the "OK" button.

Now close the current dialog box by clicking the close button near the button toward the right side.

Let set up our cards now. Near the top left of the current screen are two buttons. The button to the right is labeled "Cards". Click this button.

This is where we set up the card template. One part of the JavaScript code goes into the Front Template. There should be the following text here currently...

{{Front}}

Delete everything and paste the following code.

<script>

function persist(cb) {
    window.setTimeout(function() {
        // Determine whether to use Anki's Bridge object (Desktop) or sessionStorage (AnkiDroid) to store data across sides.
        var dummy = {};

        var mode = "dummy";
        if (typeof(py) !== "undefined") {
            mode = "py";
            py.data = py.data || {};
        } else if (typeof(sessionStorage) !== "undefined") {
            mode = "sessionStorage";
        }

        var dataObj = {
            setItem: function(key, val) {
                if (mode === "dummy") {
                    dummy[key] = val;
                } else if (mode === "py") {
                    py.data[key] = val;
                } else if (mode === "sessionStorage") {
                    sessionStorage.setItem(key, val);
                }
            },
            getItem: function(key, def) {
                var val = undefined;
                if (mode === "dummy") {
                    val = dummy[key];
                } else if (mode === "py") {
                    val = py.data[key];
                } else if (mode === "sessionStorage") {
                    val = sessionStorage.getItem(key);
                }
                if (val == null) {
                    return def;
                } else {
                    return val;
                }
            },
            tryItem: function(key, val) {
                var currVal = dataObj.getItem(key, undefined);
                if (currVal == null) {
                    dataObj.setItem(key, val);
                    return val;
                } else {
                    return currVal;
                }
            },
            clear: function() {
                if (mode === "dummy") {
                    dummy = {};
                } else if (mode === "py") {
                    window.py.data = {};
                } else if (mode === "sessionStorage") {
                    sessionStorage.clear();
                }
            }
        };

        if (!document.getElementById("back")) {
            dataObj.clear();
        }

        cb(dataObj);
    }, 0); //Execute after Anki has loaded its Bridge object.
}

</script>

<script>

var code = (function () {/* {{Script}} */}).toString();
code = code.replace(/^[^\/]+\/\*!?/, "").replace(/\*\/[^\/]+$/, ""); //Strip beginning/ending ments.
code = code.replace(/<div>/g, "\n").replace(/<\/div>/g, "\n").replace(/<br \/>/g, "\n"); //Strip HTML.
code = code.replace(/&nbsp;/g, " ").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&"); //Replace special symbols.
eval(code);
</script>

<div id="front">{{Front}}</div>

This JavaScript takes the JavaScrippt code that we will place in the "Script" field and executes it with an eval statement.

Notice in this code the persist function that keeps values consistent between the front and back of the Anki card. My issue is related to this persist function.

In the bottom third of the screen is a "Back Template". Delete everything and paste the following code...

  {{FrontSide}}

<hr id=answer>

<div id="back">{{Back}}</div>

On the lower right click the "Close" button.

Now we need to fill in the fields. The values in front and back do not matter. I put "Front" in Front and "Back" in back.

For the Script field we want to add some code. We need to open the HTML view before we paste code. Make sure your cursor is in the Script fields.

In the 2.0 version of Anki there is a button with a picture of a down arrow in the upper right portion of the screen. The 2.1 version has "..." instead of the down arrow button.

Click this arrow and a menu appears. Select the "Edit HTML" menu option.

Paste the following code...

window.setTimeout(function(){

persist(function(data) {


var num1 = data.tryItem("num1",Math.random());


document.getElementById("front").innerHTML = num1; 
document.getElementById("back").innerHTML = num1;
});

},0);

This works in the 2.0 version of Anki.

Here is the Question

And here is the Answer...

We generated a random number in JavaScript and that value is persisted between the front and back of an Anki Card. This opens up worlds of possibility for Math and Science Anki cards using random numbers.

However, all is not well in Whoville.

When you run this code on the 2.1 version of Anki it does not work.

The script is bypassed and the static values of the Front and Back fields are displayed.

JavaScript is still being executed. I could put an alert statement in the Script field and it works.

The problem is in the Persist function. The newer version of Anki does not like something about the Persist function.

Can anyone point me in the right direction.

I'm using Anki to help my 5th grade son study Math. My son has reached a point where he needs to review how to approach specific problems and not simply memorize facts.

For example imagine you are trying to learn how to multiply fractions... .45 X .61 = ?

I needed a way to put random math questions into an Anki card. This means that one day the card would display .45 X .61 = ? The next time the card is displayed the question might be.. .34 X .12 = ?

After scouring the internet I was able to find an article that explains how to do this...

https://yingtongli.me/blog/2015/03/15/random-question-generator-on-anki-using.html

I'm not a JavaScript developer, but I was able to get this technique to work after banging my head on my keyboard for a week.

Let me explain how you do this and then I'll explain where the problem is.

Install the 2.0 version of Anki.

Open Anki and create a new deck.

Add a new card to the deck.

The add card dialog box appears. At the very top are two long buttons... "Type" & "Deck". Click on the "Type" button to the left.

The Choose note type dialog box appears.

Click on the "Manage" button at the bottom of the dialog box.

Select the Basic note type and click the "Add" button to the left.

The first option should be "Add Basic". Click the "ok" button at the bottom of the dialog box.

Name your new note type.. "Scripting Note Type" then click "OK"

Hit escape.

Now select your new note type ("Scripting Note Type") then click "Choose".

Now we want to add an additional field. By default this note type has fields named "Front" & "Back". We want to add a third field named "Script".

There is a button labeled "Fields" near the top left. Click on this button.

Another dialog box appears. To the left is an Add button. Click it.

Enter Script in the dialog box that appears and click the "OK" button.

Now close the current dialog box by clicking the close button near the button toward the right side.

Let set up our cards now. Near the top left of the current screen are two buttons. The button to the right is labeled "Cards". Click this button.

This is where we set up the card template. One part of the JavaScript code goes into the Front Template. There should be the following text here currently...

{{Front}}

Delete everything and paste the following code.

<script>

function persist(cb) {
    window.setTimeout(function() {
        // Determine whether to use Anki's Bridge object (Desktop) or sessionStorage (AnkiDroid) to store data across sides.
        var dummy = {};

        var mode = "dummy";
        if (typeof(py) !== "undefined") {
            mode = "py";
            py.data = py.data || {};
        } else if (typeof(sessionStorage) !== "undefined") {
            mode = "sessionStorage";
        }

        var dataObj = {
            setItem: function(key, val) {
                if (mode === "dummy") {
                    dummy[key] = val;
                } else if (mode === "py") {
                    py.data[key] = val;
                } else if (mode === "sessionStorage") {
                    sessionStorage.setItem(key, val);
                }
            },
            getItem: function(key, def) {
                var val = undefined;
                if (mode === "dummy") {
                    val = dummy[key];
                } else if (mode === "py") {
                    val = py.data[key];
                } else if (mode === "sessionStorage") {
                    val = sessionStorage.getItem(key);
                }
                if (val == null) {
                    return def;
                } else {
                    return val;
                }
            },
            tryItem: function(key, val) {
                var currVal = dataObj.getItem(key, undefined);
                if (currVal == null) {
                    dataObj.setItem(key, val);
                    return val;
                } else {
                    return currVal;
                }
            },
            clear: function() {
                if (mode === "dummy") {
                    dummy = {};
                } else if (mode === "py") {
                    window.py.data = {};
                } else if (mode === "sessionStorage") {
                    sessionStorage.clear();
                }
            }
        };

        if (!document.getElementById("back")) {
            dataObj.clear();
        }

        cb(dataObj);
    }, 0); //Execute after Anki has loaded its Bridge object.
}

</script>

<script>

var code = (function () {/* {{Script}} */}).toString();
code = code.replace(/^[^\/]+\/\*!?/, "").replace(/\*\/[^\/]+$/, ""); //Strip beginning/ending ments.
code = code.replace(/<div>/g, "\n").replace(/<\/div>/g, "\n").replace(/<br \/>/g, "\n"); //Strip HTML.
code = code.replace(/&nbsp;/g, " ").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&"); //Replace special symbols.
eval(code);
</script>

<div id="front">{{Front}}</div>

This JavaScript takes the JavaScrippt code that we will place in the "Script" field and executes it with an eval statement.

Notice in this code the persist function that keeps values consistent between the front and back of the Anki card. My issue is related to this persist function.

In the bottom third of the screen is a "Back Template". Delete everything and paste the following code...

  {{FrontSide}}

<hr id=answer>

<div id="back">{{Back}}</div>

On the lower right click the "Close" button.

Now we need to fill in the fields. The values in front and back do not matter. I put "Front" in Front and "Back" in back.

For the Script field we want to add some code. We need to open the HTML view before we paste code. Make sure your cursor is in the Script fields.

In the 2.0 version of Anki there is a button with a picture of a down arrow in the upper right portion of the screen. The 2.1 version has "..." instead of the down arrow button.

Click this arrow and a menu appears. Select the "Edit HTML" menu option.

Paste the following code...

window.setTimeout(function(){

persist(function(data) {


var num1 = data.tryItem("num1",Math.random());


document.getElementById("front").innerHTML = num1; 
document.getElementById("back").innerHTML = num1;
});

},0);

This works in the 2.0 version of Anki.

Here is the Question

And here is the Answer...

We generated a random number in JavaScript and that value is persisted between the front and back of an Anki Card. This opens up worlds of possibility for Math and Science Anki cards using random numbers.

However, all is not well in Whoville.

When you run this code on the 2.1 version of Anki it does not work.

The script is bypassed and the static values of the Front and Back fields are displayed.

JavaScript is still being executed. I could put an alert statement in the Script field and it works.

The problem is in the Persist function. The newer version of Anki does not like something about the Persist function.

Can anyone point me in the right direction.

Share Improve this question edited Jan 30, 2019 at 12:57 codingguy3000 asked Jan 29, 2019 at 14:25 codingguy3000codingguy3000 2,83515 gold badges49 silver badges76 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 10

I figured it out...

The issue is that Anki 2.1 eliminated the ability to store persistent data in the window.py object.

Now you might be asking (as I did) what is the window.py object? I'm not 100% sure but I believe its an internal Python object that allows some interactions between JavaScript and Python. Anki is written in Python.

In any case window.py is no longer accessible in Anki 2.1 which mean the technique I described in my question (see above) no longer works in Anki 2.1.

Fortunately, the author the article (referenced above in the question) mentioned that the window object is shared across invocations of an Anki card question and answer.

I started playing around with the window object in Javascript and I was able to get data persistence working in Anki 2.1 (This also works in Anki 2.0)

In the question above I go into great depth into to this technique to generate persistent random values in your Anki cards. This means you generate a random number in the question and that same values is available in the answer. I won't go into as much detail here, but refer the the question above for more details.

In the "Front Template" here is the code.

    <script>    
        var code = (function () {/* {{Script}} */}).toString();    
        code = code.replace(/^[^\/]+\/\*!?/, "").replace(/\*\/[^\/]+$/, ""); //Strip beginning/ending ments.

        code = code.replace(/<div>/g, "\n").replace(/<\/div>/g, "\n").replace(/<br \/>/g, "\n"); //Strip HTML.

        code = code.replace(/&nbsp;/g, " ").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&"); //Replace special symbols.

        eval(code);

    </script>    
<div id="front">{{Front}}</div>

This is the same code from the article. I have simply removed some code that no longer works.

Here is the card script code...

window.setTimeout(function(){     

var windowsref = window; //Set a reference to the window object     

if ((("num1" in windowsref) == false) || windowsref.cnt != 1) {

            windowsref.num1 = Math.random();

            windowsref.cnt = 0;

   }      

windowsref.cnt++;

document.getElementById("front").innerHTML = windowsref.num1;

document.getElementById("back").innerHTML = windowsref.num1;     

},0);

This code works only in Anki 2.1 desktop. I also tested iOS and it works there as well.

I hope this helps someone else.

check out https://github./SimonLammer/anki-persistence It uses different approaches to achieve persistence.

But Anki environment is somewhat unpredictable, and you need to check if the code has succesfuly initialized.

glad to hear that you found a solution, codingguy.

For further information, I want to add I have dabbled with the same idea around 2014. I got it to work using JavaScript and Cookies in my note templates.

For that, I had to modify Anki with a plugin (to monkey-patch creation of the Qt Web View) which I shared as Anki 2.0 plugin JS Booster. Adapted for Anki 2.1, another developer published the same solution here Anki 2.1 plugin Cookie Monster.

发布评论

评论列表(0)

  1. 暂无评论