@@ -9,10 +9,12 @@ if (LIVE_RELOAD) {
99 . addEventListener ( "change" , ( ) => location . reload ( ) ) ;
1010}
1111import { dictionary } from "../dictionary/dictionary.ts" ;
12+ import { dictionaryParser } from "../dictionary/parser.ts" ;
1213import PROJECT_DATA from "../project_data.json" with { type : "json" } ;
1314import { loadCustomDictionary } from "./dictionary.ts" ;
1415import { checkLocalStorage , setIgnoreError } from "./local_storage.ts" ;
1516import { translate } from "./mod.ts" ;
17+ import { PositionedError } from "./parser/parser_lib.ts" ;
1618import { settings } from "./settings.ts" ;
1719import {
1820 loadFromElements ,
@@ -21,6 +23,11 @@ import {
2123 resetElementsToDefault ,
2224} from "./settings_frontend.ts" ;
2325
26+ const DICTIONARY_AUTO_PARSE_THRESHOLD = 9000 ;
27+
28+ // never change this
29+ const DICTIONARY_KEY = "dictionary" ;
30+
2431const TRANSLATE_LABEL = "Translate" ;
2532const TRANSLATE_LABEL_MULTILINE = "Translate (Ctrl + Enter)" ;
2633
@@ -31,31 +38,27 @@ const SINGULAR_ERROR_MESSAGE = "An error has been found:";
3138const MULTIPLE_ERROR_MESSAGE = "Multiple errors has been found:" ;
3239
3340const DEFAULT_CUSTOM_DICTIONARY_MESSAGE = `\
34- ====================================
35- Welcome to Custom Dictionary Editor!
36- ====================================
37-
38- Here you can customize the dictionary
39- used in ilo Token. You may change the
40- definitions of existing words and
41- even extend ilo Token with more
42- non-pu words. Just know that the
43- custom dictionary comes with
44- limitations. Press Help above to get
45- started.` ;
41+ # ====================================
42+ # Welcome to Custom Dictionary Editor!
43+ # ====================================
44+ #
45+ # Here you can customize the dictionary
46+ # used in ilo Token. You may change the
47+ # definitions of existing words and
48+ # even extend ilo Token with more
49+ # non-pu words. Just know that the
50+ # custom dictionary comes with
51+ # limitations. Press Help above to get
52+ # started.
53+ ` ;
4654
4755const DICTIONARY_LOADING_FAILED_MESSAGE =
4856 "Failed to load custom dictionary. This is mostly likely because the " +
4957 "syntax has been updated and your custom dictionary still uses the old " +
5058 "syntax. Please fix it. Apologies for the inconvenience." ;
5159const NO_WORD_MESSAGE = "Please provide a word" ;
5260const WORD_NOT_FOUND_MESSAGE = "Word not found" ;
53-
54- const DICTIONARY_ERROR_MESSAGE =
55- "Please fix these errors before saving.\n(You may remove these when fixed)" ;
56-
57- // never change this
58- const DICTIONARY_KEY = "dictionary" ;
61+ const DICTIONARY_ERROR_MESSAGE = "Please fix the errors before saving" ;
5962
6063function main ( ) : void {
6164 // load elements
@@ -106,6 +109,12 @@ function main(): void {
106109 const customDictionaryTextBox = document . getElementById (
107110 "custom-dictionary" ,
108111 ) as HTMLTextAreaElement ;
112+ const customDictionaryErrorSummary = document . getElementById (
113+ "custom-dictionary-error-summary" ,
114+ ) as HTMLElement ;
115+ const customDictionaryErrorList = document . getElementById (
116+ "custom-dictionary-error-list" ,
117+ ) as HTMLUListElement ;
109118 const discardButton = document . getElementById (
110119 "discard-button" ,
111120 ) as HTMLButtonElement ;
@@ -151,14 +160,21 @@ function main(): void {
151160 // load settings
152161 loadFromLocalStorage ( ) ;
153162
154- // load custom dictionary
155- const customDictionary = checkLocalStorage ( )
163+ // states for storing previous dictionary states for discarding dictionary edits
164+ let lastSavedText = checkLocalStorage ( )
156165 ? localStorage . getItem ( DICTIONARY_KEY ) ?? ""
157166 : customDictionaryTextBox . value ;
158- if ( customDictionary . trim ( ) !== "" ) {
159- if ( loadCustomDictionary ( customDictionary ) != null ) {
160- showMessage ( DICTIONARY_LOADING_FAILED_MESSAGE ) ;
161- }
167+ let lastSavedDictionary = dictionaryParser . parse ( lastSavedText ) ;
168+
169+ // this variable also holds error messages
170+ let currentDictionary = lastSavedDictionary ;
171+
172+ // load custom dictionary
173+ if ( ! currentDictionary . isError ( ) ) {
174+ loadCustomDictionary ( currentDictionary . array [ 0 ] ) ;
175+ } else {
176+ showDictionaryError ( ) ;
177+ showMessage ( DICTIONARY_LOADING_FAILED_MESSAGE ) ;
162178 }
163179
164180 // initial text area size
@@ -176,6 +192,29 @@ function main(): void {
176192 : TRANSLATE_LABEL ;
177193 }
178194
195+ // show custom dictionary errors
196+ function showDictionaryError ( ) : void {
197+ customDictionaryErrorSummary . innerText =
198+ `Errors (${ currentDictionary . errors . length } ):` ;
199+ customDictionaryErrorList . innerHTML = "" ;
200+ for ( const error of currentDictionary . errors ) {
201+ const element = document . createElement ( "li" ) ;
202+ element . innerText = error . message ;
203+ if ( error instanceof PositionedError && error . position != null ) {
204+ const { position, length } = error . position ;
205+ element . addEventListener ( "click" , ( ) => {
206+ customDictionaryTextBox . focus ( ) ;
207+ customDictionaryTextBox . setSelectionRange (
208+ position ,
209+ position + length ,
210+ ) ;
211+ } ) ;
212+ } else {
213+ throw new Error ( "error without position" ) ;
214+ }
215+ customDictionaryErrorList . appendChild ( element ) ;
216+ }
217+ }
179218 // add all event listener
180219 translateButton . addEventListener ( "click" , updateOutput ) ;
181220 inputTextBox . addEventListener ( "input" , resizeTextarea ) ;
@@ -239,7 +278,7 @@ function main(): void {
239278 customDictionaryDialogBox . showModal ( ) ;
240279 if ( checkLocalStorage ( ) ) {
241280 customDictionaryTextBox . value = localStorage . getItem ( DICTIONARY_KEY ) ??
242- ` ${ asComment ( DEFAULT_CUSTOM_DICTIONARY_MESSAGE ) } \n` ;
281+ DEFAULT_CUSTOM_DICTIONARY_MESSAGE ;
243282 }
244283 } ) ;
245284 importWordButton . addEventListener ( "click" , importWord ) ;
@@ -249,43 +288,61 @@ function main(): void {
249288 importWord ( ) ;
250289 }
251290 } ) ;
252- function displayToCustomDictionary ( message : string ) : void {
253- const original = customDictionaryTextBox . value . trimEnd ( ) ;
254- const append = original === "" ? "" : "\n\n" ;
255- customDictionaryTextBox . value =
256- `${ original } ${ append } ${ message . trimEnd ( ) } \n` ;
257- customDictionaryTextBox . scrollTo ( 0 , customDictionaryTextBox . scrollHeight ) ;
258- }
259291 function importWord ( ) : void {
260292 const word = importWordTextBox . value . trim ( ) ;
261293 if ( word === "" ) {
262294 showMessage ( NO_WORD_MESSAGE ) ;
263295 } else {
264296 const definitions = dictionary . get ( word ) ?. source ;
265297 if ( definitions != null ) {
266- displayToCustomDictionary ( `${ word } :${ definitions } ` ) ;
298+ const original = customDictionaryTextBox . value . trimEnd ( ) ;
299+ const append = original === "" ? "" : "\n\n" ;
300+ customDictionaryTextBox . value =
301+ `${ original } ${ append } ${ word } :${ definitions . trimEnd ( ) } \n` ;
302+ customDictionaryTextBox . scrollTo (
303+ 0 ,
304+ customDictionaryTextBox . scrollHeight ,
305+ ) ;
267306 } else {
268307 showMessage ( WORD_NOT_FOUND_MESSAGE ) ;
269308 }
270309 }
271310 }
311+ customDictionaryTextBox . addEventListener ( "input" , ( ) => {
312+ if (
313+ customDictionaryTextBox . value . length <= DICTIONARY_AUTO_PARSE_THRESHOLD
314+ ) {
315+ updateDictionary ( ) ;
316+ }
317+ } ) ;
272318 discardButton . addEventListener ( "click" , ( ) => {
273- customDictionaryDialogBox . close ( ) ;
319+ customDictionaryTextBox . value = lastSavedText ;
320+ currentDictionary = lastSavedDictionary ;
321+ tryCloseDictionary ( ) ;
274322 } ) ;
275323 saveButton . addEventListener ( "click" , ( ) => {
276- const { value } = customDictionaryTextBox ;
277- const errors = loadCustomDictionary ( value ) ;
278- if ( errors == null ) {
279- setIgnoreError ( DICTIONARY_KEY , value ) ;
324+ if (
325+ customDictionaryTextBox . value . length > DICTIONARY_AUTO_PARSE_THRESHOLD
326+ ) {
327+ updateDictionary ( ) ;
328+ }
329+ tryCloseDictionary ( ) ;
330+ } ) ;
331+ function updateDictionary ( ) : void {
332+ currentDictionary = dictionaryParser . parse ( customDictionaryTextBox . value ) ;
333+ showDictionaryError ( ) ;
334+ }
335+ function tryCloseDictionary ( ) : void {
336+ if ( ! currentDictionary . isError ( ) ) {
337+ lastSavedText = customDictionaryTextBox . value ;
338+ lastSavedDictionary = currentDictionary ;
339+ loadCustomDictionary ( currentDictionary . array [ 0 ] ) ;
340+ setIgnoreError ( DICTIONARY_KEY , customDictionaryTextBox . value ) ;
280341 customDictionaryDialogBox . close ( ) ;
281342 } else {
282- const errorListMessage = errors
283- . map ( ( error ) => `\n- ${ error . message . replaceAll ( / \r ? \n / g, "$& " ) } ` ) ;
284- displayToCustomDictionary (
285- asComment ( `${ DICTIONARY_ERROR_MESSAGE } ${ errorListMessage } ` ) ,
286- ) ;
343+ showMessage ( DICTIONARY_ERROR_MESSAGE ) ;
287344 }
288- } ) ;
345+ }
289346 closeButton . addEventListener ( "click" , ( ) => {
290347 alertBox . close ( ) ;
291348 } ) ;
@@ -311,8 +368,3 @@ const unused = [...new Array(localStorage.length).keys()]
311368for ( const key of unused ) {
312369 localStorage . removeItem ( key ) ;
313370}
314- export function asComment ( text : string ) : string {
315- return text
316- . replaceAll ( / ^ / mg, "# " )
317- . replaceAll ( / ^ # \s + $ / mg, "#" ) ;
318- }
0 commit comments