diff --git a/pylib/tests/test_models.py b/pylib/tests/test_models.py
index ce5a98b51..5e45e35bd 100644
--- a/pylib/tests/test_models.py
+++ b/pylib/tests/test_models.py
@@ -2,6 +2,8 @@
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
# coding: utf-8
+import html
+import re
import time
from anki.consts import MODEL_CLOZE
@@ -10,6 +12,12 @@ from anki.utils import is_win, strip_html
from tests.shared import getEmptyCol
+def encode_attribute(s):
+ return "".join(
+ c if c.isalnum() else "{:X};".format(ord(c)) for c in html.escape(s)
+ )
+
+
def test_modelDelete():
col = getEmptyCol()
note = col.newNote()
@@ -176,29 +184,41 @@ def test_cloze():
note = col.new_note(m)
note["Text"] = "hello {{c1::world}}"
assert col.addNote(note) == 1
- assert "hello [...]" in note.cards()[0].question()
- assert "hello world" in note.cards()[0].answer()
+ assert (
+ f'hello [...]'
+ in note.cards()[0].question()
+ )
+ assert 'hello world' in note.cards()[0].answer()
# and with a comment
note = col.new_note(m)
note["Text"] = "hello {{c1::world::typical}}"
assert col.addNote(note) == 1
- assert "[typical]" in note.cards()[0].question()
- assert "world" in note.cards()[0].answer()
+ assert (
+ f'[typical]'
+ in note.cards()[0].question()
+ )
+ assert 'world' in note.cards()[0].answer()
# and with 2 clozes
note = col.new_note(m)
note["Text"] = "hello {{c1::world}} {{c2::bar}}"
assert col.addNote(note) == 2
(c1, c2) = note.cards()
- assert "[...] bar" in c1.question()
- assert "world bar" in c1.answer()
- assert "world [...]" in c2.question()
- assert "world bar" in c2.answer()
+ assert (
+ f'[...] bar'
+ in c1.question()
+ )
+ assert 'world bar' in c1.answer()
+ assert (
+ f'world [...]'
+ in c2.question()
+ )
+ assert 'world bar' in c2.answer()
# if there are multiple answers for a single cloze, they are given in a
# list
note = col.new_note(m)
note["Text"] = "a {{c1::b}} {{c1::c}}"
assert col.addNote(note) == 1
- assert "b c" in (
+ assert 'b c' in (
note.cards()[0].answer()
)
# if we add another cloze, a card should be generated
@@ -216,16 +236,42 @@ def test_cloze_mathjax():
col = getEmptyCol()
m = col.models.by_name("Cloze")
note = col.new_note(m)
+ q1 = "ok"
+ q2 = "not ok"
+ q3 = "2"
+ q4 = "blah"
+ q5 = "text with \(x^2\) jax"
note[
"Text"
- ] = r"{{c1::ok}} \(2^2\) {{c2::not ok}} \(2^{{c3::2}}\) \(x^3\) {{c4::blah}} {{c5::text with \(x^2\) jax}}"
+ ] = "{{{{c1::{}}}}} \(2^2\) {{{{c2::{}}}}} \(2^{{{{c3::{}}}}}\) \(x^3\) {{{{c4::{}}}}} {{{{c5::{}}}}}".format(
+ q1,
+ q2,
+ q3,
+ q4,
+ q5,
+ )
assert col.addNote(note)
assert len(note.cards()) == 5
- assert "class=cloze" in note.cards()[0].question()
- assert "class=cloze" in note.cards()[1].question()
- assert "class=cloze" not in note.cards()[2].question()
- assert "class=cloze" in note.cards()[3].question()
- assert "class=cloze" in note.cards()[4].question()
+ assert (
+ f'class="cloze" data-cloze="{encode_attribute(q1)}"'
+ in note.cards()[0].question()
+ )
+ assert (
+ f'class="cloze" data-cloze="{encode_attribute(q2)}"'
+ in note.cards()[1].question()
+ )
+ assert (
+ f'class="cloze" data-cloze="{encode_attribute(q3)}"'
+ not in note.cards()[2].question()
+ )
+ assert (
+ f'class="cloze" data-cloze="{encode_attribute(q4)}"'
+ in note.cards()[3].question()
+ )
+ assert (
+ f'class="cloze" data-cloze="{encode_attribute(q5)}"'
+ in note.cards()[4].question()
+ )
note = col.new_note(m)
note["Text"] = r"\(a\) {{c1::b}} \[ {{c1::c}} \]"
@@ -234,7 +280,7 @@ def test_cloze_mathjax():
assert (
note.cards()[0]
.question()
- .endswith(r"\(a\) [...] \[ [...] \]")
+ .endswith(r'\(a\) [...] \[ [...] \]')
)
@@ -278,11 +324,12 @@ def test_chained_mods():
)
assert col.addNote(note) == 1
assert (
- "This [sentence] demonstrates [chained] clozes."
+ f'This [sentence]'
+ f' demonstrates [chained] clozes.'
in note.cards()[0].question()
)
assert (
- "This phrase demonstrates en chaine clozes."
+ f'This phrase demonstrates en chaine clozes.'
in note.cards()[0].answer()
)
diff --git a/rslib/src/cloze.rs b/rslib/src/cloze.rs
index dcd064c2f..0612f7795 100644
--- a/rslib/src/cloze.rs
+++ b/rslib/src/cloze.rs
@@ -57,26 +57,30 @@ pub fn reveal_cloze_text(text: &str, cloze_ord: u16, question: bool) -> Cow
.parse()
.unwrap_or(0);
+ let text = caps.get(cloze_caps::TEXT).unwrap().as_str().to_owned();
if captured_ord != cloze_ord {
// other cloze deletions are unchanged
- return caps.get(cloze_caps::TEXT).unwrap().as_str().to_owned();
+ return text;
} else {
cloze_ord_was_in_text = true;
}
+ let text_attr;
let replacement;
if question {
+ text_attr = format!(r#" data-cloze="{}""#, htmlescape::encode_attribute(&text));
// hint provided?
if let Some(hint) = caps.get(cloze_caps::HINT) {
replacement = format!("[{}]", hint.as_str());
} else {
- replacement = "[...]".to_string()
+ replacement = "[...]".to_string();
}
} else {
- replacement = caps.get(cloze_caps::TEXT).unwrap().as_str().to_owned();
+ text_attr = "".to_string();
+ replacement = text;
}
- format!("{}", replacement)
+ format!(r#"{}"#, text_attr, replacement)
});
if !cloze_ord_was_in_text {
diff --git a/rslib/src/template_filters.rs b/rslib/src/template_filters.rs
index b799983b3..21b5b99ce 100644
--- a/rslib/src/template_filters.rs
+++ b/rslib/src/template_filters.rs
@@ -256,7 +256,7 @@ field
assert_eq!(strip_html(&cloze_filter(text, &ctx)).as_ref(), "[...] two");
assert_eq!(
cloze_filter(text, &ctx),
- "[...] two"
+ r#"[...] two"#
);
ctx.card_ord = 1;