|
| 1 | +// Copyright: Ankitects Pty Ltd and contributors |
| 2 | +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html |
| 3 | + |
| 4 | +import * as tr from "@tslib/ftl"; |
| 5 | +import { get } from "svelte/store"; |
| 6 | + |
| 7 | +import type { Collection } from "../lib/proto"; |
| 8 | +import { addImageOcclusionNotes } from "./lib"; |
| 9 | +import { noteFieldsData, tagsWritable } from "./store"; |
| 10 | +import Toast from "./Toast.svelte"; |
| 11 | +import { getQuestionMaskColor } from "./tools/lib"; |
| 12 | + |
| 13 | +const divData = [ |
| 14 | + "angle", |
| 15 | + "fill", |
| 16 | + "height", |
| 17 | + "left", |
| 18 | + "points", |
| 19 | + "rx", |
| 20 | + "ry", |
| 21 | + "top", |
| 22 | + "type", |
| 23 | + "width", |
| 24 | +]; |
| 25 | + |
| 26 | +export function generate(): { occlusionCloze: string; noteCount: number } { |
| 27 | + const canvas = globalThis.canvas; |
| 28 | + const canvasObjects = canvas.getObjects(); |
| 29 | + if (canvasObjects.length < 1) { |
| 30 | + return { occlusionCloze: "", noteCount: 0 }; |
| 31 | + } |
| 32 | + |
| 33 | + let occlusionCloze = ""; |
| 34 | + let clozeData = ""; |
| 35 | + let noteCount = 0; |
| 36 | + |
| 37 | + canvasObjects.forEach((object, index) => { |
| 38 | + const obJson = object.toJSON(); |
| 39 | + noteCount++; |
| 40 | + if (obJson.type === "group") { |
| 41 | + clozeData += getGroupCloze(object, index); |
| 42 | + } else { |
| 43 | + clozeData += getCloze(object, index, null); |
| 44 | + } |
| 45 | + }); |
| 46 | + |
| 47 | + occlusionCloze += clozeData; |
| 48 | + console.log(occlusionCloze); |
| 49 | + return { occlusionCloze, noteCount }; |
| 50 | +} |
| 51 | + |
| 52 | +const getCloze = (object, index, relativePos): string => { |
| 53 | + const obJson = object.toJSON(); |
| 54 | + let clozeData = ""; |
| 55 | + |
| 56 | + // generate cloze data in form of |
| 57 | + // {{c1::image-occlusion:rect:top=100:left=100:width=100:height=100}} |
| 58 | + Object.keys(obJson).forEach(function(key) { |
| 59 | + if (divData.includes(key)) { |
| 60 | + if (key === "type") { |
| 61 | + clozeData += `:${obJson[key]}`; |
| 62 | + } else if (key === "points") { |
| 63 | + const points = obJson[key]; |
| 64 | + let pnts = ""; |
| 65 | + points.forEach((point: { x: number; y: number }) => { |
| 66 | + pnts += point.x.toFixed(2) + "," + point.y.toFixed(2) + " "; |
| 67 | + }); |
| 68 | + clozeData += `:${key}=${pnts.trim()}`; |
| 69 | + } else if (relativePos && key === "top") { |
| 70 | + clozeData += `:top=${relativePos.top}`; |
| 71 | + } else if (relativePos && key === "left") { |
| 72 | + clozeData += `:left=${relativePos.left}`; |
| 73 | + } else { |
| 74 | + clozeData += `:${key}=${obJson[key]}`; |
| 75 | + } |
| 76 | + } |
| 77 | + }); |
| 78 | + |
| 79 | + // question mask color, on front side asking for cloze |
| 80 | + clozeData += `:quesmaskcolor=${getQuestionMaskColor()}`; |
| 81 | + clozeData = `{{c${index + 1}::image-occlusion${clozeData}}}\n`; |
| 82 | + return clozeData; |
| 83 | +}; |
| 84 | + |
| 85 | +const getGroupCloze = (group, index): string => { |
| 86 | + let clozeData = ""; |
| 87 | + const objects = group._objects; |
| 88 | + |
| 89 | + objects.forEach((object) => { |
| 90 | + const { top, left } = getObjectPositionInGroup(group, object); |
| 91 | + clozeData += getCloze(object, index, { top, left }); |
| 92 | + }); |
| 93 | + |
| 94 | + return clozeData; |
| 95 | +}; |
| 96 | + |
| 97 | +const getObjectPositionInGroup = (group, object): { top: number; left: number } => { |
| 98 | + let left = object.left + group.left + group.width / 2; |
| 99 | + let top = object.top + group.top + group.height / 2; |
| 100 | + left = left.toFixed(2); |
| 101 | + top = top.toFixed(2); |
| 102 | + return { top, left }; |
| 103 | +}; |
| 104 | + |
| 105 | +export const saveImageNotes = async function( |
| 106 | + imagePath: string, |
| 107 | + deckId: number, |
| 108 | + hideAll: boolean, |
| 109 | +): Promise<void> { |
| 110 | + const { occlusionCloze, noteCount } = generate(); |
| 111 | + if (noteCount === 0) { |
| 112 | + return; |
| 113 | + } |
| 114 | + |
| 115 | + const fieldsData = get(noteFieldsData); |
| 116 | + const tags = get(tagsWritable); |
| 117 | + let header = fieldsData["header"]; |
| 118 | + let notes = fieldsData["back-extra"]; |
| 119 | + |
| 120 | + if (header === undefined) { |
| 121 | + const textArea = document.getElementById("img-occ-html-header")! as HTMLTextAreaElement; |
| 122 | + header = textArea.value; |
| 123 | + } |
| 124 | + |
| 125 | + if (notes === undefined) { |
| 126 | + const textArea = document.getElementById("img-occ-html-back-extra")! as HTMLTextAreaElement; |
| 127 | + notes = textArea.value; |
| 128 | + } |
| 129 | + |
| 130 | + header = `<div>${header}</div>`; |
| 131 | + notes = `<div">${notes}</div>`; |
| 132 | + |
| 133 | + const result = await addImageOcclusionNotes(imagePath, deckId, occlusionCloze, header, notes, tags, hideAll); |
| 134 | + showResult(result, noteCount); |
| 135 | +}; |
| 136 | + |
| 137 | +// show toast message |
| 138 | +const showResult = (result: Collection.OpChanges, count: number) => { |
| 139 | + const toastComponent = new Toast({ |
| 140 | + target: document.body, |
| 141 | + props: { |
| 142 | + message: "", |
| 143 | + type: "error", |
| 144 | + }, |
| 145 | + }); |
| 146 | + |
| 147 | + if (result.note) { |
| 148 | + const msg = tr.importingNoteAdded({ count: count }); |
| 149 | + toastComponent.$set({ message: msg, type: "success", showToast: true }); |
| 150 | + } else { |
| 151 | + const msg = tr.notetypesErrorGeneratingCloze(); |
| 152 | + toastComponent.$set({ message: msg, showToast: true }); |
| 153 | + } |
| 154 | +}; |
0 commit comments