JavaScript 适用于 Anki 2.0 版本但不适用于 2.1 版本

JavaScript works in 2.0 version of Anki but not in 2.1 version

我正在使用 Anki 帮助我五年级的儿子学习数学。我儿子已经到了需要复习如何处理具体问题的地步,而不是简单地死记硬背。

例如,假设您正在尝试学习如何乘分数... .45 X .61 = ?

我需要一种将随机数学问题放入 Anki 卡片中的方法。这意味着有一天卡片会显示 .45 X .61 = ? 下次显示卡片时,问题可能是…… .34 X .12 = ?

在网上搜索后,我找到了一篇解释如何执行此操作的文章...

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

我不是 JavaScript 开发人员,但在我的头在键盘上敲了一个星期后,我能够让这项技术发挥作用。

让我解释一下您是如何做到这一点的,然后我将解释问题出在哪里。

安装Anki 2.0版本。

打开 Anki 并创建一个新牌组。

在牌组中添加一张新牌。

出现添加卡片对话框。在最顶部是两个长按钮... "Type" 和 "Deck"。单击左侧的 "Type" 按钮。

出现“选择笔记类型”对话框。

单击对话框底部的 "Manage" 按钮。

Select 基本注释类型,然后单击左侧的 "Add" 按钮。

第一个选项应该是"Add Basic"。单击对话框底部的 "ok" 按钮。

命名您的新笔记类型.. "Scripting Note Type" 然后点击 "OK"

命中逃生。

现在 select 您的新笔记类型 ("Scripting Note Type") 然后单击 "Choose"。

现在我们要添加一个额外的字段。默认情况下,此注释类型具有名为 "Front" 和 "Back" 的字段。我们要添加名为 "Script".

的第三个字段

左上角附近有一个标有 "Fields" 的按钮。单击此按钮。

出现另一个对话框。左边是一个添加按钮。点击它。

在出现的对话框中输入脚本,然后点击"OK"按钮。

现在单击右侧按钮附近的关闭按钮关闭当前对话框。

现在开始设置我们的卡片。当前屏幕的左上角附近有两个按钮。右侧的按钮标记为 "Cards"。单击此按钮。

这是我们设置卡片模板的地方。 JavaScript 代码的一部分进入前端模板。目前这里应该有以下文字...

{{正面}}

删除所有内容并粘贴以下代码。

<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 comments.
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>

此 JavaScript 获取我们将放置在 "Script" 字段中的 JavaScript 代码,并使用 eval 语句执行它。

注意这段代码中的 persist 函数,它使 Anki 卡片正面和背面的值保持一致。我的问题与此持久功能有关。

在屏幕的底部三分之一是 "Back Template"。删除所有内容并粘贴以下代码...

  {{FrontSide}}

<hr id=answer>

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

点击右下角的"Close"按钮。

现在我们需要填写字段。前面和后面的值无关紧要。我把 "Front" 放在前面,"Back" 在后面。

我们要为脚本字段添加一些代码。在粘贴代码之前,我们需要打开 HTML 视图。确保您的光标位于脚本字段中。

在Anki 2.0 版本中,屏幕右上角有一个带有向下箭头图片的按钮。 2.1 版本有“...”而不是向下箭头按钮。

单击此箭头将出现一个菜单。 Select "Edit HTML" 菜单选项。

粘贴以下代码...

window.setTimeout(function(){

persist(function(data) {


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


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

},0);

这适用于 Anki 2.0 版本。

这是问题

答案在这里...

我们在 JavaScript 中生成了一个随机数,该值保存在 Anki Card 的正面和背面之间。这为使用随机数的数学和科学 Anki 卡片打开了无限可能。

然而,Whoville 并非一帆风顺。

当你 运行 这段代码在 2.1 版本的 Anki 上它不起作用。

绕过脚本,显示Front和Back字段的静态值。

JavaScript还在执行中。我可以在 Script 字段中放置一个警报语句并且它有效。

问题出在 Persist 函数中。较新版本的 Anki 不喜欢 Persist 函数。

任何人都可以指出我正确的方向。

我想通了...

问题是 Anki 2.1 消除了在 window.py 对象中存储持久数据的能力。

现在您可能会问(和我一样)window.py 对象是什么?我不是 100% 确定,但我相信它是一个内部 Python 对象,允许 JavaScript 和 Python 之间进行一些交互。 Anki 是用 Python.

编写的

无论如何,window.py 在 Anki 2.1 中不再可用,这意味着我在问题中描述的技术(见上文)在 Anki 2.1 中不再有效。

幸运的是,文章的作者(在上面的问题中引用)提到 window 对象在 Anki 卡片问题和答案的调用之间共享。

我开始在 Javascript 中使用 window 对象,并且我能够在 Anki 2.1 中使用数据持久性(这在 Anki 2.0 中也适用)

在上面的问题中,我深入探讨了这种在 Anki 卡片中生成持久随机值的技术。这意味着您在问题中生成了一个随机数,而答案中也有相同的值。我不会在这里详细介绍,但请参阅上面的问题以获取更多详细信息。

在"Front Template"这里是代码。

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

        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>

这与文章中的代码相同。我只是删除了一些不再有效的代码。

这是卡片脚本代码...

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);

此代码仅适用于 Anki 2.1 桌面。我还测试了 iOS 并且它也在那里工作。

我希望这对其他人有帮助。

查看 https://github.com/SimonLammer/anki-persistence 它使用不同的方法来实现持久化。

但Anki环境有些不可预测,需要检查代码是否初始化成功

很高兴听到您找到了解决方案,编码专家。

更多信息,我想补充 我在 2014 年左右涉足过同样的想法。 我在我的笔记模板中使用 JavaScript 和 Cookies 让它工作。

为此,我不得不使用我共享为 Anki 2.0 plugin JS Booster 的插件修改 Anki(以猴子补丁创建 Qt Web 视图)。 适用于 Anki 2.1,另一位开发人员在此处发布了相同的解决方案 Anki 2.1 plugin Cookie Monster