mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
Revert "Revert "Preserve HTML formatting inside clozes (#3038)""
This reverts commit e911b4b69a
.
Trying again now that 24.04 is out.
This commit is contained in:
parent
9f55cf26fc
commit
9c733848b8
7 changed files with 95 additions and 19 deletions
|
@ -1246,7 +1246,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too
|
||||||
highest += 1
|
highest += 1
|
||||||
# must start at 1
|
# must start at 1
|
||||||
highest = max(1, highest)
|
highest = max(1, highest)
|
||||||
self.web.eval("wrap('{{c%d::', '}}');" % highest)
|
self.web.eval("wrapCloze(%d);" % highest)
|
||||||
|
|
||||||
def setupForegroundButton(self) -> None:
|
def setupForegroundButton(self) -> None:
|
||||||
self.fcolour = self.mw.pm.profile.get("lastColour", "#00f")
|
self.fcolour = self.mw.pm.profile.get("lastColour", "#00f")
|
||||||
|
|
|
@ -55,18 +55,16 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
async function onIncrementCloze(): Promise<void> {
|
async function onIncrementCloze(): Promise<void> {
|
||||||
const highestCloze = getCurrentHighestCloze(true);
|
const highestCloze = getCurrentHighestCloze(true);
|
||||||
|
|
||||||
dispatch("surround", {
|
dispatch("cloze", {
|
||||||
prefix: `{{c${highestCloze}::`,
|
n: highestCloze,
|
||||||
suffix: "}}",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onSameCloze(): Promise<void> {
|
async function onSameCloze(): Promise<void> {
|
||||||
const highestCloze = getCurrentHighestCloze(false);
|
const highestCloze = getCurrentHighestCloze(false);
|
||||||
|
|
||||||
dispatch("surround", {
|
dispatch("cloze", {
|
||||||
prefix: `{{c${highestCloze}::`,
|
n: highestCloze,
|
||||||
suffix: "}}",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -392,7 +392,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
import { ImageOcclusionFieldIndexes } from "@generated/anki/image_occlusion_pb";
|
import { ImageOcclusionFieldIndexes } from "@generated/anki/image_occlusion_pb";
|
||||||
import { getImageOcclusionFields } from "@generated/backend";
|
import { getImageOcclusionFields } from "@generated/backend";
|
||||||
import { wrapInternal } from "@tslib/wrap";
|
import { wrapClozeInternal, wrapInternal } from "@tslib/wrap";
|
||||||
|
|
||||||
import Shortcut from "$lib/components/Shortcut.svelte";
|
import Shortcut from "$lib/components/Shortcut.svelte";
|
||||||
|
|
||||||
|
@ -547,6 +547,16 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function wrapCloze(n: number): void {
|
||||||
|
if (!$focusedInput || !editingInputIsRichText($focusedInput)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$focusedInput.element.then((element) => {
|
||||||
|
wrapClozeInternal(element, n);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Object.assign(globalThis, {
|
Object.assign(globalThis, {
|
||||||
saveSession,
|
saveSession,
|
||||||
setFields,
|
setFields,
|
||||||
|
@ -565,6 +575,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
setNoteId,
|
setNoteId,
|
||||||
setNotetypeMeta,
|
setNotetypeMeta,
|
||||||
wrap,
|
wrap,
|
||||||
|
wrapCloze,
|
||||||
setMathjaxEnabled,
|
setMathjaxEnabled,
|
||||||
setShrinkImages,
|
setShrinkImages,
|
||||||
setCloseHTMLTags,
|
setCloseHTMLTags,
|
||||||
|
|
|
@ -3,7 +3,7 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { wrapInternal } from "@tslib/wrap";
|
import { wrapClozeInternal } from "@tslib/wrap";
|
||||||
|
|
||||||
import ClozeButtons from "../ClozeButtons.svelte";
|
import ClozeButtons from "../ClozeButtons.svelte";
|
||||||
import { context as noteEditorContext } from "../NoteEditor.svelte";
|
import { context as noteEditorContext } from "../NoteEditor.svelte";
|
||||||
|
@ -13,12 +13,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
$: richTextAPI = $focusedInput as RichTextInputAPI;
|
$: richTextAPI = $focusedInput as RichTextInputAPI;
|
||||||
|
|
||||||
async function onSurround({ detail }): Promise<void> {
|
async function onCloze({ detail }): Promise<void> {
|
||||||
const richText = await richTextAPI.element;
|
const richText = await richTextAPI.element;
|
||||||
const { prefix, suffix } = detail;
|
const { n } = detail;
|
||||||
|
wrapClozeInternal(richText, n);
|
||||||
wrapInternal(richText, prefix, suffix, false);
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ClozeButtons on:surround={onSurround} />
|
<ClozeButtons on:cloze={onCloze} />
|
||||||
|
|
|
@ -39,7 +39,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
|
|
||||||
<ClozeButtons on:surround alwaysEnabled={true} />
|
<ClozeButtons on:cloze alwaysEnabled={true} />
|
||||||
|
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
|
|
@ -261,12 +261,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
on:surround={async ({ detail }) => {
|
on:cloze={async ({ detail }) => {
|
||||||
const editor = await mathjaxEditor.editor;
|
const editor = await mathjaxEditor.editor;
|
||||||
const { prefix, suffix } = detail;
|
const { n } = detail;
|
||||||
|
|
||||||
editor.replaceSelection(
|
editor.replaceSelection(
|
||||||
prefix + editor.getSelection() + suffix,
|
`{{c${n}::` + editor.getSelection() + "}}",
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
import { placeCaretAfter } from "../domlib/place-caret";
|
||||||
import { getRange, getSelection } from "./cross-browser";
|
import { getRange, getSelection } from "./cross-browser";
|
||||||
|
import { nodeIsElement } from "./dom";
|
||||||
|
|
||||||
function wrappedExceptForWhitespace(text: string, front: string, back: string): string {
|
function wrappedExceptForWhitespace(text: string, front: string, back: string): string {
|
||||||
const match = text.match(/^(\s*)([^]*?)(\s*)$/)!;
|
const match = text.match(/^(\s*)([^]*?)(\s*)$/)!;
|
||||||
|
@ -53,3 +55,70 @@ export function wrapInternal(
|
||||||
moveCursorInside(selection, back);
|
moveCursorInside(selection, back);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function wrapClozeInternal(base: Element, n: number): void {
|
||||||
|
const selection = getSelection(base)!;
|
||||||
|
const range = getRange(selection);
|
||||||
|
if (!range) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expand the range to include parent nodes whose children are already included.
|
||||||
|
// This is to work around .extractContents() adding redundant empty elements
|
||||||
|
let startParent: Node | null = range.startContainer.parentNode;
|
||||||
|
if (
|
||||||
|
startParent !== base
|
||||||
|
&& startParent?.firstChild === range.startContainer && range.startOffset === 0
|
||||||
|
) {
|
||||||
|
range.setStartBefore(startParent);
|
||||||
|
}
|
||||||
|
let endParent: Node | null = range.endContainer.parentNode;
|
||||||
|
if (
|
||||||
|
endParent !== base
|
||||||
|
&& endParent?.lastChild === range.endContainer && (
|
||||||
|
(!nodeIsElement(range.endContainer)
|
||||||
|
&& range.endOffset === range.endContainer.textContent?.length)
|
||||||
|
|| (nodeIsElement(range.endContainer)
|
||||||
|
&& range.endOffset === range.endContainer.childNodes.length)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
range.setEndAfter(endParent);
|
||||||
|
}
|
||||||
|
let expand: boolean;
|
||||||
|
do {
|
||||||
|
expand = false;
|
||||||
|
if (
|
||||||
|
startParent
|
||||||
|
&& startParent.parentNode !== base && startParent.parentNode?.firstChild === startParent
|
||||||
|
&& range.isPointInRange(startParent.parentNode, startParent.parentNode?.childNodes.length)
|
||||||
|
) {
|
||||||
|
startParent = startParent.parentNode;
|
||||||
|
range.setStartBefore(startParent);
|
||||||
|
expand = true;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
endParent && endParent.parentNode !== base && endParent.parentNode?.lastChild === endParent
|
||||||
|
&& range.isPointInRange(endParent.parentNode, 0)
|
||||||
|
) {
|
||||||
|
endParent = endParent.parentNode;
|
||||||
|
range.setEndAfter(endParent);
|
||||||
|
expand = true;
|
||||||
|
}
|
||||||
|
if (range.endOffset === 0) {
|
||||||
|
range.setEndBefore(range.endContainer);
|
||||||
|
expand = true;
|
||||||
|
}
|
||||||
|
} while (expand);
|
||||||
|
|
||||||
|
const fragment = range.extractContents();
|
||||||
|
if (fragment.childNodes.length === 0) {
|
||||||
|
document.execCommand("inserthtml", false, `{{c${n}::}}`);
|
||||||
|
} else {
|
||||||
|
const startNode = document.createTextNode(`{{c${n}::`);
|
||||||
|
const endNode = document.createTextNode("}}");
|
||||||
|
range.insertNode(endNode);
|
||||||
|
range.insertNode(fragment);
|
||||||
|
range.insertNode(startNode);
|
||||||
|
placeCaretAfter(endNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue