source: gs3-installations/atea/trunk/interfaces/atea/macron-restoration/src/components/DirectInput.vue@ 36871

Last change on this file since 36871 was 36871, checked in by cstephen, 18 months ago

Update the macron-restoration project to fix dependency vunerabilities

File size: 8.2 KB
Line 
1<script>
2import { mapState } from "vuex";
3import { saveAs } from "file-saver"
4import Util, { log } from "../js/Util"
5import MacronRestorationModule from "../js/MacronRestorationModule"
6import { SnackController } from "./SnackBar.vue"
7
8const macroniser = new MacronRestorationModule();
9
10export default {
11 name: "DirectInput",
12 data() {
13 return {
14 macroniseWaitTimeout: null,
15 waitingToMacronise: false,
16 errorState: false,
17 preserveExistingMacrons: true,
18 enableSpellcheck: false,
19 showMacronisedWords: true,
20 normaliseLinebreaks: false
21 }
22 },
23 computed: {
24 canDownload() {
25 return this.restored.length > 0 && !this.waitingToMacronise;
26 },
27 input: {
28 get() {
29 return this.$store.state.directInput;
30 },
31 set(newValue) {
32 this.$store.commit("setDirectInput", newValue);
33 this.startMacronisationWait();
34 }
35 },
36 restored: {
37 get() {
38 return this.$store.state.directOutput;
39 },
40 set(newValue) {
41 this.$store.commit("setDirectOutput", newValue);
42 }
43 },
44 htmlFormattedRestored() {
45 let restored = "";
46
47 for (const word of this.restored) {
48 if (word.w) {
49 if (word[0] !== "-") {
50 restored += " ";
51 }
52
53 if (word.macronised && this.showMacronisedWords) {
54 restored += `<mark class="highlight">${word.w}</mark>`;
55 }
56 else {
57 restored += word.w;
58 }
59 }
60
61 if (word.linebreaks) {
62 restored += this.normaliseLinebreak(word.linebreaks);
63 }
64 }
65
66 return restored.trim();
67 },
68 formattedRestored() {
69 let restored = "";
70
71 for (const word of this.restored) {
72 if (word.w) {
73 if (word[0] !== "-") {
74 restored += " ";
75 }
76
77 restored += word.w;
78 }
79
80 if (word.linebreaks) {
81 restored += this.normaliseLinebreak(word.linebreaks);
82 }
83 }
84
85 return restored.trim();
86 },
87 ...mapState({
88 translations: state => state.translations
89 })
90 },
91 watch: {
92 preserveExistingMacrons(newValue) {
93 localStorage.setItem("preserveExistingMacrons", newValue);
94 },
95 enableSpellcheck(newValue) {
96 localStorage.setItem("enableSpellcheck", newValue);
97 },
98 showMacronisedWords(newValue) {
99 localStorage.setItem("showMacronisedWords", newValue);
100 },
101 normaliseLinebreaks(newValue) {
102 localStorage.setItem("normaliseLinebreaks", newValue);
103 }
104 },
105 methods: {
106 /**
107 * Handles input on an element, and grows its height so that the text is always visible.
108 * @param {InputEvent} event The input event.
109 */
110 onTextInput(event) {
111 event.target.height = "auto";
112 event.target.height = event.target.scrollHeight;
113 },
114 startMacronisationWait() {
115 if (this.input === null || this.input === "") {
116 return;
117 }
118
119 this.waitingToMacronise = true;
120
121 if (this.macroniseWaitTimeout !== null) {
122 clearTimeout(this.macroniseWaitTimeout);
123 }
124
125 this.macroniseWaitTimeout = setTimeout(this.updateMacronisedText, 1000);
126 },
127 async updateMacronisedText() {
128 this.errorState = false;
129
130 try {
131 this.restored = await macroniser.directMacronisation(this.input, this.preserveExistingMacrons);
132 this.waitingToMacronise = false;
133 }
134 catch (ex) {
135 this.errorState = true;
136 log(ex, "warn");
137 }
138 },
139
140 downloadAsText() {
141 const blob = new Blob([ this.formattedRestored ], { type: "text/plain;charset=utf-8" });
142 saveAs(blob, "restored.txt");
143 },
144 async copyToClipboard() {
145 await navigator.clipboard.writeText(this.formattedRestored);
146 SnackController.addSnack(this.translations.get("DirectInput_CopySuccess"));
147 },
148
149 /**
150 * Normalises a linebreak.
151 * @param {String} linebreakCount The linebreak sequence.
152 */
153 normaliseLinebreak(linebreakCount) {
154 if (!this.normaliseLinebreaks) {
155 return this.repeatString("\n", linebreakCount);
156 }
157
158 linebreakCount = Math.min(2, linebreakCount);
159 return this.repeatString("\n", linebreakCount);
160 },
161 repeatString(string, count) {
162 let result = "";
163
164 for (let i = 0; i < count; i++) {
165 result += string;
166 }
167
168 return result;
169 }
170 },
171 beforeMount() {
172 // The null check is used to default this setting to true
173 this.preserveExistingMacrons = (localStorage.getItem("preserveExistingMacrons") ?? "true") === "true";
174 this.showMacronisedWords = (localStorage.getItem("showMacronisedWords") ?? "true") === "true";
175 this.normaliseLinebreaks = localStorage.getItem("normaliseLinebreaks") === "true";
176
177 const enableSpellcheck = localStorage.getItem("enableSpellcheck");
178 if (enableSpellcheck) {
179 this.enableSpellcheck = enableSpellcheck === "true";
180 }
181 else {
182 this.enableSpellcheck = Util.getNavigatorLanguage().startsWith("mi");
183 }
184 }
185}
186</script>
187
188<template>
189<div class="root">
190 <textarea class="text-input input-area" @input="onTextInput" autocomplete="off"
191 v-model="input" :placeholder="translations.get('DirectInput_InputPlaceholder')"
192 :spellcheck="enableSpellcheck" />
193
194 <div class="text-container">
195 <span v-if="errorState" class="material-icons mdi-m error-text">error</span>
196 <span v-if="errorState" class="error-text">{{ translations.get('DirectInput_UnknownError') }}</span>
197
198 <span class="preserveWhitespace" v-html="htmlFormattedRestored" />
199 </div>
200
201 <div class="flex">
202 <input type="checkbox" id="i-preserve-existing-macrons" v-model="preserveExistingMacrons" @input="startMacronisationWait" />
203 <label for="i-preserve-existing-macrons">{{ translations.get('DirectInput_PreserveExistingMacrons') }}</label>
204 </div>
205
206 <div class="flex">
207 <input type="checkbox" id="i-show-macronised-words" v-model="showMacronisedWords" :disabled="restored.length === 0" />
208 <label for="i-show-macronised-words">{{ translations.get('DirectInput_ShowMacronisedWords') }}</label>
209 </div>
210
211 <div class="flex">
212 <input type="checkbox" id="i-enable-spellcheck" v-model="enableSpellcheck" />
213 <label for="i-enable-spellcheck">{{ translations.get('DirectInput_EnableSpellcheck') }}</label>
214 </div>
215
216 <div class="flex">
217 <input type="checkbox" id="i-show-macronised-words" v-model="normaliseLinebreaks" :disabled="restored.length === 0" />
218 <label for="i-show-macronised-words">{{ translations.get('DirectInput_NormaliseLinebreaks') }}</label>
219 </div>
220
221 <button class="btn-primary right-column" :disabled="!canDownload" @click="copyToClipboard">
222 {{ translations.get('DirectInput_CopyToClipboard') }}
223 </button>
224
225 <button class="btn-primary right-column" :disabled="!canDownload" @click="downloadAsText">
226 {{ translations.get('DirectInput_Download') }}
227 </button>
228</div>
229</template>
230
231<style scoped lang="scss">
232.root {
233 display: grid;
234 grid-template-columns: 1fr 1fr;
235 gap: 1em;
236}
237
238.preserveWhitespace {
239 white-space: pre-line;
240}
241
242.input-area {
243 resize: vertical;
244 min-height: 4em;
245}
246
247.flex {
248 display: flex;
249 align-items: center;
250 gap: 0.5em;
251}
252
253.left-column {
254 grid-column: 1;
255}
256
257.right-column {
258 grid-column: 2;
259}
260
261.highlight {
262 background-color: var(--highlighted-word-bg);
263}
264
265.error-text {
266 color: rgb(185, 3, 3);
267}
268</style>
Note: See TracBrowser for help on using the repository browser.