diff --git a/.classpath b/.classpath index 2824a92..48d3b5e 100644 --- a/.classpath +++ b/.classpath @@ -21,17 +21,17 @@ - + - + - + diff --git a/build.xml b/build.xml index e06e8b4..15fe150 100644 --- a/build.xml +++ b/build.xml @@ -6,9 +6,9 @@ - - - + + + diff --git a/css/dark.css b/css/dark.css index 9c23bc5..886a51d 100644 --- a/css/dark.css +++ b/css/dark.css @@ -1,5 +1,6 @@ @import "./theme.css"; @import "./neutrals.css"; + :root { --error-color: #960303; font-size: 16px; @@ -12,6 +13,11 @@ body { stroke: var(--gray10); padding: 0; margin: 0; + overflow: hidden; +} + +.dialog_body { + padding: 8px; } .hdivider { @@ -111,6 +117,7 @@ td.list { td.editing { background-color: var(--gray190) !important; outline: 1px solid var(--themePrimary) !important; + white-space: pre-wrap; } td.fixed { @@ -185,6 +192,10 @@ th { border-bottom: 2px solid var(--gray150); } +.leftBorder { + border-left: 2px solid var(--gray150); +} + .currentRow { border-top: 2px solid var(--themePrimary) !important; border-bottom: 2px solid var(--themePrimary) !important; @@ -230,6 +241,15 @@ th { border-radius: 2px; } +.spaces { + color: var(--gray40); + background-color: var(--themeTertiary); + white-space-collapse: preserve; + padding-left: 0px; + padding-right: 0px; + border-radius: 2px; +} + .right { float: right; } @@ -344,6 +364,10 @@ label { padding-right: 4px; } +.middleLabel { + padding-top: 2px; +} + .column { display: flex; flex-direction: column; @@ -607,6 +631,11 @@ th svg { white-space: pre-wrap; } +.container { + width: 100%; + height: 100%; +} + .divContainer { /* Any container that needs to scroll content */ width: 100%; diff --git a/css/light.css b/css/light.css index 084f04c..c9fe110 100644 --- a/css/light.css +++ b/css/light.css @@ -1,5 +1,6 @@ @import "./theme.css"; @import "./neutrals.css"; + :root { --error-color: #960303; font-size: 16px; @@ -12,6 +13,11 @@ body { stroke: var(--gray190); padding: 0; margin: 0; + overflow: hidden; +} + +.dialog_body { + padding: 8px; } .hdivider { @@ -111,6 +117,7 @@ td.list { td.editing { background-color: var(--gray10) !important; outline: 1px solid var(--themePrimary) !important; + white-space: pre-wrap; } td.fixed { @@ -185,6 +192,10 @@ th { border-bottom: 2px solid var(--gray40); } +.leftBorder { + border-left: 2px solid var(--gray40); +} + .currentRow { border-top: 2px solid var(--themePrimary) !important; border-bottom: 2px solid var(--themePrimary) !important; @@ -230,6 +241,15 @@ th { border-radius: 2px; } +.spaces { + color: var(--gray40); + background-color: var(--themeTertiary); + white-space-collapse: preserve; + padding-left: 0px; + padding-right: 0px; + border-radius: 2px; +} + .right { float: right; } @@ -344,6 +364,10 @@ label { padding-right: 4px; } +.middleLabel { + padding-top: 2px; +} + .column { display: flex; flex-direction: column; @@ -607,6 +631,11 @@ th svg { white-space: pre-wrap; } +.container { + width: 100%; + height: 100%; +} + .divContainer { /* Any container that needs to scroll content */ width: 100%; diff --git a/html/en/about.html b/html/en/about.html index 4bd667c..672dfbc 100644 --- a/html/en/about.html +++ b/html/en/about.html @@ -7,7 +7,7 @@ - +
about box
diff --git a/html/en/addLanguage.html b/html/en/addLanguage.html index 773ac39..c6d1b6f 100644 --- a/html/en/addLanguage.html +++ b/html/en/addLanguage.html @@ -1,5 +1,5 @@ - + Add Language @@ -7,7 +7,7 @@ - + diff --git a/html/en/addNote.html b/html/en/addNote.html index 9fa2e80..d175cec 100644 --- a/html/en/addNote.html +++ b/html/en/addNote.html @@ -7,7 +7,7 @@ - +
diff --git a/html/en/addProperty.html b/html/en/addProperty.html index b876e6a..167a220 100644 --- a/html/en/addProperty.html +++ b/html/en/addProperty.html @@ -7,7 +7,7 @@ - +
diff --git a/html/en/attributes.html b/html/en/attributes.html index 7af78e2..22c5f8c 100644 --- a/html/en/attributes.html +++ b/html/en/attributes.html @@ -1,5 +1,5 @@ - +Translation Unit Attributes @@ -7,87 +7,86 @@ - + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + +
+ + + + + + + + +
+ + + + + + + + +
+ + + + + +  
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - -
- - - - - - - - -
- - - - - - - - -
- - - - - - - - -
- - - - - -  
-
diff --git a/html/en/changeLanguage.html b/html/en/changeLanguage.html index 84e6a6a..aef6b15 100644 --- a/html/en/changeLanguage.html +++ b/html/en/changeLanguage.html @@ -1,5 +1,5 @@ - + Change Language @@ -7,7 +7,7 @@ - + diff --git a/html/en/consolidate.html b/html/en/consolidate.html index 6f5e2d9..a2f6301 100644 --- a/html/en/consolidate.html +++ b/html/en/consolidate.html @@ -1,5 +1,5 @@ - +Consolidate Units @@ -7,7 +7,7 @@ - +
diff --git a/html/en/convertCSV.html b/html/en/convertCSV.html index 44ab2be..04d1a1c 100644 --- a/html/en/convertCSV.html +++ b/html/en/convertCSV.html @@ -1,5 +1,5 @@ - +Convert CSV File to TMX @@ -7,7 +7,7 @@ - +
@@ -79,7 +79,6 @@ - diff --git a/html/en/convertExcel.html b/html/en/convertExcel.html index 58d2637..599d9d1 100644 --- a/html/en/convertExcel.html +++ b/html/en/convertExcel.html @@ -1,5 +1,5 @@ - +Convert Excel File to TMX @@ -7,7 +7,7 @@ - +
@@ -46,7 +46,6 @@ - diff --git a/html/en/convertSDLTM.html b/html/en/convertSDLTM.html index 7e42853..af52200 100644 --- a/html/en/convertSDLTM.html +++ b/html/en/convertSDLTM.html @@ -1,5 +1,5 @@ - +Convert SDLTM File to TMX @@ -7,7 +7,7 @@ - +
@@ -29,7 +29,6 @@ - diff --git a/html/en/convertTBX.html b/html/en/convertTBX.html index b075eba..4c7e012 100644 --- a/html/en/convertTBX.html +++ b/html/en/convertTBX.html @@ -1,5 +1,5 @@ - +Convert TBX File to TMX @@ -7,7 +7,7 @@ - +
@@ -28,7 +28,6 @@ - diff --git a/html/en/csvLanguages.html b/html/en/csvLanguages.html index 07cdf57..efbd820 100644 --- a/html/en/csvLanguages.html +++ b/html/en/csvLanguages.html @@ -1,5 +1,5 @@ - +CSV/Text File Languages @@ -7,7 +7,7 @@ - +
diff --git a/html/en/excelLanguages.html b/html/en/excelLanguages.html index 33bbeaa..8dfd840 100644 --- a/html/en/excelLanguages.html +++ b/html/en/excelLanguages.html @@ -1,5 +1,5 @@ - + Excel File Languages @@ -7,7 +7,7 @@ - +
diff --git a/html/en/fileInfo.html b/html/en/fileInfo.html index 19a0574..8447930 100644 --- a/html/en/fileInfo.html +++ b/html/en/fileInfo.html @@ -9,7 +9,7 @@ -
+
@@ -22,63 +22,121 @@
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Creation Tool
Creation Tool Version
Segmentation Type
Original Translation Memory Format
Administrative Language
Source Language
Data Type
- +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + + + +
+ + + + + +
+ + + + + +
+ + + + + +
+ + + + + +
+
+ +
+ + + + + diff --git a/html/en/filters.html b/html/en/filters.html index 4f30617..8305a5a 100644 --- a/html/en/filters.html +++ b/html/en/filters.html @@ -1,5 +1,5 @@ - + Filter Translation Units @@ -7,7 +7,7 @@ - + @@ -53,7 +53,6 @@

Filter Options

- diff --git a/html/en/licenses.html b/html/en/licenses.html index 968f8fd..8902012 100644 --- a/html/en/licenses.html +++ b/html/en/licenses.html @@ -1,5 +1,5 @@ - +Licenses @@ -7,55 +7,53 @@ - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TMXEditorEclipse Public License 1.0
BCP47JEclipse Public License 1.0
ElectronMIT License
JavaGPL2 with Classpath Exception
JSONJSON.org
sdltmEclipse Public License 1.0
SLF4JMIT License
SQLite JDBC DriverApache License 2.0
TMXValidatorEclipse Public License 1.0
TypesXMLEclipse Public License 1.0
XMLJavaEclipse Public License 1.0
- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TMXEditorEclipse Public License 1.0
BCP47JEclipse Public License 1.0
ElectronMIT License
JavaGPL2 with Classpath Exception
JSONJSON.org
sdltmEclipse Public License 1.0
SLF4JMIT License
SQLite JDBC DriverApache License 2.0
TMXValidatorEclipse Public License 1.0
TypesXMLEclipse Public License 1.0
XMLJavaEclipse Public License 1.0
diff --git a/html/en/maintenance.html b/html/en/maintenance.html index bbf3851..9093310 100644 --- a/html/en/maintenance.html +++ b/html/en/maintenance.html @@ -7,7 +7,7 @@ - + diff --git a/html/en/mergeFiles.html b/html/en/mergeFiles.html index 2d2aeb4..6991149 100644 --- a/html/en/mergeFiles.html +++ b/html/en/mergeFiles.html @@ -1,5 +1,5 @@ - +Merge TMX Files @@ -7,7 +7,7 @@ - +
diff --git a/html/en/messages.html b/html/en/messages.html index c052a47..84aed4d 100644 --- a/html/en/messages.html +++ b/html/en/messages.html @@ -7,7 +7,7 @@ - +
@@ -26,7 +26,6 @@
- diff --git a/html/en/newFile.html b/html/en/newFile.html index 5105a97..1e2ab0c 100644 --- a/html/en/newFile.html +++ b/html/en/newFile.html @@ -1,5 +1,5 @@ - + New TMX File @@ -7,7 +7,7 @@ - +
diff --git a/html/en/notes.html b/html/en/notes.html index 75f6152..92cca5c 100644 --- a/html/en/notes.html +++ b/html/en/notes.html @@ -1,5 +1,5 @@ - + Translation Unit Notes @@ -7,7 +7,7 @@ - +
diff --git a/html/en/preferences.html b/html/en/preferences.html index 5724ffe..4cd397a 100644 --- a/html/en/preferences.html +++ b/html/en/preferences.html @@ -1,5 +1,5 @@ - +Preferences @@ -7,7 +7,7 @@ - +
@@ -33,11 +33,14 @@
spaces
- +
+ + +
- + \ No newline at end of file diff --git a/html/en/properties.html b/html/en/properties.html index e197190..b77a833 100644 --- a/html/en/properties.html +++ b/html/en/properties.html @@ -1,5 +1,5 @@ - + Translation Unit Properties @@ -7,7 +7,7 @@ - +
diff --git a/html/en/removeLanguage.html b/html/en/removeLanguage.html index d19efc6..82cb935 100644 --- a/html/en/removeLanguage.html +++ b/html/en/removeLanguage.html @@ -1,5 +1,5 @@ - +Remove Language @@ -7,7 +7,7 @@ - +
diff --git a/html/en/removeSameAsSource.html b/html/en/removeSameAsSource.html index 76ce82b..3143643 100644 --- a/html/en/removeSameAsSource.html +++ b/html/en/removeSameAsSource.html @@ -1,5 +1,5 @@ - +Remove Translation Same as Source @@ -7,7 +7,7 @@ - +
diff --git a/html/en/removeUntranslated.html b/html/en/removeUntranslated.html index 332b17c..d56293b 100644 --- a/html/en/removeUntranslated.html +++ b/html/en/removeUntranslated.html @@ -1,5 +1,5 @@ - +Remove Untranslated Units @@ -7,7 +7,7 @@ - +
diff --git a/html/en/searchReplace.html b/html/en/searchReplace.html index ebb563c..1990d1f 100644 --- a/html/en/searchReplace.html +++ b/html/en/searchReplace.html @@ -1,5 +1,5 @@ - +Replace Text @@ -7,7 +7,7 @@ - +
@@ -29,13 +29,12 @@
- +
- diff --git a/html/en/sortUnits.html b/html/en/sortUnits.html index 8f918d6..848f158 100644 --- a/html/en/sortUnits.html +++ b/html/en/sortUnits.html @@ -1,5 +1,5 @@ - +Sort Translation Units @@ -7,7 +7,7 @@ - +
diff --git a/html/en/splitFile.html b/html/en/splitFile.html index 044a16e..1dd0432 100644 --- a/html/en/splitFile.html +++ b/html/en/splitFile.html @@ -1,5 +1,5 @@ - +Split TMX File @@ -7,7 +7,7 @@ - +
diff --git a/html/en/srcLanguage.html b/html/en/srcLanguage.html index 3d169a9..5727702 100644 --- a/html/en/srcLanguage.html +++ b/html/en/srcLanguage.html @@ -1,5 +1,5 @@ - +Change Source Language @@ -7,7 +7,7 @@ - +
diff --git a/html/en/systemInfo.html b/html/en/systemInfo.html index 5425094..9d3802f 100644 --- a/html/en/systemInfo.html +++ b/html/en/systemInfo.html @@ -7,32 +7,29 @@ - -
-
- - - - - - - - - - - - - - - - - - - - -
TMXEditor
BCP47J
Java
XMLJava
Electron
-
- + + + + + + + + + + + + + + + + + + + + + + +
TMXEditor
BCP47J
Java
XMLJava
Electron
diff --git a/html/en/updates.html b/html/en/updates.html index e4dc8ae..33f74ab 100644 --- a/html/en/updates.html +++ b/html/en/updates.html @@ -1,5 +1,5 @@ - + Software Updates @@ -7,7 +7,7 @@ - +
@@ -30,7 +30,6 @@
- diff --git a/html/es/about.html b/html/es/about.html index 0a34310..ce8358c 100644 --- a/html/es/about.html +++ b/html/es/about.html @@ -7,7 +7,7 @@ - +
acerca de...
diff --git a/html/es/addLanguage.html b/html/es/addLanguage.html index 236ea97..d2d17a2 100644 --- a/html/es/addLanguage.html +++ b/html/es/addLanguage.html @@ -1,5 +1,5 @@ - + Añadir Idioma @@ -7,7 +7,7 @@ - + diff --git a/html/es/addNote.html b/html/es/addNote.html index dcf0012..1a8f0ed 100644 --- a/html/es/addNote.html +++ b/html/es/addNote.html @@ -7,7 +7,7 @@ - +
diff --git a/html/es/addProperty.html b/html/es/addProperty.html index 9f98f8d..44ad65e 100644 --- a/html/es/addProperty.html +++ b/html/es/addProperty.html @@ -7,7 +7,7 @@ - +
diff --git a/html/es/attributes.html b/html/es/attributes.html index 958bd96..dfa543f 100644 --- a/html/es/attributes.html +++ b/html/es/attributes.html @@ -1,5 +1,5 @@ - +Atributos de la Unidad de Traducción @@ -7,85 +7,84 @@ - + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + +
+ + + + + + + + +
+ + + + + + + + +
+ + + + + +  
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - -
- - - - - - - - -
- - - - - - - - -
- - - - - - - - -
- - - - - -  
-
diff --git a/html/es/changeLanguage.html b/html/es/changeLanguage.html index 8b57afa..0a6221c 100644 --- a/html/es/changeLanguage.html +++ b/html/es/changeLanguage.html @@ -1,5 +1,5 @@ - + Cambiar Idioma @@ -7,7 +7,7 @@ - + diff --git a/html/es/consolidate.html b/html/es/consolidate.html index b17bbd9..ee8357c 100644 --- a/html/es/consolidate.html +++ b/html/es/consolidate.html @@ -1,5 +1,5 @@ - +Consolidar Unidades @@ -7,7 +7,7 @@ - +
diff --git a/html/es/convertCSV.html b/html/es/convertCSV.html index 882a720..9a6579d 100644 --- a/html/es/convertCSV.html +++ b/html/es/convertCSV.html @@ -1,5 +1,5 @@ - +Convertir Archivo CSV a TMX @@ -7,7 +7,7 @@ - +
@@ -72,7 +72,6 @@ - diff --git a/html/es/convertExcel.html b/html/es/convertExcel.html index 22ce198..a4d99a1 100644 --- a/html/es/convertExcel.html +++ b/html/es/convertExcel.html @@ -1,5 +1,5 @@ - +Convertir Archivo Excel a TMX @@ -7,7 +7,7 @@ - +
@@ -42,7 +42,6 @@ - diff --git a/html/es/convertSDLTM.html b/html/es/convertSDLTM.html index 63d03a5..66dcfc6 100644 --- a/html/es/convertSDLTM.html +++ b/html/es/convertSDLTM.html @@ -1,5 +1,5 @@ - +Convertir Archivo SDLTM a TMX @@ -7,7 +7,7 @@ - +
@@ -28,7 +28,6 @@ - diff --git a/html/es/convertTBX.html b/html/es/convertTBX.html index 2d6405c..63178a3 100644 --- a/html/es/convertTBX.html +++ b/html/es/convertTBX.html @@ -1,5 +1,5 @@ - +Convertir Archivo TBX a TMX @@ -7,7 +7,7 @@ - +
@@ -28,7 +28,6 @@ - diff --git a/html/es/csvLanguages.html b/html/es/csvLanguages.html index f3c54ab..45724b7 100644 --- a/html/es/csvLanguages.html +++ b/html/es/csvLanguages.html @@ -1,5 +1,5 @@ - +Idiomas del Archivo CSV/Texto @@ -7,7 +7,7 @@ - +
diff --git a/html/es/excelLanguages.html b/html/es/excelLanguages.html index 56392b7..aed1e36 100644 --- a/html/es/excelLanguages.html +++ b/html/es/excelLanguages.html @@ -1,5 +1,5 @@ - + Idiomas del Archivo Excel @@ -7,7 +7,7 @@ - +
diff --git a/html/es/fileInfo.html b/html/es/fileInfo.html index de0e7a3..1dad784 100644 --- a/html/es/fileInfo.html +++ b/html/es/fileInfo.html @@ -9,7 +9,7 @@ -
+
@@ -22,63 +22,115 @@
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Herramienta de Creación
Versión de Herramienta de Creación
Tipo de Segmentación
Formato de Memoria de Traducción Original
Idioma Administrativo
Idioma Origen
Tipo de Dato
-
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + + + +
+ + + + + +
+ + + + + +
+ + + + + +
+ + + + + +
+
+ +
+ + + + + diff --git a/html/es/filters.html b/html/es/filters.html index 54ab6f4..ed05913 100644 --- a/html/es/filters.html +++ b/html/es/filters.html @@ -1,5 +1,5 @@ - + Filtrar Unidades de Traducción @@ -7,7 +7,7 @@ - + @@ -52,7 +52,6 @@

Opciones de Filtrado

- diff --git a/html/es/licenses.html b/html/es/licenses.html index faa3183..910bc95 100644 --- a/html/es/licenses.html +++ b/html/es/licenses.html @@ -1,5 +1,5 @@ - +Licencias @@ -7,15 +7,15 @@ - -
+ +
- - + + @@ -29,18 +29,11 @@ - - - - - - - - - + + @@ -52,6 +45,10 @@ + + + + diff --git a/html/es/maintenance.html b/html/es/maintenance.html index 6eb824c..4953ac7 100644 --- a/html/es/maintenance.html +++ b/html/es/maintenance.html @@ -7,7 +7,7 @@ - +
TMXEditor Eclipse Public License 1.0
DTDParserLGPL 2.1BCP47JEclipse Public License 1.0
ElectronJSON JSON.org
jsoupMIT License
OpenXLIFF FiltersEclipse Public License 1.0
sdltm Eclipse Public License 1.0
SLF4J MIT License
TMXValidator Eclipse Public License 1.0
TypesXMLEclipse Public License 1.0
XMLJava Eclipse Public License 1.0
@@ -23,7 +23,7 @@ @@ -39,7 +39,7 @@ diff --git a/html/es/mergeFiles.html b/html/es/mergeFiles.html index 5d69539..1040ca2 100644 --- a/html/es/mergeFiles.html +++ b/html/es/mergeFiles.html @@ -1,5 +1,5 @@ - +Combinar Archivos TMX @@ -7,7 +7,7 @@ - +
- +
- +
diff --git a/html/es/messages.html b/html/es/messages.html index 51fabf4..d3dcab2 100644 --- a/html/es/messages.html +++ b/html/es/messages.html @@ -7,7 +7,7 @@ - +
@@ -26,7 +26,6 @@
- diff --git a/html/es/newFile.html b/html/es/newFile.html index cc57b46..86e95e8 100644 --- a/html/es/newFile.html +++ b/html/es/newFile.html @@ -1,5 +1,5 @@ - + Nuevo Archivo TMX @@ -7,7 +7,7 @@ - + ' + setAdminLanguages(langs: Language[]) { + let adminLangSelect: HTMLSelectElement = document.getElementById('adminlang') as HTMLSelectElement; + adminLangSelect.innerHTML = ''; + for (let lang of langs) { + let option: HTMLOptionElement = document.createElement('option'); + option.value = lang.code; + option.text = lang.name; + adminLangSelect.appendChild(option); } - document.getElementById('propertiesTable').innerHTML = propsContent; + adminLangSelect.value = this.adminLang; - let notes: string[] = arg.notes; - let notesContent: string = ''; - for (let note of notes) { - notesContent = notesContent + '' - } - document.getElementById('notesTable').innerHTML = notesContent; setTimeout(() => { - this.electron.ipcRenderer.send('fileInfo-height', { width: document.body.clientWidth, height: document.body.clientHeight}); + this.electron.ipcRenderer.send('fileInfo-height', { width: document.body.clientWidth, height: document.body.clientHeight }); + document.getElementById('properties').style.height = document.getElementById('attributes').clientHeight + 'px'; + document.getElementById('notes').style.height = document.getElementById('attributes').clientHeight + 'px'; + document.getElementById('attributes').style.height = document.getElementById('attributes').clientHeight + 'px'; }, 150); } + setFileProperties(arg: { attributes: any, fileLanguages: Language[], properties: Array, notes: string[] }): void { + let srcLangSelect: HTMLSelectElement = document.getElementById('srclang') as HTMLSelectElement; + srcLangSelect.innerHTML = ''; + let srcLangs: Language[] = arg.fileLanguages; + for (let lang of srcLangs) { + let option: HTMLOptionElement = document.createElement('option'); + option.value = lang.code; + option.text = lang.name; + srcLangSelect.appendChild(option); + } + srcLangSelect.value = arg.attributes.srclang; + this.adminLang = arg.attributes.adminlang + + this.electron.ipcRenderer.send('all-languages'); + + (document.getElementById('creationid') as HTMLInputElement).value = arg.attributes.creationid; + (document.getElementById('creationdate') as HTMLInputElement).value = arg.attributes.creationdate; + (document.getElementById('creationtool') as HTMLInputElement).value = arg.attributes.creationtool; + (document.getElementById('creationtoolversion') as HTMLInputElement).value = arg.attributes.creationtoolversion; + (document.getElementById('segtype') as HTMLSelectElement).value = arg.attributes.segtype; + (document.getElementById('o-tmf') as HTMLInputElement).value = arg.attributes.o_tmf; + (document.getElementById('datatype') as HTMLInputElement).value = arg.attributes.datatype; + (document.getElementById('changedate') as HTMLInputElement).value = arg.attributes.changedate; + (document.getElementById('changeid') as HTMLInputElement).value = arg.attributes.changeid; + (document.getElementById('o-encoding') as HTMLInputElement).value = arg.attributes.o_encoding; + + this.properties = arg.properties; + this.drawProperties(); + + this.notes = arg.notes; + this.drawNotes(); + } + + drawProperties(): void { + let propertiesTableBody: HTMLTableSectionElement = document.getElementById('propertiesBody') as HTMLTableSectionElement; + propertiesTableBody.innerHTML = ''; + this.propertiesChecks = []; + for (let pair of this.properties) { + let tr: HTMLTableRowElement = document.createElement('tr'); + propertiesTableBody.appendChild(tr); + let td: HTMLTableCellElement = document.createElement('td'); + td.classList.add('middle'); + tr.appendChild(td); + let checkbox: HTMLInputElement = document.createElement('input'); + checkbox.type = 'checkbox'; + checkbox.id = pair[0]; + td.appendChild(checkbox); + this.propertiesChecks.push(checkbox); + td = document.createElement('td'); + td.classList.add('middle'); + td.classList.add('noWrap'); + td.classList.add('leftBorder'); + td.innerHTML = pair[0]; + tr.appendChild(td); + td = document.createElement('td'); + td.innerHTML = pair[1]; + td.classList.add('middle'); + td.classList.add('fill_width'); + td.classList.add('leftBorder'); + tr.appendChild(td); + tr.addEventListener('click', (event: MouseEvent) => { + if ((event.target as HTMLElement).tagName !== 'INPUT') { + let checkbox: HTMLInputElement = document.getElementById(pair[0]) as HTMLInputElement; + checkbox.checked = !checkbox.checked; + } + }); + } + } + + drawNotes() { + let notesTableBody: HTMLTableSectionElement = document.getElementById('notesTable') as HTMLTableSectionElement; + notesTableBody.innerHTML = ''; + this.notesChecks = []; + for (let i: number = 0; i < this.notes.length; i++) { + let tr: HTMLTableRowElement = document.createElement('tr'); + notesTableBody.appendChild(tr); + let td: HTMLTableCellElement = document.createElement('td'); + td.classList.add('middle'); + tr.appendChild(td); + let checkbox: HTMLInputElement = document.createElement('input'); + checkbox.type = 'checkbox'; + checkbox.id = i.toString(); + td.appendChild(checkbox); + this.notesChecks.push(checkbox); + td = document.createElement('td'); + td.classList.add('middle'); + td.classList.add('noWrap'); + td.classList.add('fill_width'); + td.innerText = this.notes[i]; + tr.appendChild(td); + } + } + showAttributes(): void { document.getElementById('atributesTab').classList.add('selectedTab'); document.getElementById('attributes').classList.remove('hidden'); @@ -78,6 +201,15 @@ class FileInfo { document.getElementById('notesTab').classList.remove('selectedTab'); document.getElementById('notes').classList.remove('tabContent'); document.getElementById('notes').classList.add('hidden'); + + document.getElementById('attributesButtons').classList.add('buttonArea'); + document.getElementById('attributesButtons').classList.remove('hidden'); + + document.getElementById('propButtons').classList.add('hidden'); + document.getElementById('propButtons').classList.remove('buttonArea'); + + document.getElementById('notesButtons').classList.add('hidden'); + document.getElementById('notesButtons').classList.remove('buttonArea'); } showProperties(): void { @@ -92,6 +224,15 @@ class FileInfo { document.getElementById('notesTab').classList.remove('selectedTab'); document.getElementById('notes').classList.remove('tabContent'); document.getElementById('notes').classList.add('hidden'); + + document.getElementById('attributesButtons').classList.remove('buttonArea'); + document.getElementById('attributesButtons').classList.add('hidden'); + + document.getElementById('propButtons').classList.remove('hidden'); + document.getElementById('propButtons').classList.add('buttonArea'); + + document.getElementById('notesButtons').classList.add('hidden'); + document.getElementById('notesButtons').classList.remove('buttonArea'); } showNotes(): void { @@ -106,5 +247,99 @@ class FileInfo { document.getElementById('atributesTab').classList.remove('selectedTab'); document.getElementById('attributes').classList.remove('tabContent'); document.getElementById('attributes').classList.add('hidden'); + + document.getElementById('attributesButtons').classList.remove('buttonArea'); + document.getElementById('attributesButtons').classList.add('hidden'); + + document.getElementById('propButtons').classList.add('hidden'); + document.getElementById('propButtons').classList.remove('buttonArea'); + + document.getElementById('notesButtons').classList.remove('hidden'); + document.getElementById('notesButtons').classList.add('buttonArea'); + } + + saveAttributes(): void { + let creationid: string = (document.getElementById('creationid') as HTMLInputElement).value; + let creationdate: string = (document.getElementById('creationdate') as HTMLInputElement).value; + let creationtool: string = (document.getElementById('creationtool') as HTMLInputElement).value; + let creationtoolversion: string = (document.getElementById('creationtoolversion') as HTMLInputElement).value; + let changeid: string = (document.getElementById('changeid') as HTMLInputElement).value; + let changedate: string = (document.getElementById('changedate') as HTMLInputElement).value; + let segtype: string = (document.getElementById('segtype') as HTMLSelectElement).value; + let o_tmf: string = (document.getElementById('o-tmf') as HTMLInputElement).value; + let srclang: string = (document.getElementById('srclang') as HTMLSelectElement).value; + let adminlang: string = (document.getElementById('adminlang') as HTMLSelectElement).value; + let datatype: string = (document.getElementById('datatype') as HTMLInputElement).value; + let o_encoding: string = (document.getElementById('o-encoding') as HTMLInputElement).value; + + // required: creationtool, creationtoolversion, segtype, o-tmf, adminlang, srclang, datatype. + if (creationtool === '') { + this.electron.ipcRenderer.send('show-message', { parent: 'fileInfo', group: 'fileInfo', key: 'creationtool' }); + return; + } + if (creationtoolversion === '') { + this.electron.ipcRenderer.send('show-message', { parent: 'fileInfo', group: 'fileInfo', key: 'creationtoolversion' }); + return; + } + if (segtype === '') { + this.electron.ipcRenderer.send('show-message', { parent: 'fileInfo', group: 'fileInfo', key: 'segtype' }); + return; + } + if (o_tmf === '') { + this.electron.ipcRenderer.send('show-message', { parent: 'fileInfo', group: 'fileInfo', key: 'o-tmf' }); + return; + } + if (adminlang === '' || adminlang === 'none') { + this.electron.ipcRenderer.send('show-message', { parent: 'fileInfo', group: 'fileInfo', key: 'adminlang' }); + return; + } + if (srclang === '') { + this.electron.ipcRenderer.send('show-message', { parent: 'fileInfo', group: 'fileInfo', key: 'srclang' }); + return; + } + if (datatype === '') { + this.electron.ipcRenderer.send('show-message', { parent: 'fileInfo', group: 'fileInfo', key: 'datatype' }); + return; + } + this.electron.ipcRenderer.send('save-file-attributes', { + creationid: creationid, + creationdate: creationdate, + creationtool: creationtool, + creationtoolversion: creationtoolversion, + changeid: changeid, + changedate: changedate, + segtype: segtype, + o_tmf: o_tmf, + srclang: srclang, + adminlang: adminlang, + datatype: datatype, + o_encoding: o_encoding + }); + } + + deleteProperties(): void { + for (let i: number = 0; i < this.propertiesChecks.length; i++) { + if (this.propertiesChecks[i].checked) { + this.properties.splice(i, 1); + } + } + this.drawProperties(); + } + + saveProperties(): void { + this.electron.ipcRenderer.send('save-file-properties', this.properties); + } + + deleteNotes(): void { + for (let i: number = 0; i < this.notesChecks.length; i++) { + if (this.notesChecks[i].checked) { + this.notes.splice(i, 1); + } + } + this.drawNotes(); + } + + saveNotes(): void { + this.electron.ipcRenderer.send('save-file-notes', this.notes); } } diff --git a/ts/filters.ts b/ts/filters.ts index 1a8ac89..256115e 100644 --- a/ts/filters.ts +++ b/ts/filters.ts @@ -46,7 +46,6 @@ class Filters { document.getElementById('clearFilters').addEventListener('click', () => { this.clearFilters(); }); - document.addEventListener('keydown', (event: KeyboardEvent) => { KeyboardHandler.keyListener(event); }); document.addEventListener('keydown', (event: KeyboardEvent) => { if (event.code === 'Enter' || event.code === 'NumpadEnter') { this.applyFilters(); diff --git a/ts/main.ts b/ts/main.ts index 429e8f3..5b18ed0 100644 --- a/ts/main.ts +++ b/ts/main.ts @@ -177,6 +177,11 @@ class Main { }); this.topBar.appendChild(saveFile); + let span1: HTMLSpanElement = document.createElement('span'); + span1.style.width = '30px'; + span1.innerHTML = ' '; + this.topBar.appendChild(span1); + let showFileInfo: HTMLAnchorElement = document.createElement('a'); showFileInfo.classList.add('tooltip'); showFileInfo.innerHTML = '' + @@ -186,7 +191,7 @@ class Main { }); this.topBar.appendChild(showFileInfo); - let span1: HTMLSpanElement = document.createElement('span'); + span1 = document.createElement('span'); span1.style.width = '30px'; span1.innerHTML = ' '; this.topBar.appendChild(span1); @@ -453,7 +458,13 @@ class Main { document.getElementById('nextPage').innerText = tooltips.nextPage; document.getElementById('lastPage').innerText = tooltips.lastPage; document.getElementById('unitsPage').innerText = tooltips.unitsPage; + document.getElementById('pageSpan').innerText = tooltips.pageSpan; + document.getElementById('ofSpan').innerText = tooltips.ofSpan; + document.getElementById('unitsLabel').innerText = tooltips.unitsLabel; document.getElementById('unitsTooltip').innerText = tooltips.unitsTooltip; + document.getElementById('attributesSpan').innerHTML = tooltips.tuAttributes; + document.getElementById('propertiesSpan').innerHTML = tooltips.tuProperties; + document.getElementById('notesSpan').innerHTML = tooltips.tuNotes; } buildRightPanels(rightPanel: HTMLDivElement): void { @@ -500,7 +511,7 @@ class Main { tableRow.appendChild(attributesCell); let cell: HTMLTableCellElement = document.createElement('td'); - cell.innerText = 'Attributes'; + cell.innerHTML = ' '; cell.classList.add('fill_width'); cell.style.marginLeft = '4px'; tableRow.appendChild(cell); @@ -562,7 +573,7 @@ class Main { tableRow.appendChild(propertiesCell); let cell: HTMLTableCellElement = document.createElement('td'); - cell.innerText = 'Properties'; + cell.innerHTML = ' '; cell.classList.add('fill_width'); cell.style.marginLeft = '4px'; tableRow.appendChild(cell); @@ -623,7 +634,7 @@ class Main { tableRow.appendChild(notesCell); let cell: HTMLTableCellElement = document.createElement('td'); - cell.innerText = 'Notes'; + cell.innerHTML = ' '; cell.classList.add('fill_width'); cell.style.marginLeft = '4px'; tableRow.appendChild(cell); @@ -687,6 +698,7 @@ class Main { this.bottomBar.appendChild(previous); let span: HTMLSpanElement = document.createElement('span'); + span.id = 'pageSpan'; span.innerText = 'Page'; span.style.marginLeft = '10px'; span.style.marginTop = '4px'; @@ -715,6 +727,7 @@ class Main { pageDiv.appendChild(pageTooltip); let ofSpan: HTMLSpanElement = document.createElement('span'); + ofSpan.id = 'ofSpan'; ofSpan.innerText = 'of '; ofSpan.style.marginLeft = '10px'; ofSpan.style.marginTop = '4px'; @@ -777,6 +790,7 @@ class Main { unitsDiv.appendChild(unitsTooltip); let unitsLabel: HTMLSpanElement = document.createElement('span'); + unitsLabel.id = 'unitsLabel'; unitsLabel.innerText = 'Units: '; unitsLabel.style.marginLeft = '10px'; unitsLabel.style.marginTop = '4px'; @@ -811,7 +825,7 @@ class Main { if (!this.isLoaded) { return; } - if (this.currentCell != null && this.currentCell.isContentEditable) { + if (this.currentCell?.isContentEditable) { if (this.currentContent === this.currentCell.innerHTML) { this.cancelEdit(); return; @@ -1011,11 +1025,8 @@ class Main { document.getElementById('pages').innerHTML = '0'; document.getElementById('units').innerHTML = ''; document.getElementById('attributesTable').innerHTML = ''; - document.getElementById('attributesSpan').innerHTML = 'TU'; document.getElementById('propertiesTable').innerHTML = ''; - document.getElementById('propertiesSpan').innerHTML = 'TU'; document.getElementById('notesTable').innerHTML = ''; - document.getElementById('notesSpan').innerHTML = 'TU'; document.getElementById('filterUnits').classList.remove('active'); document.getElementById('sortUnits').classList.remove('active'); document.getElementById('selectAll').addEventListener('click', () => { @@ -1051,11 +1062,8 @@ class Main { fixed[i].addEventListener('click', (ev: MouseEvent) => this.fixedListener(ev)); } document.getElementById('attributesTable').innerHTML = ''; - document.getElementById('attributesSpan').innerHTML = 'TU'; document.getElementById('propertiesTable').innerHTML = ''; - document.getElementById('propertiesSpan').innerHTML = 'TU'; document.getElementById('notesTable').innerHTML = ''; - document.getElementById('notesSpan').innerHTML = 'TU'; this.currentId = undefined; this.setStatus(''); } @@ -1064,7 +1072,7 @@ class Main { if (!this.isLoaded) { return; } - if (this.currentCell != null && this.currentCell.isContentEditable) { + if (this.currentCell?.isContentEditable) { this.saveEdit(); } let element: Element = (event.target as Element); @@ -1157,7 +1165,7 @@ class Main { updateProperties(arg: any): void { this.attributesType = arg.type; - document.getElementById('attributesSpan').innerHTML = this.attributesType; + document.getElementById('attributesSpan').innerHTML = arg.attributesType; let table = document.getElementById('attributesTable'); table.innerHTML = ''; this.attributes = arg.attributes; @@ -1173,10 +1181,12 @@ class Main { let right = document.createElement('td'); right.textContent = pair[1]; right.className = 'noWrap'; + right.classList.add('leftBorder'); + right.classList.add('fill_width'); tr.appendChild(right); } - document.getElementById('propertiesSpan').innerHTML = arg.type; + document.getElementById('propertiesSpan').innerHTML = arg.propertiesType; table = document.getElementById('propertiesTable'); table.innerHTML = ''; this.properties = arg.properties; @@ -1192,10 +1202,12 @@ class Main { let right = document.createElement('td'); right.textContent = pair[1]; right.className = 'noWrap'; + right.classList.add('leftBorder'); + right.classList.add('fill_width'); tr.appendChild(right); } - document.getElementById('notesSpan').innerHTML = arg.type; + document.getElementById('notesSpan').innerHTML = arg.notesType; table = document.getElementById('notesTable'); table.innerHTML = ''; this.notes = arg.notes; @@ -1223,7 +1235,7 @@ class Main { } firstPage(): void { - if (this.currentCell && this.currentCell.isContentEditable) { + if (this.currentCell?.isContentEditable) { this.saveEdit(); } this.currentPage = 0; @@ -1233,7 +1245,7 @@ class Main { previousPage(): void { if (this.currentPage > 0) { - if (this.currentCell && this.currentCell.isContentEditable) { + if (this.currentCell?.isContentEditable) { this.saveEdit(); } this.currentPage--; @@ -1244,7 +1256,7 @@ class Main { nextPage(): void { if (this.currentPage < this.maxPage - 1) { - if (this.currentCell && this.currentCell.isContentEditable) { + if (this.currentCell?.isContentEditable) { this.saveEdit(); } this.currentPage++; @@ -1254,7 +1266,7 @@ class Main { } lastPage(): void { - if (this.currentCell && this.currentCell.isContentEditable) { + if (this.currentCell?.isContentEditable) { this.saveEdit(); } this.currentPage = this.maxPage - 1; @@ -1277,7 +1289,7 @@ class Main { (document.getElementById('units_page') as HTMLInputElement).value = '' + this.unitsPage; this.maxPage = Math.ceil(this.unitsCount / this.unitsPage); document.getElementById('pages').innerText = '' + this.maxPage; - if (this.currentCell && this.currentCell.isContentEditable) { + if (this.currentCell?.isContentEditable) { this.saveEdit(); } this.firstPage(); @@ -1296,7 +1308,7 @@ class Main { if (this.currentPage > this.maxPage - 1) { this.currentPage = this.maxPage - 1; } - if (this.currentCell && this.currentCell.isContentEditable) { + if (this.currentCell?.isContentEditable) { this.saveEdit(); } (document.getElementById('page') as HTMLInputElement).value = '' + (this.currentPage + 1); diff --git a/ts/mergeFiles.ts b/ts/mergeFiles.ts index 5ccc20d..89b69b7 100644 --- a/ts/mergeFiles.ts +++ b/ts/mergeFiles.ts @@ -48,7 +48,7 @@ class MergeFiles { } }); document.getElementById('file').focus(); - this.electron.ipcRenderer.send('mergeFiles-height', { width: document.body.clientWidth, height: document.body.clientHeight + 10 }); + this.electron.ipcRenderer.send('mergeFiles-height', { width: document.body.clientWidth, height: document.body.clientHeight }); } mergeFiles(): void { @@ -82,7 +82,9 @@ class MergeFiles { this.files.push(arg); } } - this.files.sort(); + this.files.sort((a: string, b: string) => { + return a.localeCompare(b, document.documentElement.lang, { sensitivity: 'base' }); + }); let rows: string = ''; for (let file of this.files) { rows = rows + ''; @@ -118,7 +120,9 @@ class MergeFiles { array.push(file); } } - array.sort(); + array.sort((a: string, b: string) => { + return a.localeCompare(b, document.documentElement.lang, { sensitivity: 'base' }); + }); this.files = array; let rows: string = ''; for (let file of this.files) { diff --git a/ts/notes.ts b/ts/notes.ts index 2b1218a..7606e8f 100644 --- a/ts/notes.ts +++ b/ts/notes.ts @@ -30,8 +30,8 @@ class Notes { this.notes = arg.notes; this.drawNotes(); }); - this.electron.ipcRenderer.on('set-new-note', (event: Electron.IpcRendererEvent, arg: any) => { - this.notes.push(arg.note); + this.electron.ipcRenderer.on('set-new-note', (event: Electron.IpcRendererEvent, note: string) => { + this.notes.push(note); this.drawNotes(); (document.getElementById('save') as HTMLButtonElement).focus(); }); diff --git a/ts/preferences.ts b/ts/preferences.ts index c34d96e..cf73300 100644 --- a/ts/preferences.ts +++ b/ts/preferences.ts @@ -10,48 +10,10 @@ * Maxprograms - initial API and implementation *******************************************************************************/ -class Preferences { +interface Preferences { - electron = require("electron"); - - constructor() { - this.electron.ipcRenderer.send('get-theme'); - this.electron.ipcRenderer.on('set-theme', (event: Electron.IpcRendererEvent, arg: any) => { - (document.getElementById('theme') as HTMLLinkElement).href = arg; - this.electron.ipcRenderer.send('get-appLanguage'); - }); - this.electron.ipcRenderer.on('set-appLanguage', (event: Electron.IpcRendererEvent, arg: any) => { - (document.getElementById('appLangSelect') as HTMLSelectElement).value = arg; - }); - this.electron.ipcRenderer.on('set-preferences', (event: Electron.IpcRendererEvent, arg: any) => { - this.setPreferences(arg); - }); - this.electron.ipcRenderer.send('get-preferences'); - document.getElementById('savePreferences').addEventListener('click', () => { - this.savePreferences(); - }); - document.addEventListener('keydown', (event: KeyboardEvent) => { - if (event.code === 'Enter' || event.code === 'NumpadEnter') { - this.savePreferences(); - } - if (event.code === 'Escape') { - this.electron.ipcRenderer.send('close-preferences'); - } - }); - document.getElementById('appLangSelect').focus(); - this.electron.ipcRenderer.send('preferences-height', { width: document.body.clientWidth, height: document.body.clientHeight }); - } - - setPreferences(arg: any): void { - (document.getElementById('themeColor') as HTMLSelectElement).value = arg.theme; - (document.getElementById('indentation') as HTMLInputElement).value = '' + arg.indentation; - } - - savePreferences(): void { - let theme: string = (document.getElementById('themeColor') as HTMLSelectElement).value; - let indent: number = Number.parseInt((document.getElementById('indentation') as HTMLInputElement).value); - let appLang: string = (document.getElementById('appLangSelect') as HTMLSelectElement).value; - let prefs: any = { theme: theme, indentation: indent, appLang: appLang } - this.electron.ipcRenderer.send('save-preferences', prefs); - } -} + theme: string; + indentation: number; + appLang: string; + changeId: boolean; +} \ No newline at end of file diff --git a/ts/preferencesDialog.ts b/ts/preferencesDialog.ts new file mode 100644 index 0000000..1920298 --- /dev/null +++ b/ts/preferencesDialog.ts @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2018-2024 Maxprograms. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-v10.html + * + * Contributors: + * Maxprograms - initial API and implementation + *******************************************************************************/ + +class PreferencesDialog { + + electron = require("electron"); + + constructor() { + this.electron.ipcRenderer.send('get-theme'); + this.electron.ipcRenderer.on('set-theme', (event: Electron.IpcRendererEvent, arg: any) => { + (document.getElementById('theme') as HTMLLinkElement).href = arg; + this.electron.ipcRenderer.send('get-preferences'); + }); + this.electron.ipcRenderer.on('set-preferences', (event: Electron.IpcRendererEvent, arg: Preferences) => { + this.setPreferences(arg); + }); + document.getElementById('savePreferences').addEventListener('click', () => { + this.savePreferences(); + }); + document.addEventListener('keydown', (event: KeyboardEvent) => { + if (event.code === 'Enter' || event.code === 'NumpadEnter') { + this.savePreferences(); + } + if (event.code === 'Escape') { + this.electron.ipcRenderer.send('close-preferences'); + } + }); + document.getElementById('appLangSelect').focus(); + setTimeout(() => { + this.electron.ipcRenderer.send('preferences-height', { width: document.body.clientWidth, height: document.body.clientHeight }); + }, 200); + } + + setPreferences(arg: Preferences): void { + (document.getElementById('themeColor') as HTMLSelectElement).value = arg.theme; + (document.getElementById('indentation') as HTMLInputElement).value = '' + arg.indentation; + (document.getElementById('appLangSelect') as HTMLSelectElement).value = arg.appLang; + (document.getElementById('changeId') as HTMLInputElement).checked = arg.changeId; + } + + savePreferences(): void { + let theme: string = (document.getElementById('themeColor') as HTMLSelectElement).value; + let indent: number = Number.parseInt((document.getElementById('indentation') as HTMLInputElement).value); + let appLang: string = (document.getElementById('appLangSelect') as HTMLSelectElement).value; + let changeId: boolean = (document.getElementById('changeId') as HTMLInputElement).checked; + let prefs: Preferences = { theme: theme, indentation: indent, appLang: appLang, changeId: changeId }; + this.electron.ipcRenderer.send('save-preferences', prefs); + } +} diff --git a/ts/properties.ts b/ts/properties.ts index 26068b0..4d67eab 100644 --- a/ts/properties.ts +++ b/ts/properties.ts @@ -66,7 +66,7 @@ class Properties { } addProperty(): void { - this.electron.ipcRenderer.send('show-add-property'); + this.electron.ipcRenderer.send('show-add-property', 'properties'); } setNewProperty(arg: any): void { @@ -80,7 +80,7 @@ class Properties { let collection: HTMLCollectionOf = document.getElementsByClassName('rowCheck'); for (let check of collection) { if ((check as HTMLInputElement).checked) { - this.removeProperty(check.parentElement.parentElement.id); + this.removeProperty(check.id); } } this.drawProperties(); @@ -88,11 +88,36 @@ class Properties { } drawProperties(): void { - let rows: string = ''; + let propsTable: HTMLTableElement = document.getElementById('propsTable') as HTMLTableElement; + propsTable.innerHTML = ''; for (let pair of this.props) { - rows = rows + ''; + let tr: HTMLTableRowElement = document.createElement('tr'); + propsTable.appendChild(tr); + let td: HTMLTableCellElement = document.createElement('td'); + td.classList.add('middle'); + tr.appendChild(td); + let checkbox: HTMLInputElement = document.createElement('input'); + checkbox.type = 'checkbox'; + checkbox.classList.add('rowCheck'); + checkbox.id = pair[0]; + td.appendChild(checkbox); + td = document.createElement('td'); + td.classList.add('middle'); + td.classList.add('noWrap'); + td.innerHTML = pair[0]; + tr.appendChild(td); + td = document.createElement('td'); + td.classList.add('middle'); + td.classList.add('fill_width'); + td.innerHTML = pair[1]; + tr.appendChild(td); + tr.addEventListener('click', (event: MouseEvent) => { + if ((event.target as HTMLElement).tagName !== 'INPUT') { + let checkbox: HTMLInputElement = document.getElementById(pair[0]) as HTMLInputElement; + checkbox.checked = !checkbox.checked; + } + }); } - document.getElementById('propsTable').innerHTML = rows; } removeProperty(type: string): void { diff --git a/ts/searchReplace.ts b/ts/searchReplace.ts index 926c967..b9e0b5f 100644 --- a/ts/searchReplace.ts +++ b/ts/searchReplace.ts @@ -23,8 +23,7 @@ class SearchReplace { this.electron.ipcRenderer.on('file-languages', (event: Electron.IpcRendererEvent, arg: Language[]) => { this.filterLanguages(arg); }); - document.addEventListener('keydown', (event: KeyboardEvent) => { KeyboardHandler.keyListener(event); }); - + document.getElementById('replace').addEventListener('click', () => { this.replace(); }); diff --git a/ts/systemInfo.ts b/ts/systemInfo.ts index 97b1c63..45525e6 100644 --- a/ts/systemInfo.ts +++ b/ts/systemInfo.ts @@ -29,7 +29,7 @@ class SystemInformation { this.electron.ipcRenderer.send('close-systemInfo'); } }); - this.electron.ipcRenderer.send('systemInfo-height', { width: document.body.clientWidth, height: (document.body.clientHeight) }); + } setInfo(info: any) { @@ -38,5 +38,6 @@ class SystemInformation { document.getElementById('xmljava').innerText = info.xmljava; document.getElementById('java').innerText = info.java; document.getElementById('electron').innerText = info.electron; + this.electron.ipcRenderer.send('systemInfo-height', { width: document.body.clientWidth, height: document.body.clientHeight }); } } diff --git a/ts/updates.ts b/ts/updates.ts index c6bceb2..36caad6 100644 --- a/ts/updates.ts +++ b/ts/updates.ts @@ -25,7 +25,6 @@ class Updates { document.getElementById('latest').innerText = arg.latest; this.electron.ipcRenderer.send('updates-height', { width: document.body.clientWidth, height: document.body.clientHeight }); }); - document.addEventListener('keydown', (event: KeyboardEvent) => { KeyboardHandler.keyListener(event); }); document.addEventListener('keydown', (event: KeyboardEvent) => { if (event.code === 'Enter' || event.code === 'NumpadEnter') { this.electron.ipcRenderer.send('download-latest');
diff --git a/html/es/notes.html b/html/es/notes.html index 0913bbc..fab5c0f 100644 --- a/html/es/notes.html +++ b/html/es/notes.html @@ -1,5 +1,5 @@ - + Notas de Unidad de Traducción @@ -7,7 +7,7 @@ - +
diff --git a/html/es/preferences.html b/html/es/preferences.html index 9dd086f..c900b73 100644 --- a/html/es/preferences.html +++ b/html/es/preferences.html @@ -1,5 +1,5 @@ - +Preferencias @@ -7,10 +7,10 @@ - +
- +
espacios
- +
+ + +
- + diff --git a/html/es/properties.html b/html/es/properties.html index ae119ee..05dbfdd 100644 --- a/html/es/properties.html +++ b/html/es/properties.html @@ -1,5 +1,5 @@ - + Propiedades de Unidad de Traducción @@ -7,7 +7,7 @@ - +
diff --git a/html/es/removeLanguage.html b/html/es/removeLanguage.html index 6141a13..baa1b02 100644 --- a/html/es/removeLanguage.html +++ b/html/es/removeLanguage.html @@ -1,5 +1,5 @@ - +Eliminar Idioma @@ -7,7 +7,7 @@ - +
diff --git a/html/es/removeSameAsSource.html b/html/es/removeSameAsSource.html index 3bfe1fc..11e0476 100644 --- a/html/es/removeSameAsSource.html +++ b/html/es/removeSameAsSource.html @@ -1,5 +1,5 @@ - +Eliminar Traducciones Iguales a Origen @@ -7,7 +7,7 @@ - +
diff --git a/html/es/removeUntranslated.html b/html/es/removeUntranslated.html index bc5773b..cc46378 100644 --- a/html/es/removeUntranslated.html +++ b/html/es/removeUntranslated.html @@ -1,5 +1,5 @@ - +Eliminar Unidades No Traducidas @@ -7,7 +7,7 @@ - +
diff --git a/html/es/searchReplace.html b/html/es/searchReplace.html index b94a7a0..e688e46 100644 --- a/html/es/searchReplace.html +++ b/html/es/searchReplace.html @@ -1,5 +1,5 @@ - +Reemplazar Texto @@ -7,7 +7,7 @@ - +
@@ -29,13 +29,12 @@
- +
- diff --git a/html/es/sortUnits.html b/html/es/sortUnits.html index 6068b28..e648c33 100644 --- a/html/es/sortUnits.html +++ b/html/es/sortUnits.html @@ -1,5 +1,5 @@ - +Ordenar Unidades de Traducción @@ -7,7 +7,7 @@ - +
diff --git a/html/es/splitFile.html b/html/es/splitFile.html index 7121b75..e1d9e98 100644 --- a/html/es/splitFile.html +++ b/html/es/splitFile.html @@ -1,5 +1,5 @@ - +Dividir Archivo TMX @@ -7,7 +7,7 @@ - +
diff --git a/html/es/srcLanguage.html b/html/es/srcLanguage.html index bfd220b..fc74dc5 100644 --- a/html/es/srcLanguage.html +++ b/html/es/srcLanguage.html @@ -1,5 +1,5 @@ - +Cambiar Idioma Origen @@ -7,7 +7,7 @@ - +
diff --git a/html/es/systemInfo.html b/html/es/systemInfo.html index 4676a03..e6a875e 100644 --- a/html/es/systemInfo.html +++ b/html/es/systemInfo.html @@ -7,31 +7,29 @@ - - -
+ +
- - - - - - + + + + + +
TMXEditor
OpenXLIFF
XMLJavaBCP47J
Java
XMLJava
Electron
- diff --git a/html/es/updates.html b/html/es/updates.html index 453bb6d..f277daa 100644 --- a/html/es/updates.html +++ b/html/es/updates.html @@ -1,5 +1,5 @@ - + Actualizaciones de software @@ -7,7 +7,7 @@ - +
@@ -29,7 +29,6 @@
- diff --git a/i18n/tmxeditor_en.json b/i18n/tmxeditor_en.json index 2846af1..bccb568 100644 --- a/i18n/tmxeditor_en.json +++ b/i18n/tmxeditor_en.json @@ -1,6 +1,6 @@ { "App": { - "m1Mac": "You are running a version for Macs with Intel processors on a Mac with Apple M1 chipset.", + "m1Mac": "You are running a version for Macs with Intel processors on a Mac with Apple chipset.", "languageChanged": "Application language settings will be applied on next start", "restart": "Restart Now", "dismiss": "Dismiss", @@ -87,12 +87,24 @@ "UpdateDownloaded": "Update downloaded", "RegistrationRequired": "Registration Required", "RegistrationMessage": "You must register a subscription key to continue using this application", + "pageSpan": "Page", + "ofSpan": "of ", "pageTooltip": "Enter page number and press ENTER", "UnitsPage": "Units Page", + "UnitsLabel":"Units: ", "UnitsTooltip": "Enter number of units/page and press ENTER", "anyLanguage": "Any Language", "selectLanguage": "Select Language", - "columnLabel": "Column {0}" + "columnLabel": "Column {0}", + "fileAttributesSaved": "File attributes saved", + "filePropertiesSaved": "File properties saved", + "fileNotesSaved": "File notes saved", + "langAttributes": "'{0}' Attributes", + "langProperties": "'{0}' Properties", + "langNotes": "'{0}' Notes", + "tuAttributes": "TU Attributes", + "tuProperties": "TU Properties", + "tuNotes": "TU Notes" }, "menu": { "FileMenu": "&File", @@ -233,5 +245,14 @@ }, "splitFile": { "selectTmx": "Select TMX file" + }, + "fileInfo": { + "creationtool": "Creation Tool is required", + "creationtoolversion": "Creation Tool Version is required", + "segtype": "Segment Type is required", + "o-tmf" : "Translation Memory Format is required", + "adminlang": "Administrative Language is required", + "srclang": "Source Language is required", + "datatype": "Data Type is required" } } \ No newline at end of file diff --git a/i18n/tmxeditor_es.json b/i18n/tmxeditor_es.json index 10d89f1..aa9059c 100644 --- a/i18n/tmxeditor_es.json +++ b/i18n/tmxeditor_es.json @@ -1,26 +1,17 @@ { "App": { - "noUpdates": "No hay actualizaciones disponibles", - "ConvertedTMX": "Archivo TMX Convertido", - "RemovingDuplicates": "Eliminando unidades duplicadas...", "Downloading": "Descargando...", "unknownErrorMerging": "Error desconocido al combinar archivos", - "ErrorCleaning": "Error desconocido al limpiar caracteres", - "ErrorRemovingEntries": "Error desconocido eliminando entradas con traducciones iguales a origen", "filesMerged": "Archivos combinados", "ErrorValidating": "Error desconocido al validar archivo", "Exporting": "Exportando...", - "RemovingEntries": "Eliminando entradas...", "MergedFile": "Archivo TMX Combinado", - "selectUnits": "Seleccione unidades", + "filePropertiesSaved": "Propiedades de archivo guardadas", + "langNotes": "Notas '{0}'", "columnLabel": "Columna {0}", "fileIsValid": "El archivo es válido", - "ErrorRemovingSpaces": "Error desconocido al eliminar espacios", - "SaveTMX": "Guardar Archivo TMX", - "Downloaded": "Descargado: {0}%", - "AnyFile": "Cualquier Archivo", + "Downloaded": "{0}% descargado", "Replacing": "Reemplazando...", - "ErrorLoading": "Error desconocido al cargar archivo", "fileSplit": "Archivo dividido", "ErrorExporting": "Error desconocido al exportar archivo", "CleanCharacters": "Limpiar Caracteres Inválidos", @@ -30,79 +21,109 @@ "UnitsPage": "Unidades Página", "ClosingFile": "Cerrando archivo...", "DontSave": "No Guardar", - "OpeningFile": "Abriendo archivo...", + "tuProperties": "Propiedades UT", "RemovingUnits": "Eliminando unidades...", - "fileCleaned": "Archivo limpiado", - "ErrorSplitting": "Error desconocido al dividir archivo", - "LoadedUnits": "Cargando {0} unidades...", - "Changing": "Cambiando...", "ErrorRemovingUnits": "Error desconocido al eliminar unidades", "Consolidating": "Consolidando...", - "RegistrationMessage": "Debe registrar una clave de suscripción para seguir usando esta aplicación", - "Save": "Guardar", "UpdateDownloaded": "Actualización descargada", "UnsavedWarning": "Sus cambios se perderán si no los guarda", "subscriptionRegistered": "Suscripción registrada", "RemovingSpaces": "Eliminando espacios...", + "pageSpan": "Página", "CSVFile": "Archivo CSV/Texto", "ExportTabDelimited": "Exportar Archivo Delimitado por TAB", - "openTmxFile": "Abrir archivo TMX", "Cancel": "Cancelar", - "Merging": "Combinando...", - "fileNeeds3Languages": "El archivo necesita al menos 3 idiomas", - "Yes": "Si", - "ExcelFile": "Archivo Excel", - "ErrorRemovingTags": "Error desconocido al eliminar etiquetas", - "RemovingTags": "Eliminando etiquetas...", "Validating": "Validando...", "unknownErrorReplacing": "Error desconocido al reemplazar texto", "ErrorConsolidating": "Error desconocido al consolidar unidades", - "Processing": "Procesando...", - "OpenCSV": "Abrir Archivo CSV/Texto", - "GettingLanguages": "Buscando idiomas...", "fileConverted": "Archivo convertido", "unknownLicense": "Licencia desconocida", "RegistrationRequired": "Registro Obligatorio", - "LoadingSegments": "Cargando segmentos...", - "m1Mac": "Está ejecutando una versión para Mac con procesadores Intel en una Mac con chipset Apple M1.", + "m1Mac": "Está ejecutando una versión para Mac con procesadores Intel en una Mac con chipset Apple.", "convertedEntries": "{0} entradas convertidas", "OpenExcel": "Abrir Archivo Excel", "anyLanguage": "Cualquier Idioma", - "TMXFiles": "Archivos TMX", - "ValidateTMX": "Validar Archivo TMX", "languageChanged": "La configuración de idioma se aplicará en el próximo inicio", - "No": "No", - "selectLanguage": "Seleccione Idioma", "TBXFile": "Archivo TBX", "ExportExcel": "Exportar Archivo Excel", "restart": "Reiniciar Ahora", "TextFile": "Archivo de Texto", + "langAttributes": "Atributos '{0}'", + "ErrorRemovingDuplicates": "Error desconocido al eliminar unidades duplicadas", + "fileExported": "Archivo exportado", + "Splitting": "Dividiendo...", + "OpenSDLTM": "Abrir Archivo SDLTM", + "MaintenanceError": "Error desconocido al realizar mantenimiento", + "messageReceived": "Hemos recibido su solicitud. Se ha enviado una clave de evaluación a la dirección de correo electrónico indicada.

 Compruebe su filtro de correo no deseado si no recibe la clave de suscripción de evaluación en los próximos minutos.", + "UnitsTooltip": "Introduzca el número de unidades/página y pulse INTRO", + "SDLTMFile": "Archivo SDLTM", + "noUpdates": "No hay actualizaciones disponibles", + "ConvertedTMX": "Archivo TMX Convertido", + "RemovingDuplicates": "Eliminando unidades duplicadas...", + "ErrorCleaning": "Error desconocido al limpiar caracteres", + "ErrorRemovingEntries": "Error desconocido eliminando entradas con traducciones iguales a origen", + "RemovingEntries": "Eliminando entradas...", + "selectUnits": "Seleccione unidades", + "ErrorRemovingSpaces": "Error desconocido al eliminar espacios", + "SaveTMX": "Guardar Archivo TMX", + "AnyFile": "Cualquier Archivo", + "ErrorLoading": "Error desconocido al cargar archivo", + "OpeningFile": "Abriendo archivo...", + "fileCleaned": "Archivo limpiado", + "ErrorSplitting": "Error desconocido al dividir archivo", + "LoadedUnits": "Cargando {0} unidades...", + "Changing": "Cambiando...", + "RegistrationMessage": "Debe registrar una clave de suscripción para seguir usando esta aplicación", + "tuNotes": "Notas UT", + "Save": "Guardar", + "tuAttributes": "Atributos UT", + "openTmxFile": "Abrir archivo TMX", + "Merging": "Combinando...", + "fileNeeds3Languages": "El archivo necesita al menos 3 idiomas", + "Yes": "Si", + "ExcelFile": "Archivo Excel", + "ErrorRemovingTags": "Error desconocido al eliminar etiquetas", + "RemovingTags": "Eliminando etiquetas...", + "fileNotesSaved": "Notas de archivo guardadas", + "Processing": "Procesando...", + "OpenCSV": "Abrir Archivo CSV/Texto", + "GettingLanguages": "Buscando idiomas...", + "LoadingSegments": "Cargando segmentos...", + "TMXFiles": "Archivos TMX", + "ValidateTMX": "Validar Archivo TMX", + "No": "No", + "selectLanguage": "Seleccione Idioma", "Error": "Error", "Cleaning": "Limpiando...", "errorChangingLanguage": "Error desconocido al cambiar código de idioma", "OpenTBX": "Abrir Archivo TBX", - "ErrorRemovingDuplicates": "Error desconocido al eliminar unidades duplicadas", - "fileExported": "Archivo exportado", + "ofSpan": "de ", + "langProperties": "Propiedades '{0}'", "TMXFile": "Archivo TMX", "pageTooltip": "Introduzca el número de página y pulse INTRO", - "Splitting": "Dividiendo...", - "OpenSDLTM": "Abrir Archivo SDLTM", "Converting": "Convirtiendo...", - "MaintenanceError": "Error desconocido al realizar mantenimiento", - "messageReceived": "Hemos recibido su solicitud. Se ha enviado una clave de evaluación a la dirección de correo electrónico indicada. Compruebe su filtro de correo no deseado si no recibe la clave de suscripción de evaluación en los próximos minutos.", + "UnitsLabel": "Unidades: ", "SaveChanges": "¿Guardar cambios?", - "UnitsTooltip": "Introduzca el número de unidades/página y pulse INTRO", - "SDLTMFile": "Archivo SDLTM" + "fileAttributesSaved": "Atributos de archivo guardados" }, "registerExpired": {"enterSubscription": "Introduzca la clave de suscripción"}, "excelLanguages": {"selectAllLanguages": "Seleccione todos los idiomas"}, - "changeLanguage": {"selectLanguageWarning": "Seleccione nuevo idioma"}, + "changeLanguage": {"selectLanguageWarning": "Seleccionar nuevo idioma"}, "splitFile": {"selectTmx": "Seleccione archivo TMX"}, "newFile": { "selectTgtLanguageWarning": "Seleccione idioma destino", "selectDifferentLanguages": "Seleccione idiomas diferentes", "selectsrcLanguageWarning": "Seleccione idioma origen" }, + "fileInfo": { + "creationtool": "Herramienta de Creación es obligatorio", + "adminlang": "Idioma Administrativo es obligatorio", + "datatype": "Tipo de Dato es obligatorio", + "creationtoolversion": "Versión de Herramienta de Creación es obligatorio", + "segtype": "Tipo de Segmento es obligatorio", + "srclang": "Idioma Origen es obligatorio", + "o-tmf": "Formato de Memoria de Traducción is required" + }, "filters": {"enterText": "Introduzca el texto a buscar"}, "menu": { "New": "Nuevo", @@ -113,7 +134,7 @@ "ViewLicenses": "Ver Licencias", "UserGuide": "Manual del Usuario de TMXEditor", "ExportCSV": "Exportar Como Archivo Delimitado por TAB...", - "RemoveTags": "Eliminar todas las etiquetas", + "RemoveTags": "Eliminar Todas las Etiquetas", "RemoveLanguage": "Eliminar Idioma...", "MergeTMX": "Combinar Archivos TMX...", "InsertUnit": "Insertar Unidad de Traducción", @@ -145,7 +166,7 @@ "RegisterSubscription": "Registrar Suscripción...", "ChangeLanguage": "Cambiar Idioma...", "Preferences": "Preferencias...", - "RemoveSpaces": "Eliminar espacios iniciales/finales", + "RemoveSpaces": "Eliminar Espacios Iniciales/Finales", "ValidateTMX": "Validar Archivo TMX...", "ExportExcel": "Exportar Archivo Excel...", "CleanInvalidChars": "Limpiar Caracteres Inválidos...", diff --git a/jars/slf4j-api-2.0.13.jar b/jars/slf4j-api-2.0.13.jar new file mode 100644 index 0000000..a800cc2 Binary files /dev/null and b/jars/slf4j-api-2.0.13.jar differ diff --git a/jars/slf4j-api-2.0.9.jar b/jars/slf4j-api-2.0.9.jar deleted file mode 100644 index 3796afe..0000000 Binary files a/jars/slf4j-api-2.0.9.jar and /dev/null differ diff --git a/jars/slf4j-nop-2.0.13.jar b/jars/slf4j-nop-2.0.13.jar new file mode 100644 index 0000000..e2e1122 Binary files /dev/null and b/jars/slf4j-nop-2.0.13.jar differ diff --git a/jars/slf4j-nop-2.0.9.jar b/jars/slf4j-nop-2.0.9.jar deleted file mode 100644 index 6a9403d..0000000 Binary files a/jars/slf4j-nop-2.0.9.jar and /dev/null differ diff --git a/jars/sqlite-jdbc-3.45.2.0.jar b/jars/sqlite-jdbc-3.45.3.0.jar similarity index 61% rename from jars/sqlite-jdbc-3.45.2.0.jar rename to jars/sqlite-jdbc-3.45.3.0.jar index 9b6e9db..4debbd4 100644 Binary files a/jars/sqlite-jdbc-3.45.2.0.jar and b/jars/sqlite-jdbc-3.45.3.0.jar differ diff --git a/package.json b/package.json index 971ac6f..047a67f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "tmxeditor", "productName": "TMXEditor", - "version": "3.1.1", + "version": "3.3.0", "description": "TMX Editor", "main": "js/app.js", "scripts": { @@ -19,11 +19,12 @@ "url": "https://github.com/rmraya/TMXEditor.git" }, "devDependencies": { - "electron": "^29.3.0", - "typescript": "^5.4.5" + "electron": "^31.3.1", + "typescript": "^5.5.4" }, "dependencies": { - "typesxml": "^1.3.0", - "sdltm": "^1.1.0" + "typesxml": "^1.5.2", + "typesbcp47": "^1.4.2", + "sdltm": "^1.4.0" } } \ No newline at end of file diff --git a/src/com/maxprograms/tmxserver/Constants.java b/src/com/maxprograms/tmxserver/Constants.java index af290f3..c32bd6f 100644 --- a/src/com/maxprograms/tmxserver/Constants.java +++ b/src/com/maxprograms/tmxserver/Constants.java @@ -19,8 +19,8 @@ private Constants() { } public static final String APPNAME = "TMXEditor"; - public static final String VERSION = "3.1.1"; - public static final String BUILD = "20240411_0806"; + public static final String VERSION = "3.3.0"; + public static final String BUILD = "20240812_0919"; public static final String REASON = "reason"; public static final String STATUS = "status"; diff --git a/src/com/maxprograms/tmxserver/TMXServer.java b/src/com/maxprograms/tmxserver/TMXServer.java index 4791662..a62ccf7 100644 --- a/src/com/maxprograms/tmxserver/TMXServer.java +++ b/src/com/maxprograms/tmxserver/TMXServer.java @@ -135,6 +135,15 @@ public void handle(HttpExchange t) throws IOException { case "fileInfo": response = service.getFileInfo().toString(); break; + case "saveFileAttributes": + response = service.saveFileAttributes(json.getJSONObject("attributes")).toString(); + break; + case "saveFileProperties": + response = service.saveFileProperties(json.getJSONArray("properties")).toString(); + break; + case "saveFileNotes": + response = service.saveFileNotes(json.getJSONArray("notes")).toString(); + break; case "loadingProgress": response = getLoadingProgress(); break; diff --git a/src/com/maxprograms/tmxserver/TMXService.java b/src/com/maxprograms/tmxserver/TMXService.java index 41b4c14..6ff8f5a 100644 --- a/src/com/maxprograms/tmxserver/TMXService.java +++ b/src/com/maxprograms/tmxserver/TMXService.java @@ -74,7 +74,6 @@ public class TMXService { protected StoreInterface store; protected File currentFile; - protected int indentation; protected boolean parsing; protected String parsingError; @@ -148,6 +147,11 @@ public JSONObject openFile(String fileName) { } currentFile = new File(fileName); Set languages = getLanguages(fileName); + if (languages.isEmpty()) { + result.put(Constants.STATUS, Constants.ERROR); + result.put(Constants.REASON, Messages.getString("TMXService.15")); + return result; + } store = new SqlStore(languages); parsingError = ""; Thread.ofVirtual().start(() -> { @@ -347,12 +351,12 @@ public JSONObject saveData(String id, String lang, String value) { public JSONObject saveFile(String file) { saving = true; currentFile = new File(file); - getIndentation(); - store.setIndentation(indentation); savingError = ""; try { Thread.ofVirtual().start(() -> { try { + JSONObject json = getPreferences(); + store.setIndentation(json.getInt("indentation")); store.writeFile(currentFile); } catch (Exception ex) { logger.log(Level.SEVERE, ex.getMessage(), ex); @@ -402,6 +406,8 @@ public JSONObject getFileProperties() { JSONObject attributes = new JSONObject(); result.put("attributes", attributes); + attributes.put("creationid", header.getAttributeValue("creationid")); + attributes.put("creationdate", header.getAttributeValue("creationdate")); attributes.put("creationtool", header.getAttributeValue("creationtool")); attributes.put("creationtoolversion", header.getAttributeValue("creationtoolversion")); attributes.put("segtype", header.getAttributeValue("segtype")); @@ -409,6 +415,9 @@ public JSONObject getFileProperties() { attributes.put("adminlang", header.getAttributeValue("adminlang")); attributes.put("srclang", header.getAttributeValue("srclang")); attributes.put("datatype", header.getAttributeValue("datatype")); + attributes.put("changedate", header.getAttributeValue("changedate")); + attributes.put("changeid", header.getAttributeValue("changeid")); + attributes.put("o_encoding", header.getAttributeValue("o-encoding")); result.put(Constants.STATUS, Constants.SUCCESS); } else { @@ -684,6 +693,7 @@ public JSONObject createFile(Language srcLang, Language tgtLang) { Element tmx = doc.getRootElement(); tmx.setAttribute("version", "1.4"); Element header = new Element("header"); + header.setAttribute("creationid", System.getProperty("user.name")); header.setAttribute("creationdate", TmxUtils.tmxDate()); header.setAttribute("creationtool", Constants.APPNAME); header.setAttribute("creationtoolversion", Constants.VERSION); @@ -696,18 +706,23 @@ public JSONObject createFile(Language srcLang, Language tgtLang) { Element body = new Element("body"); tmx.addContent(body); Element tu = new Element("tu"); + tu.setAttribute("creationid", System.getProperty("user.name")); tu.setAttribute("creationdate", TmxUtils.tmxDate()); tu.setAttribute("creationtool", Constants.APPNAME); tu.setAttribute("creationtoolversion", Constants.VERSION); body.addContent(tu); Element tuv1 = new Element("tuv"); tuv1.setAttribute("xml:lang", srcLang.getCode()); + tuv1.setAttribute("creationid", System.getProperty("user.name")); + tuv1.setAttribute("creationdate", TmxUtils.tmxDate()); Element seg1 = new Element("seg"); seg1.setText(srcLang.getCode()); tuv1.addContent(seg1); tu.addContent(tuv1); Element tuv2 = new Element("tuv"); tuv2.setAttribute("xml:lang", tgtLang.getCode()); + tuv2.setAttribute("creationid", System.getProperty("user.name")); + tuv2.setAttribute("creationdate", TmxUtils.tmxDate()); Element seg2 = new Element("seg"); seg2.setText(tgtLang.getCode()); tuv2.addContent(seg2); @@ -715,7 +730,7 @@ public JSONObject createFile(Language srcLang, Language tgtLang) { File tempFile = File.createTempFile("temp", ".tmx"); tempFile.deleteOnExit(); - Indenter.indent(tmx, 2); + Indenter.indent(tmx, getPreferences().getInt("indentation")); XMLOutputter outputter = new XMLOutputter(); outputter.preserveSpace(true); try (FileOutputStream out = new FileOutputStream(tempFile)) { @@ -881,8 +896,8 @@ public JSONObject splitFile(String file, int parts) { l++; } splitStore = new SplitStore(f, l); - getIndentation(); - splitStore.setIndentation(indentation); + JSONObject json = getPreferences(); + splitStore.setIndentation(json.getInt("indentation")); countStore = null; reader = new TMXReader(splitStore); reader.parse(f); @@ -929,7 +944,8 @@ public JSONObject mergeFiles(String merged, List files) { mergeError = ""; Thread.ofVirtual().start(() -> { try { - getIndentation(); + JSONObject json = getPreferences(); + int indentation = json.getInt("indentation"); try (FileOutputStream out = new FileOutputStream(new File(merged))) { Element header = new Element("header"); header.setAttribute("creationdate", TmxUtils.tmxDate()); @@ -1518,81 +1534,14 @@ public JSONObject convertCsv(String csvFile, String tmxFile, List langua return result; } - public JSONObject getIndentation() { - JSONObject result = new JSONObject(); - File home = getPreferencesFolder(); - if (!home.exists()) { - home.mkdirs(); - } - File preferences = new File(home, "preferences.json"); - try { - if (!preferences.exists()) { - JSONObject prefs = new JSONObject(); - prefs.put("theme", "system"); - prefs.put("indentation", 2); - try (FileOutputStream output = new FileOutputStream(preferences)) { - output.write(prefs.toString().getBytes(StandardCharsets.UTF_8)); - } - } - try (FileReader input = new FileReader(preferences)) { - try (BufferedReader reader = new BufferedReader(input)) { - StringBuilder builder = new StringBuilder(); - String line = ""; - while ((line = reader.readLine()) != null) { - builder.append(line); - } - JSONObject json = new JSONObject(builder.toString()); - indentation = json.getInt("indentation"); - result.put("indentation", indentation); - } - } - result.put(Constants.STATUS, Constants.SUCCESS); - } catch (IOException e) { - logger.log(Level.SEVERE, e.getMessage(), e); - result.put(Constants.STATUS, Constants.ERROR); - result.put(Constants.REASON, e.getMessage()); - } - return result; - } - public JSONObject saveIndentation(int value) { JSONObject result = new JSONObject(); - File home = getPreferencesFolder(); - if (!home.exists()) { - home.mkdirs(); - } - File preferences = new File(home, "preferences.json"); try { - if (!preferences.exists()) { - JSONObject prefs = new JSONObject(); - prefs.put("theme", "system"); - prefs.put("indentation", 2); - try (FileOutputStream output = new FileOutputStream(preferences)) { - output.write(prefs.toString().getBytes(StandardCharsets.UTF_8)); - } - } - JSONObject json = null; - try (FileReader input = new FileReader(preferences)) { - try (BufferedReader reader = new BufferedReader(input)) { - StringBuilder builder = new StringBuilder(); - String line = ""; - while ((line = reader.readLine()) != null) { - builder.append(line); - } - json = new JSONObject(builder.toString()); - json.put("indentation", value); - - } - } - try (FileOutputStream output = new FileOutputStream(preferences)) { - output.write(json.toString().getBytes(StandardCharsets.UTF_8)); - } - indentation = value; - if (store != null) { - store.setIndentation(indentation); - } + JSONObject json = getPreferences(); + json.put("indentation", value); + savePreferences(json); result.put(Constants.STATUS, Constants.SUCCESS); - } catch (IOException e) { + } catch (Exception e) { logger.log(Level.SEVERE, e.getMessage(), e); result.put(Constants.STATUS, Constants.ERROR); result.put(Constants.REASON, e.getMessage()); @@ -1776,4 +1725,73 @@ public JSONObject getFileInfo() throws JSONException, SQLException { return result; } + public static JSONObject getPreferences() throws JSONException, IOException { + JSONObject result = new JSONObject(); + File home = getPreferencesFolder(); + if (!home.exists()) { + home.mkdirs(); + } + File preferences = new File(home, "preferences.json"); + if (!preferences.exists()) { + JSONObject prefs = new JSONObject(); + prefs.put("theme", "system"); + prefs.put("appLang", "en"); + prefs.put("indentation", 2); + prefs.put("changeId", false); + try (FileOutputStream output = new FileOutputStream(preferences)) { + output.write(prefs.toString().getBytes(StandardCharsets.UTF_8)); + } + } + try (FileReader input = new FileReader(preferences)) { + try (BufferedReader reader = new BufferedReader(input)) { + StringBuilder builder = new StringBuilder(); + String line = ""; + while ((line = reader.readLine()) != null) { + if (!builder.isEmpty()) { + builder.append('\n'); + } + builder.append(line); + } + result = new JSONObject(builder.toString()); + } + } + if (!result.has("changeId")) { + result.put("changeId", false); + } + return result; + } + + private JSONObject savePreferences(JSONObject json) throws JSONException, IOException { + JSONObject result = new JSONObject(); + File home = getPreferencesFolder(); + if (!home.exists()) { + home.mkdirs(); + } + File preferences = new File(home, "preferences.json"); + try (FileOutputStream output = new FileOutputStream(preferences)) { + output.write(json.toString(2).getBytes(StandardCharsets.UTF_8)); + } + return result; + } + + public JSONObject saveFileAttributes(JSONObject attributes) { + ((SqlStore)store).setFileAttributes(attributes); + JSONObject result = new JSONObject(); + result.put(Constants.STATUS, Constants.SUCCESS); + return result; + } + + public JSONObject saveFileProperties(JSONArray properties) { + ((SqlStore)store).setFileProperties(properties); + JSONObject result = new JSONObject(); + result.put(Constants.STATUS, Constants.SUCCESS); + return result; + } + + public Object saveFileNotes(JSONArray notes) { + ((SqlStore)store).setFileNotes(notes); + JSONObject result = new JSONObject(); + result.put(Constants.STATUS, Constants.SUCCESS); + return result; + } } diff --git a/src/com/maxprograms/tmxserver/tmx/LanguageHandler.java b/src/com/maxprograms/tmxserver/tmx/LanguageHandler.java index 269136b..6038637 100644 --- a/src/com/maxprograms/tmxserver/tmx/LanguageHandler.java +++ b/src/com/maxprograms/tmxserver/tmx/LanguageHandler.java @@ -12,6 +12,7 @@ package com.maxprograms.tmxserver.tmx; +import java.io.IOException; import java.util.HashSet; import java.util.Set; @@ -19,6 +20,7 @@ import org.xml.sax.Locator; import org.xml.sax.SAXException; +import com.maxprograms.languages.LanguageUtils; import com.maxprograms.xml.Catalog; import com.maxprograms.xml.Document; import com.maxprograms.xml.IContentHandler; @@ -139,8 +141,17 @@ public void setCatalog(Catalog arg0) { // do nothing } - public Set getLanguages() { - return languages; + public Set getLanguages() throws IOException { + Set result = new HashSet<>(); + Set normalized = new HashSet<>(); + for (String lang : languages) { + String normal = LanguageUtils.normalizeCode(lang.replace('_', '-')); + if (!normalized.contains(normal)) { + normalized.add(normal); + result.add(lang); + } + } + return result; } } diff --git a/src/com/maxprograms/tmxserver/tmx/SqlStore.java b/src/com/maxprograms/tmxserver/tmx/SqlStore.java index 5ef3df7..c8c8767 100644 --- a/src/com/maxprograms/tmxserver/tmx/SqlStore.java +++ b/src/com/maxprograms/tmxserver/tmx/SqlStore.java @@ -36,11 +36,14 @@ import javax.xml.parsers.ParserConfigurationException; +import org.json.JSONArray; +import org.json.JSONObject; import org.sqlite.Function; import org.xml.sax.SAXException; import com.maxprograms.languages.Language; import com.maxprograms.tmxserver.Constants; +import com.maxprograms.tmxserver.TMXService; import com.maxprograms.tmxserver.excel.ExcelWriter; import com.maxprograms.tmxserver.excel.Sheet; import com.maxprograms.tmxserver.models.TUnit; @@ -66,6 +69,7 @@ public class SqlStore implements StoreInterface { private PreparedStatement selectTU; private PreparedStatement selectTUS; private SAXBuilder builder; + private boolean setChangeId; public SqlStore(Set langSet) throws IOException, SQLException { File workFolder = TmxUtils.getWorkFolder(); @@ -101,6 +105,7 @@ protected void xFunc() throws SQLException { } }); createTables(); + setChangeId = TMXService.getPreferences().getBoolean("changeId"); } private void createTables() throws SQLException { @@ -135,7 +140,7 @@ PRIMARY KEY(id) @Override public void storeTU(Element tu) throws IOException, SQLException { - String id = tu.hasAttribute("tuid") ? tu.getAttributeValue("tuid") : "" + time++; + String id = "" + time++; List tuvs = tu.getChildren("tuv"); Iterator it = tuvs.iterator(); int tuvCount = 0; @@ -325,6 +330,7 @@ public Element getTuv(String id, String lang) String tuv = rs.getString(1); if (tuv != null && !tuv.isBlank()) { result = parseElement(tuv); + result.setAttribute("xml:lang", lang); } } } @@ -385,11 +391,16 @@ public String saveData(String id, String lang, String value) } catch (Exception ex) { seg.setText(text); } + if (setChangeId) { + tuv.setAttribute("changeid", System.getProperty("user.name")); + tuv.setAttribute("changedate", TmxUtils.tmxDate()); + } updateTUV(id, lang, tuv); conn.commit(); } else { tuv = new Element("tuv"); tuv.setAttribute("xml:lang", lang); + tuv.setAttribute("creationid", System.getProperty("user.name")); tuv.setAttribute("creationdate", TmxUtils.tmxDate()); Element seg = new Element("seg"); seg.setText(text); @@ -409,7 +420,10 @@ public void writeFile(File file) throws IOException, SAXException, ParserConfigu """); - + if (setChangeId) { + header.setAttribute("changeid", System.getProperty("user.name")); + header.setAttribute("changedate", TmxUtils.tmxDate()); + } writeString(out, TextUtils.padding(1, indentation) + header.toString() + "\n"); writeString(out, TextUtils.padding(1, indentation) + "\n"); selectTUS.setLong(1, 0l); @@ -502,11 +516,19 @@ public void replaceText(String search, String replace, Language language, boolea if (regExp) { TmxUtils.replaceText(seg, search, replace, regExp); if (!segText.equals(TmxUtils.textOnly(seg))) { + if (setChangeId) { + tuv.setAttribute("changeid", System.getProperty("user.name")); + tuv.setAttribute("changedate", TmxUtils.tmxDate()); + } updateTUV(id, language.getCode(), tuv); } } else { if (segText.indexOf(search) != -1) { TmxUtils.replaceText(tuv.getChild("seg"), search, replace, regExp); + if (setChangeId) { + tuv.setAttribute("changeid", System.getProperty("user.name")); + tuv.setAttribute("changedate", TmxUtils.tmxDate()); + } updateTUV(id, language.getCode(), tuv); } } @@ -674,6 +696,10 @@ public void removeTags() throws SAXException, IOException, ParserConfigurationEx Element seg = tuv.getChild("seg"); if (!seg.getChildren().isEmpty()) { seg.setText(TmxUtils.textOnly(seg)); + if (setChangeId) { + tuv.setAttribute("changeid", System.getProperty("user.name")); + tuv.setAttribute("changedate", TmxUtils.tmxDate()); + } updateTUV(id, lang, tuv); } } @@ -775,6 +801,10 @@ public void removeSpaces() throws SAXException, IOException, ParserConfiguration Element tuv = parseElement(rs.getString(lower + "_tuv")); Element seg = tuv.getChild("seg"); seg.setContent(TmxUtils.stripSegment(seg).getContent()); + if (setChangeId) { + tuv.setAttribute("changeid", System.getProperty("user.name")); + tuv.setAttribute("changedate", TmxUtils.tmxDate()); + } updateTUV(id, lang, tuv); } } @@ -833,6 +863,10 @@ public void consolidateUnits(Language language) Element b = segments.get(lang); if (a == null && b != null) { lastSegments.put(lang, b); + if (setChangeId) { + b.setAttribute("changeid", System.getProperty("user.name")); + b.setAttribute("changedate", TmxUtils.tmxDate()); + } updateTUV(lastId, lang, b); clearTuv(id, lang); } @@ -906,6 +940,10 @@ public void setTuProperties(String id, List properties) } content.addAll(tu.getChildren("note")); tu.setChildren(content); + if (setChangeId) { + tu.setAttribute("changeid", System.getProperty("user.name")); + tu.setAttribute("changedate", TmxUtils.tmxDate()); + } updateTU(id, tu); conn.commit(); } @@ -930,6 +968,10 @@ public void setTuvProperties(String id, String lang, List properties) content.add(0, prop); } tuv.setChildren(content); + if (setChangeId) { + tuv.setAttribute("changeid", System.getProperty("user.name")); + tuv.setAttribute("changedate", TmxUtils.tmxDate()); + } updateTUV(id, lang, tuv); conn.commit(); } @@ -953,6 +995,10 @@ public void setTuNotes(String id, List notes) content.add(not); } tu.setChildren(content); + if (setChangeId) { + tu.setAttribute("changeid", System.getProperty("user.name")); + tu.setAttribute("changedate", TmxUtils.tmxDate()); + } updateTU(id, tu); conn.commit(); } @@ -976,6 +1022,10 @@ public void setTuvNotes(String id, String lang, List notes) content.add(0, not); } tuv.setChildren(content); + if (setChangeId) { + tuv.setAttribute("changeid", System.getProperty("user.name")); + tuv.setAttribute("changedate", TmxUtils.tmxDate()); + } updateTUV(id, lang, tuv); conn.commit(); } @@ -1004,11 +1054,11 @@ public void exportDelimited(String file) try (ResultSet rs = selectTUS.executeQuery()) { while (rs.next()) { StringBuilder line = new StringBuilder(); - String tuid = rs.getString(1); + String id = rs.getString(1); Iterator langIt = languages.iterator(); while (langIt.hasNext()) { String lang = langIt.next(); - String pure = getPure(tuid, lang); + String pure = getPure(id, lang); String text = " "; if (!pure.isEmpty()) { text = TmxUtils.cleanLines(pure); @@ -1064,12 +1114,12 @@ public void exportExcel(String file) throws IOException, SAXException, ParserCon try (Statement stmt = conn.createStatement()) { try (ResultSet rs = stmt.executeQuery(sql)) { while (rs.next()) { - String tuid = rs.getString(1); + String id = rs.getString(1); Map rowMap = new HashMap<>(); langIt = languages.iterator(); while (langIt.hasNext()) { String lang = langIt.next(); - Element tuv = getTuv(tuid, lang); + Element tuv = getTuv(id, lang); String text = ""; if (tuv != null) { text = TmxUtils.textOnly(tuv.getChild("seg")); @@ -1086,4 +1136,46 @@ public void exportExcel(String file) throws IOException, SAXException, ParserCon writer.writeFile(file, sheet); } + public void setFileAttributes(JSONObject attributes) { + attributes.keySet().forEach(key -> { + String value = attributes.getString(key); + if ("o_encoding".equals(key)) { + key = "o-encoding"; + } + if ("o_tmf".equals(key)) { + key = "o-tmf"; + } + if (value.isEmpty() && header.hasAttribute(key)) { + header.removeAttribute(key); + } else { + header.setAttribute(key, value); + } + }); + } + + public void setFileNotes(JSONArray notes) { + List oldNotes = header.getChildren("note"); + for (Element note : oldNotes) { + header.removeChild(note); + } + for (int i = 0; i < notes.length(); i++) { + Element note = new Element("note"); + note.setText(notes.getString(i)); + header.addContent(note); + } + } + + public void setFileProperties(JSONArray properties) { + List oldProps = header.getChildren("prop"); + for (Element prop : oldProps) { + header.removeChild(prop); + } + for (int i = 0; i < properties.length(); i++) { + JSONArray prop = properties.getJSONArray(i); + Element property = new Element("prop"); + property.setAttribute("type", prop.getString(0)); + property.setText(prop.getString(1)); + header.addContent(property); + } + } } diff --git a/src/com/maxprograms/tmxserver/tmxserver.properties b/src/com/maxprograms/tmxserver/tmxserver.properties index 28581b7..0e57738 100644 --- a/src/com/maxprograms/tmxserver/tmxserver.properties +++ b/src/com/maxprograms/tmxserver/tmxserver.properties @@ -28,6 +28,7 @@ TMXService.11=Error getting languages TMXService.12=Error checking CSV languages TMXService.13={0} (Unknown Variant) TMXService.14=Unknown Language +TMXService.15=Selected TMX file is empty TMXService.2=Null Store TMXService.3=File does not exist TMXService.4=Error splitting files diff --git a/src/com/maxprograms/tmxserver/tmxserver_es.properties b/src/com/maxprograms/tmxserver/tmxserver_es.properties index 34d5308..3d4a164 100644 --- a/src/com/maxprograms/tmxserver/tmxserver_es.properties +++ b/src/com/maxprograms/tmxserver/tmxserver_es.properties @@ -28,6 +28,7 @@ TMXService.11=Error al obtener idiomas TMXService.12=Error al comprobar los idiomas CSV TMXService.13={0} (Variante Desconocida) TMXService.14=Idioma Desconocido +TMXService.15=El archivo TMX seleccionado está vacío TMXService.2=Almacenamiento nulo TMXService.3=El archivo no existe TMXService.4=Error al dividir archivos diff --git a/src/module-info.java b/src/module-info.java index ea3a49d..fbd4541 100644 --- a/src/module-info.java +++ b/src/module-info.java @@ -24,4 +24,5 @@ requires transitive json; requires transitive xmljava; requires org.xerial.sqlitejdbc; + requires org.slf4j.nop; } \ No newline at end of file diff --git a/tmxeditor_en.pdf b/tmxeditor_en.pdf index f736009..a03fdb0 100644 Binary files a/tmxeditor_en.pdf and b/tmxeditor_en.pdf differ diff --git a/tmxeditor_es.pdf b/tmxeditor_es.pdf index 1f35466..e706a06 100644 Binary files a/tmxeditor_es.pdf and b/tmxeditor_es.pdf differ diff --git a/ts/about.ts b/ts/about.ts index 70b7dab..586cb33 100644 --- a/ts/about.ts +++ b/ts/about.ts @@ -36,8 +36,6 @@ class About { this.electron.ipcRenderer.send('close-about'); } }); - setTimeout(() => { - this.electron.ipcRenderer.send('about-height', { width: document.body.clientWidth, height: document.body.clientHeight }); - }, 200); + this.electron.ipcRenderer.send('about-height', { width: document.body.clientWidth, height: document.body.clientHeight }); } } diff --git a/ts/addNote.ts b/ts/addNote.ts index 33e5bd1..0c938c0 100644 --- a/ts/addNote.ts +++ b/ts/addNote.ts @@ -40,9 +40,9 @@ class AddNote { saveNote(): void { let note: string = (document.getElementById('note') as HTMLInputElement).value; if (note === '') { - this.electron.ipcRenderer.send('show-message', { type: 'warning', group:'addNote', key: 'enterNote', parent: 'addNote' }); + this.electron.ipcRenderer.send('show-message', { type: 'warning', group: 'addNote', key: 'enterNote', parent: 'addNote' }); return; } - this.electron.ipcRenderer.send('add-new-note', { note: note }); + this.electron.ipcRenderer.send('add-new-note', note); } } diff --git a/ts/app.ts b/ts/app.ts index 416e035..a297fcd 100644 --- a/ts/app.ts +++ b/ts/app.ts @@ -19,6 +19,7 @@ import { TMReader } from "sdltm"; import { I18n } from "./i18n"; import { Locations, Point } from "./locations"; import { Tbx2Tmx } from "./tbx2tmx"; +import { LanguageUtils } from "typesbcp47"; const SUCCESS: string = 'Success'; const LOADING: string = 'Loading'; @@ -80,7 +81,7 @@ class App { static shouldClose: boolean = false; static currentFile: string = ''; static currentDefaults: any; - static currentPreferences: any; + static currentPreferences: Preferences; static currentCss: string; static currentStatus: any = {}; static fileLanguages: Language[]; @@ -91,7 +92,6 @@ class App { static csvEvent: IpcMainEvent; static excelEvent: IpcMainEvent; static propertyEvent: IpcMainEvent; - static notesEvent: IpcMainEvent; static filterOptions: any = {}; static loadOptions: any = { @@ -108,8 +108,6 @@ class App { static needsName: boolean = false; - static verticalPadding: number = 46; - static latestVersion: string; static downloadLink: string; @@ -228,7 +226,10 @@ class App { } else { App.currentCss = 'file://' + path.join(app.getAppPath(), 'css', 'light.css'); } - App.mainWindow.webContents.send('set-theme', App.currentCss); + } + let windows: BrowserWindow[] = BrowserWindow.getAllWindows(); + for (let window of windows) { + window.webContents.send('set-theme', App.currentCss); } }); @@ -283,8 +284,8 @@ class App { ipcMain.on('get-unit-properties', (event: IpcMainEvent) => { event.sender.send('set-unit-properties', App.propertiesArg); }); - ipcMain.on('show-add-property', (event: IpcMainEvent) => { - App.showAddProperty(event); + ipcMain.on('show-add-property', (event: IpcMainEvent, parent: string) => { + App.showAddProperty(event, parent); }); ipcMain.on('add-new-property', (event: IpcMainEvent, arg: any) => { this.addNewProperty(arg); @@ -293,16 +294,16 @@ class App { this.saveProperties(arg); }); ipcMain.on('edit-notes', (event: IpcMainEvent, arg: any) => { - App.editNotes(arg); + App.showNotesPanel(arg); }); - ipcMain.on('get-unit-notes', (event: IpcMainEvent, arg: any) => { + ipcMain.on('get-unit-notes', (event: IpcMainEvent) => { event.sender.send('set-unit-notes', App.notesArg); }); - ipcMain.on('show-add-note', (event: IpcMainEvent, arg: any) => { - this.showAddNote(event); + ipcMain.on('show-add-note', (event: IpcMainEvent, parent: string) => { + this.showAddNote(parent); }); - ipcMain.on('add-new-note', (event: IpcMainEvent, arg: any) => { - this.addNewNote(arg); + ipcMain.on('add-new-note', (event: IpcMainEvent, note: string) => { + this.addNewNote(note); }); ipcMain.on('save-notes', (event: IpcMainEvent, arg: any) => { this.saveNotes(arg); @@ -310,9 +311,6 @@ class App { ipcMain.on('get-preferences', (event: IpcMainEvent) => { event.sender.send('set-preferences', App.currentPreferences); }); - ipcMain.on('get-appLanguage', (event: IpcMainEvent) => { - event.sender.send('set-appLanguage', App.lang); - }); ipcMain.on('save-preferences', (event: IpcMainEvent, arg: any) => { App.savePreferences(arg); App.destroyWindow(App.settingsWindow); @@ -330,7 +328,16 @@ class App { }); ipcMain.on('save-file', () => { App.saveFile(); - }) + }); + ipcMain.on('save-file-attributes', (event: IpcMainEvent, arg: any) => { + App.saveFileAttributes(arg); + }); + ipcMain.on('save-file-properties', (event: IpcMainEvent, properties: Array) => { + App.saveFileProperties(properties); + }); + ipcMain.on('save-file-notes', (event: IpcMainEvent, notes: string[]) => { + App.saveFileNotes(notes); + }); ipcMain.on('convert-excel', () => { App.convertExcel(); }); @@ -755,10 +762,8 @@ class App { } } - static setHeight(window: BrowserWindow, arg: any) { - let rect: Rectangle = window.getBounds(); - rect.height = arg.height + App.verticalPadding; - window.setBounds(rect); + static setHeight(window: BrowserWindow, arg: { width: number, height: number }) { + window.setContentSize(arg.width, arg.height, true); } static destroyWindow(window: BrowserWindow): void { @@ -804,7 +809,13 @@ class App { 'nextPage': App.i18n.getString('menu', 'NextPage'), 'lastPage': App.i18n.getString('menu', 'LastPage'), 'unitsPage': App.i18n.getString('App', 'UnitsPage'), + 'pageSpan': App.i18n.getString('App', 'pageSpan'), + 'ofSpan': App.i18n.getString('App', 'ofSpan'), + 'unitsLabel': App.i18n.getString('App', 'UnitsLabel'), 'unitsTooltip': App.i18n.getString('App', 'UnitsTooltip'), + 'tuAttributes': App.i18n.getString('App', 'tuAttributes'), + 'tuProperties': App.i18n.getString('App', 'tuProperties'), + 'tuNotes': App.i18n.getString('App', 'tuNotes'), }; event.sender.send('set-tooltips', tooltips); } @@ -855,6 +866,8 @@ class App { break; case 'addLanguage': parent = App.addLanguageWindow; break; + case 'fileInfo': parent = App.fileInfoWindow; + break; default: parent = App.mainWindow; } } @@ -930,12 +943,12 @@ class App { { label: App.i18n.getString('menu', 'MergeTMX'), click: () => { App.mergeFiles(); } } ]); let editMenu: Menu = Menu.buildFromTemplate([ - { label: App.i18n.getString('menu', 'Undo'), accelerator: 'CmdOrCtrl+Z', click: () => { App.mainWindow.webContents.undo(); } }, + { label: App.i18n.getString('menu', 'Undo'), accelerator: 'CmdOrCtrl+Z', click: () => { BrowserWindow.getFocusedWindow().webContents.undo(); } }, new MenuItem({ type: 'separator' }), - { label: App.i18n.getString('menu', 'Cut'), accelerator: 'CmdOrCtrl+X', click: () => { App.mainWindow.webContents.cut(); } }, - { label: App.i18n.getString('menu', 'Copy'), accelerator: 'CmdOrCtrl+C', click: () => { App.mainWindow.webContents.copy(); } }, - { label: App.i18n.getString('menu', 'Paste'), accelerator: 'CmdOrCtrl+V', click: () => { App.mainWindow.webContents.paste(); } }, - { label: App.i18n.getString('menu', 'SelectAll'), accelerator: 'CmdOrCtrl+A', click: () => { App.mainWindow.webContents.selectAll(); } }, + { label: App.i18n.getString('menu', 'Cut'), accelerator: 'CmdOrCtrl+X', click: () => { BrowserWindow.getFocusedWindow().webContents.cut(); } }, + { label: App.i18n.getString('menu', 'Copy'), accelerator: 'CmdOrCtrl+C', click: () => { BrowserWindow.getFocusedWindow().webContents.copy(); } }, + { label: App.i18n.getString('menu', 'Paste'), accelerator: 'CmdOrCtrl+V', click: () => { BrowserWindow.getFocusedWindow().webContents.paste(); } }, + { label: App.i18n.getString('menu', 'SelectAll'), accelerator: 'CmdOrCtrl+A', click: () => { BrowserWindow.getFocusedWindow().webContents.selectAll(); } }, new MenuItem({ type: 'separator' }), { label: App.i18n.getString('menu', 'ConfirmEdit'), accelerator: 'Alt+Enter', click: () => { App.saveEdits(); } }, { label: App.i18n.getString('menu', 'CancelEdit'), accelerator: 'Esc', click: () => { App.cancelEdit(); } }, @@ -1120,6 +1133,7 @@ class App { App.aboutWindow = new BrowserWindow({ parent: App.mainWindow, width: 620, + height: 370, minimizable: false, maximizable: false, resizable: false, @@ -1177,6 +1191,7 @@ class App { this.systemInfoWindow = new BrowserWindow({ parent: App.aboutWindow, width: 430, + height: 300, minimizable: false, maximizable: false, resizable: false, @@ -1218,6 +1233,7 @@ class App { App.licensesWindow = new BrowserWindow({ parent: parent, width: 460, + height: 460, minimizable: false, maximizable: false, resizable: false, @@ -1511,7 +1527,7 @@ class App { } } - static savePreferences(preferences: any): void { + static savePreferences(preferences: Preferences): void { writeFileSync(path.join(app.getPath('appData'), app.name, 'preferences.json'), JSON.stringify(preferences, undefined, 4)); if (app.isReady() && preferences.appLang !== App.lang) { dialog.showMessageBox({ @@ -1541,14 +1557,18 @@ class App { appLang = 'es'; } } - this.savePreferences({ theme: 'system', indentation: 2, appLang: appLang }); + this.savePreferences({ theme: 'system', indentation: 2, appLang: appLang, changeId: false }); } try { let data: Buffer = readFileSync(preferencesFile); - App.currentPreferences = JSON.parse(data.toString()); - if (!App.currentPreferences.appLang) { - App.currentPreferences.appLang = 'en'; + let json: any = JSON.parse(data.toString()); + if (!json.appLang) { + json.appLang = 'en'; } + if (!json.changeId) { + json.changeId = false; + } + App.currentPreferences = json; App.lang = App.currentPreferences.appLang; } catch (err) { console.error(err); @@ -1596,6 +1616,12 @@ class App { (data: any) => { App.mainWindow.webContents.send('end-waiting'); data.type = lang; + let attributesLabel: string = App.i18n.getString('App', 'langAttributes'); + let propertiesLabel: string = App.i18n.getString('App', 'langProperties'); + let notesLabel: string = App.i18n.getString('App', 'langNotes'); + data.attributesType = App.i18n.format(attributesLabel, [lang]); + data.propertiesType = App.i18n.format(propertiesLabel, [lang]); + data.notesType = App.i18n.format(notesLabel, [lang]); App.mainWindow.webContents.send('update-properties', data); }, (reason: string) => { @@ -1612,6 +1638,9 @@ class App { App.mainWindow.webContents.send('end-waiting'); if (data.status === SUCCESS) { data.type = 'TU'; + data.attributesType = App.i18n.getString('App', 'tuAttributes'); + data.propertiesType = App.i18n.getString('App', 'tuProperties'); + data.notesType = App.i18n.getString('App', 'tuNotes'); App.mainWindow.webContents.send('update-properties', data); } else { console.log(id, JSON.stringify(data)); @@ -1629,6 +1658,7 @@ class App { App.attributesWindow = new BrowserWindow({ parent: App.mainWindow, width: 670, + height: 450, minimizable: false, maximizable: false, resizable: false, @@ -1682,6 +1712,7 @@ class App { App.propertiesWindow = new BrowserWindow({ parent: App.mainWindow, width: 500, + height: 280, minimizable: false, maximizable: false, resizable: false, @@ -1704,11 +1735,12 @@ class App { App.setLocation(App.propertiesWindow, 'properties.html'); } - static showAddProperty(event: IpcMainEvent): void { + static showAddProperty(event: IpcMainEvent, parent: string): void { App.propertyEvent = event; App.addPropertyWindow = new BrowserWindow({ - parent: App.propertiesWindow, + parent: (parent === 'fileInfo' ? App.fileInfoWindow : App.propertiesWindow), width: 350, + height: 140, minimizable: false, maximizable: false, resizable: false, @@ -1726,7 +1758,7 @@ class App { App.addPropertyWindow.show(); }); App.addPropertyWindow.on('close', () => { - App.propertiesWindow.focus(); + App.addPropertyWindow.getParentWindow().focus(); }); App.setLocation(App.addPropertyWindow, 'addProperty.html'); } @@ -1763,10 +1795,11 @@ class App { ); } - static editNotes(arg: any): void { + static showNotesPanel(arg: any): void { App.notesWindow = new BrowserWindow({ parent: App.mainWindow, width: 500, + height: 280, minimizable: false, maximizable: false, resizable: false, @@ -1789,11 +1822,11 @@ class App { App.setLocation(App.notesWindow, 'notes.html'); } - showAddNote(event: IpcMainEvent): void { - App.notesEvent = event; + showAddNote(parent: string): void { App.addNotesWindow = new BrowserWindow({ - parent: App.notesWindow, + parent: parent === 'fileInfo' ? App.fileInfoWindow : App.notesWindow, width: 350, + height: 140, minimizable: false, maximizable: false, resizable: false, @@ -1811,14 +1844,14 @@ class App { App.addNotesWindow.show(); }); App.addNotesWindow.on('close', () => { - App.notesWindow.focus(); + App.addNotesWindow.getParentWindow().focus(); }); App.setLocation(App.addNotesWindow, 'addNote.html'); } - addNewNote(arg: any): void { + addNewNote(note: string): void { + App.addNotesWindow.getParentWindow().webContents.send('set-new-note', note); App.destroyWindow(App.addNotesWindow); - App.notesEvent.sender.send('set-new-note', arg); } saveNotes(arg: any): void { @@ -1852,6 +1885,7 @@ class App { App.settingsWindow = new BrowserWindow({ parent: App.mainWindow, width: 500, + height: 250, minimizable: false, maximizable: false, resizable: false, @@ -1866,6 +1900,7 @@ class App { App.settingsWindow.loadURL('file://' + path.join(app.getAppPath(), 'html', App.lang, 'preferences.html')); App.settingsWindow.once('ready-to-show', () => { App.settingsWindow.show(); + // App.settingsWindow.webContents.openDevTools(); }); App.settingsWindow.on('close', () => { App.mainWindow.focus(); @@ -1881,6 +1916,7 @@ class App { App.newFileWindow = new BrowserWindow({ parent: App.mainWindow, width: 480, + height: 160, minimizable: false, maximizable: false, resizable: false, @@ -2030,6 +2066,7 @@ class App { App.convertCsvWindow = new BrowserWindow({ parent: App.mainWindow, width: 700, + height: 600, minimizable: false, maximizable: false, resizable: false, @@ -2055,6 +2092,7 @@ class App { App.convertTBXWindow = new BrowserWindow({ parent: App.mainWindow, width: 700, + height: 210, minimizable: false, maximizable: false, resizable: false, @@ -2080,6 +2118,7 @@ class App { App.convertSDLTMWindow = new BrowserWindow({ parent: App.mainWindow, width: 700, + height: 210, minimizable: false, maximizable: false, resizable: false, @@ -2105,6 +2144,7 @@ class App { App.convertExcelWindow = new BrowserWindow({ parent: App.mainWindow, width: 700, + height: 460, minimizable: false, maximizable: false, resizable: false, @@ -2370,10 +2410,12 @@ class App { labels.push(App.i18n.format(label, ['' + (i + 1)])); } App.csvLangArgs.labels = labels; + console.log(App.csvLangArgs); App.csvLanguagesWindow = new BrowserWindow({ parent: App.convertCsvWindow, modal: false, width: 520, + height: 300, minimizable: false, maximizable: false, resizable: false, @@ -2412,6 +2454,7 @@ class App { parent: App.convertExcelWindow, modal: false, width: 520, + height: 300, minimizable: false, maximizable: false, resizable: false, @@ -2571,10 +2614,11 @@ class App { } App.fileInfoWindow = new BrowserWindow({ parent: App.mainWindow, - width: 550, + width: 760, + height: 520, minimizable: false, maximizable: false, - resizable: true, + resizable: false, show: false, icon: App.iconPath, webPreferences: { @@ -2586,6 +2630,7 @@ class App { App.fileInfoWindow.loadURL('file://' + path.join(app.getAppPath(), 'html', App.lang, 'fileInfo.html')); App.fileInfoWindow.once('ready-to-show', () => { App.fileInfoWindow.show(); + // App.fileInfoWindow.webContents.openDevTools(); }); App.fileInfoWindow.on('close', () => { App.mainWindow.focus(); @@ -2593,10 +2638,69 @@ class App { App.setLocation(App.fileInfoWindow, 'fileInfo.html'); } + static saveFileAttributes(arg: any) { + App.sendRequest({ command: 'saveFileAttributes', attributes: arg }, + (data: any) => { + if (data.status === SUCCESS) { + App.showMessage({ parent: 'fileInfo', type: 'info', message: App.i18n.getString('App', 'fileAttributesSaved') }); + App.saved = false; + App.mainWindow.setDocumentEdited(true); + } else { + App.showMessage({ type: 'error', message: data.reason }); + } + }, + (reason: string) => { + App.showMessage({ type: 'error', message: reason }); + } + ); + } + + static saveFileProperties(properties: Array) { + App.sendRequest({ command: 'saveFileProperties', properties: properties }, + (data: any) => { + if (data.status === SUCCESS) { + App.showMessage({ parent: 'fileInfo', type: 'info', message: App.i18n.getString('App', 'filePropertiesSaved') }); + App.saved = false; + App.mainWindow.setDocumentEdited(true); + } else { + App.showMessage({ type: 'error', message: data.reason }); + } + }, + (reason: string) => { + App.showMessage({ type: 'error', message: reason }); + } + ); + } + + static saveFileNotes(notes: string[]) { + App.sendRequest({ command: 'saveFileNotes', notes: notes }, + (data: any) => { + if (data.status === SUCCESS) { + App.showMessage({ parent: 'fileInfo', type: 'info', message: App.i18n.getString('App', 'fileNotesSaved') }); + App.saved = false; + App.mainWindow.setDocumentEdited(true); + } else { + App.showMessage({ type: 'error', message: data.reason }); + } + }, + (reason: string) => { + App.showMessage({ type: 'error', message: reason }); + } + ); + } + fileProperties(event: IpcMainEvent): void { App.sendRequest({ command: 'getFileProperties' }, (data: any) => { if (data.status === SUCCESS) { + if (data.attributes.srclang && data.attributes.srclang !== '*all*') { + data.attributes.srclang = LanguageUtils.normalizeCode(data.attributes.srclang); + } + if (data.attributes.adminlang) { + data.attributes.adminlang = LanguageUtils.normalizeCode(data.attributes.adminlang); + } + data.fileLanguages = App.fileLanguages; + data.fileLanguages.unshift({ code: '*all*', name: App.i18n.getString('App', 'anyLanguage') }); event.sender.send('set-file-properties', data); } else { App.showMessage({ type: 'error', message: data.reason }); @@ -2742,6 +2846,7 @@ class App { App.splitFileWindow = new BrowserWindow({ parent: App.mainWindow, width: 504, + height: 160, minimizable: false, maximizable: false, resizable: true, @@ -2836,6 +2941,7 @@ class App { App.mergeFilesWindow = new BrowserWindow({ parent: App.mainWindow, width: 600, + height: 430, minimizable: false, maximizable: false, resizable: true, @@ -2990,6 +3096,7 @@ class App { App.replaceTextWindow = new BrowserWindow({ parent: App.mainWindow, width: 450, + height: 230, minimizable: false, maximizable: false, resizable: false, @@ -3078,6 +3185,7 @@ class App { App.sortUnitsWindow = new BrowserWindow({ parent: App.mainWindow, width: 520, + height: 160, minimizable: false, maximizable: false, resizable: false, @@ -3121,6 +3229,7 @@ class App { App.filtersWindow = new BrowserWindow({ parent: App.mainWindow, width: 520, + height: 340, minimizable: false, maximizable: false, resizable: false, @@ -3240,6 +3349,7 @@ class App { App.changeLanguageWindow = new BrowserWindow({ parent: App.mainWindow, width: 490, + height: 160, minimizable: false, maximizable: false, resizable: false, @@ -3332,6 +3442,7 @@ class App { App.removeLanguageWindow = new BrowserWindow({ parent: App.mainWindow, width: 420, + height: 120, minimizable: false, maximizable: false, resizable: false, @@ -3380,6 +3491,7 @@ class App { App.addLanguageWindow = new BrowserWindow({ parent: App.mainWindow, width: 420, + height: 120, minimizable: false, maximizable: false, resizable: false, @@ -3428,6 +3540,7 @@ class App { App.srcLanguageWindow = new BrowserWindow({ parent: App.mainWindow, width: 420, + height: 120, minimizable: false, maximizable: false, resizable: false, @@ -3594,6 +3707,7 @@ class App { App.removeUntranslatedWindow = new BrowserWindow({ parent: App.mainWindow, width: 470, + height: 120, minimizable: false, maximizable: false, resizable: false, @@ -3623,6 +3737,7 @@ class App { App.removeSameAsSourceWindow = new BrowserWindow({ parent: App.mainWindow, width: 470, + height: 120, minimizable: false, maximizable: false, resizable: false, @@ -3795,6 +3910,7 @@ class App { App.consolidateWindow = new BrowserWindow({ parent: App.mainWindow, width: 470, + height: 120, minimizable: false, maximizable: false, resizable: false, @@ -3870,6 +3986,7 @@ class App { App.maintenanceWindow = new BrowserWindow({ parent: App.mainWindow, width: 470, + height: 350, minimizable: false, maximizable: false, resizable: false, @@ -4042,6 +4159,7 @@ class App { App.updatesWindow = new BrowserWindow({ parent: App.mainWindow, width: 600, + height: 220, minimizable: false, maximizable: false, resizable: false, @@ -4056,6 +4174,7 @@ class App { App.updatesWindow.loadURL('file://' + path.join(app.getAppPath(), 'html', App.lang, 'updates.html')); App.updatesWindow.once('ready-to-show', () => { App.updatesWindow.show(); + App.updatesWindow.center(); }); App.updatesWindow.on('close', () => { App.mainWindow.focus(); diff --git a/ts/attributes.ts b/ts/attributes.ts index b4c322b..0820363 100644 --- a/ts/attributes.ts +++ b/ts/attributes.ts @@ -40,6 +40,7 @@ class Attributes { this.electron.ipcRenderer.send('close-attributes'); } }); + this.electron.ipcRenderer.send('attributes-height', { width: document.body.clientWidth, height: document.body.clientHeight }); } setUnitAttributes(arg: any): void { diff --git a/ts/convertCSV.ts b/ts/convertCSV.ts index 2a2e919..aba430d 100644 --- a/ts/convertCSV.ts +++ b/ts/convertCSV.ts @@ -68,13 +68,12 @@ class ConvertCSV { document.getElementById('convert').addEventListener('click', () => { this.convertFile(); }); - document.addEventListener('keydown', (event: KeyboardEvent) => { KeyboardHandler.keyListener(event); }); document.addEventListener('keydown', (event: KeyboardEvent) => { if (event.code === 'Escape') { this.electron.ipcRenderer.send('close-convertCsv'); } }); - this.electron.ipcRenderer.send('convertCsv-height', { width: document.body.clientWidth, height: document.body.clientHeight + 10 }); + this.electron.ipcRenderer.send('convertCsv-height', { width: document.body.clientWidth, height: document.body.clientHeight }); } setCharsets(charsets: string[]): void { diff --git a/ts/convertExcel.ts b/ts/convertExcel.ts index 86ee468..27c4f6a 100644 --- a/ts/convertExcel.ts +++ b/ts/convertExcel.ts @@ -50,7 +50,6 @@ class ConvertExcel { document.getElementById('convert').addEventListener('click', () => { this.convertExcel(); }); - document.addEventListener('keydown', (event: KeyboardEvent) => { KeyboardHandler.keyListener(event); }); document.addEventListener('keydown', (event: KeyboardEvent) => { if (event.code === 'Escape') { this.electron.ipcRenderer.send('close-convertExcel'); diff --git a/ts/convertSDLTM.ts b/ts/convertSDLTM.ts index da55cb6..f729d9a 100644 --- a/ts/convertSDLTM.ts +++ b/ts/convertSDLTM.ts @@ -37,13 +37,12 @@ class ConvertSDLTM { document.getElementById('convert').addEventListener('click', () => { this.convertFile(); }); - document.addEventListener('keydown', (event: KeyboardEvent) => { KeyboardHandler.keyListener(event); }); document.addEventListener('keydown', (event: KeyboardEvent) => { if (event.code === 'Escape') { this.electron.ipcRenderer.send('close-convertSdltm'); } }); - this.electron.ipcRenderer.send('convertSdltm-height', { width: document.body.clientWidth, height: document.body.clientHeight + 10 }); + this.electron.ipcRenderer.send('convertSdltm-height', { width: document.body.clientWidth, height: document.body.clientHeight }); } browseSdltmFiles(): void { diff --git a/ts/convertTBX.ts b/ts/convertTBX.ts index 471b086..5d84811 100644 --- a/ts/convertTBX.ts +++ b/ts/convertTBX.ts @@ -34,7 +34,6 @@ class ConvertTBX { document.getElementById('convert').addEventListener('click', () => { this.convertFile(); }); - document.addEventListener('keydown', (event: KeyboardEvent) => { KeyboardHandler.keyListener(event); }); document.addEventListener('keydown', (event: KeyboardEvent) => { if (event.code === 'Escape') { this.electron.ipcRenderer.send('close-convertTbx'); diff --git a/ts/fileInfo.ts b/ts/fileInfo.ts index d3478d8..7689e80 100644 --- a/ts/fileInfo.ts +++ b/ts/fileInfo.ts @@ -13,6 +13,11 @@ class FileInfo { electron = require('electron'); + adminLang: string = ''; + properties: Array; + propertiesChecks: HTMLInputElement[]; + notes: string[]; + notesChecks: HTMLInputElement[]; constructor() { this.electron.ipcRenderer.send('get-theme'); @@ -23,6 +28,9 @@ class FileInfo { this.electron.ipcRenderer.on('set-file-properties', (event: Electron.IpcRendererEvent, arg: any) => { this.setFileProperties(arg); }); + this.electron.ipcRenderer.on('languages-list', (event: Electron.IpcRendererEvent, arg: Language[]) => { + this.setAdminLanguages(arg); + }); document.getElementById('showAttributes').addEventListener('click', () => { this.showAttributes(); }); @@ -37,35 +45,150 @@ class FileInfo { this.electron.ipcRenderer.send('close-fileInfo'); } }); - + document.getElementById('saveAttributes').addEventListener('click', () => { + this.saveAttributes(); + }); + document.getElementById('addProperty').addEventListener('click', () => { + this.electron.ipcRenderer.send('show-add-property', 'fileInfo'); + }); + this.electron.ipcRenderer.on('set-new-property', (event: Electron.IpcRendererEvent, arg: any) => { + let prop: string[] = [arg.type, arg.value]; + this.properties.push(prop); + this.drawProperties(); + }); + document.getElementById('deleteProperties').addEventListener('click', () => { + this.deleteProperties(); + }); + document.getElementById('saveProperties').addEventListener('click', () => { + this.saveProperties(); + }); + document.getElementById('addNote').addEventListener('click', () => { + this.electron.ipcRenderer.send('show-add-note', 'fileInfo'); + }); + this.electron.ipcRenderer.on('set-new-note', (event: Electron.IpcRendererEvent, note: string) => { + this.notes.push(note); + this.drawNotes(); + }); + document.getElementById('deleteNotes').addEventListener('click', () => { + this.deleteNotes(); + }); + document.getElementById('saveNotes').addEventListener('click', () => { + this.saveNotes(); + }); } - setFileProperties(arg: any): void { - document.getElementById('creationtool').innerHTML = arg.attributes.creationtool; - document.getElementById('creationtoolversion').innerHTML = arg.attributes.creationtoolversion; - document.getElementById('segtype').innerHTML = arg.attributes.segtype; - document.getElementById('o-tmf').innerHTML = arg.attributes.o_tmf; - document.getElementById('adminlang').innerHTML = arg.attributes.adminlang; - document.getElementById('srclang').innerHTML = arg.attributes.srclang; - document.getElementById('datatype').innerHTML = arg.attributes.datatype; - - let propsContent: string = ''; - for (let pair of arg.properties) { - propsContent = propsContent + '
' + pair[0] + '' + pair[1] + '
' + note + '
' + file + '
' + pair[0] + '' + pair[1] + '