You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-09-05 20:56:22 +02:00
Compare commits
81 Commits
android-v1
...
v1.0.94
Author | SHA1 | Date | |
---|---|---|---|
|
92b71d3eb2 | ||
|
c32d7de7c4 | ||
|
c83a61d45d | ||
|
429f2d5aab | ||
|
ed70cf571c | ||
|
fd77671575 | ||
|
9d915a916e | ||
|
acb90935c7 | ||
|
6301ba0a12 | ||
|
44e1245416 | ||
|
6527d9db83 | ||
|
2bcaf62a2f | ||
|
2db3998f11 | ||
|
04724c58d1 | ||
|
48883bfa13 | ||
|
b6d9e695d1 | ||
|
43bab3c1bd | ||
|
dd67602b87 | ||
|
c4ca9cde32 | ||
|
795fd8b58c | ||
|
c226940792 | ||
|
bdd0a6106f | ||
|
d1ea7ad3ea | ||
|
a2b1181f7c | ||
|
8cce2f17d5 | ||
|
658b911513 | ||
|
3c95979d94 | ||
|
2e32211a28 | ||
|
ba2874173d | ||
|
ba9598682c | ||
|
30bfd82683 | ||
|
10c6774c28 | ||
|
c4ad9019aa | ||
|
7c99ab9947 | ||
|
feb7778fe4 | ||
|
b45185780f | ||
|
4e032c0c55 | ||
|
2e2b35dfeb | ||
|
526ef7e1d2 | ||
|
a37005446a | ||
|
e012b927dc | ||
|
359b8d5545 | ||
|
23c592b322 | ||
|
9aeddf86f4 | ||
|
0e1887988e | ||
|
394f2df664 | ||
|
2a04378a0d | ||
|
bac68f2c42 | ||
|
0f0ff86ffa | ||
|
8b38752cbf | ||
|
3c24589450 | ||
|
65065a62d8 | ||
|
482e9340bc | ||
|
69d490996e | ||
|
3494937e34 | ||
|
41ba1043be | ||
|
cc57de60c0 | ||
|
60a2b9e5c6 | ||
|
8e1fb666a5 | ||
|
f4ad777bbf | ||
|
2eacf6146a | ||
|
fe2ba34cb4 | ||
|
84daa0db61 | ||
|
b9118a90be | ||
|
ef2ffd4e52 | ||
|
5e3063abe0 | ||
|
f460b2497a | ||
|
c080d7054f | ||
|
61dd4cefbc | ||
|
63d99b2d70 | ||
|
55332d7671 | ||
|
16635defcd | ||
|
595cf3fcad | ||
|
c9b9f82130 | ||
|
f5bca733d7 | ||
|
494e235e18 | ||
|
9f2666aef9 | ||
|
f136664c11 | ||
|
c8c9f80cc5 | ||
|
69ddcc6e30 | ||
|
16554b22c7 |
@@ -801,6 +801,10 @@ msgstr "Tagy oddělujte čárkami."
|
||||
msgid "Rename notebook:"
|
||||
msgstr "Přejmenovat zápisník:"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Rename tag:"
|
||||
msgstr "Přejmenovat"
|
||||
|
||||
msgid "Set alarm:"
|
||||
msgstr "Nastavit alarm:"
|
||||
|
||||
@@ -852,10 +856,19 @@ msgstr "Otevřít..."
|
||||
msgid "Save as..."
|
||||
msgstr "Uložit jako..."
|
||||
|
||||
msgid "Copy path to clipboard"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unsupported link or message: %s"
|
||||
msgstr "Nepodporovaný link či zpráva: %s"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr "Tato poznámka je prázdný. Klikněte na \"%s\" pro otevření editoru."
|
||||
|
||||
msgid "Attach file"
|
||||
msgstr "Přiložit soubor"
|
||||
|
||||
@@ -865,12 +878,6 @@ msgstr "Tagy"
|
||||
msgid "Set alarm"
|
||||
msgstr "Nastavit alarm"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr "Tato poznámka je prázdný. Klikněte na \"%s\" pro otevření editoru."
|
||||
|
||||
msgid "to-do"
|
||||
msgstr "to-do"
|
||||
|
||||
@@ -1099,6 +1106,10 @@ msgstr "Tmavý"
|
||||
msgid "Uncompleted to-dos on top"
|
||||
msgstr "Nedokončené to-do listy nahoře"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Show completed to-dos"
|
||||
msgstr "Nedokončené to-do listy nahoře"
|
||||
|
||||
msgid "Sort notes by"
|
||||
msgstr "Řadit poznámky podle"
|
||||
|
||||
@@ -1132,9 +1143,10 @@ msgstr "Globální zoom"
|
||||
msgid "Editor font family"
|
||||
msgstr "Rodina písma v editoru"
|
||||
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"The font name will not be checked. If incorrect or empty, it will default to "
|
||||
"a generic monospace font."
|
||||
"This must be *monospace* font or it will not work properly. If the font is "
|
||||
"incorrect or empty, it will default to a generic monospace font."
|
||||
msgstr ""
|
||||
"Jméno fontu není kontrolováno. Pokud je neplatné či chybí, bude použit "
|
||||
"defaultní monospace font."
|
||||
@@ -1331,6 +1343,22 @@ msgstr "Nové tagy:"
|
||||
msgid "Type new tags or select from list"
|
||||
msgstr "Zadejte nové tagy, nebo vyberte existující ze seznamu"
|
||||
|
||||
msgid ""
|
||||
"To work correctly, the app needs the following permissions. Please enable "
|
||||
"them in your phone settings, in Apps > Joplin > Permissions"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"- Storage: to allow attaching files to notes and to enable filesystem "
|
||||
"synchronisation."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Camera: to allow taking a picture and attaching it to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Location: to allow attaching geo-location information to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Joplin website"
|
||||
msgstr "Web Joplinu"
|
||||
|
||||
@@ -1395,6 +1423,9 @@ msgstr "Přiložit obrázek"
|
||||
msgid "Attach any file"
|
||||
msgstr "Přiložit soubor"
|
||||
|
||||
msgid "Share"
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert to note"
|
||||
msgstr "Konvertovat na poznámku"
|
||||
|
||||
|
@@ -810,6 +810,10 @@ msgstr "Adskil hver mærke med komma."
|
||||
msgid "Rename notebook:"
|
||||
msgstr "Omdøb notesbog:"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Rename tag:"
|
||||
msgstr "Omdøb"
|
||||
|
||||
msgid "Set alarm:"
|
||||
msgstr "Indstil alarm:"
|
||||
|
||||
@@ -861,10 +865,19 @@ msgstr "Åben..."
|
||||
msgid "Save as..."
|
||||
msgstr "Gem som..."
|
||||
|
||||
msgid "Copy path to clipboard"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unsupported link or message: %s"
|
||||
msgstr "Ugyldigt- eller ulovligt link eller besked: %s"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr "Denne note er tom. Klik på \"%s\" for at starte editor og rette noten."
|
||||
|
||||
msgid "Attach file"
|
||||
msgstr "Vedhæft fil"
|
||||
|
||||
@@ -874,12 +887,6 @@ msgstr "Mærker"
|
||||
msgid "Set alarm"
|
||||
msgstr "Indstil alarm"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr "Denne note er tom. Klik på \"%s\" for at starte editor og rette noten."
|
||||
|
||||
msgid "to-do"
|
||||
msgstr "opgave"
|
||||
|
||||
@@ -1108,6 +1115,10 @@ msgstr "Mørkt"
|
||||
msgid "Uncompleted to-dos on top"
|
||||
msgstr "Ufærdige opgaver øverst"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Show completed to-dos"
|
||||
msgstr "Ufærdige opgaver øverst"
|
||||
|
||||
msgid "Sort notes by"
|
||||
msgstr "Sorter noter efter"
|
||||
|
||||
@@ -1141,9 +1152,10 @@ msgstr "Global zoom procent"
|
||||
msgid "Editor font family"
|
||||
msgstr "Rediger skrifttype"
|
||||
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"The font name will not be checked. If incorrect or empty, it will default to "
|
||||
"a generic monospace font."
|
||||
"This must be *monospace* font or it will not work properly. If the font is "
|
||||
"incorrect or empty, it will default to a generic monospace font."
|
||||
msgstr ""
|
||||
"Skrifttypenavn checkes ikke. Hvis tom eller ukorrekt, standard vil returnere "
|
||||
"til monospace font."
|
||||
@@ -1340,6 +1352,22 @@ msgstr "Nye tags:"
|
||||
msgid "Type new tags or select from list"
|
||||
msgstr "Indtast nye tags eller vælg fra listen"
|
||||
|
||||
msgid ""
|
||||
"To work correctly, the app needs the following permissions. Please enable "
|
||||
"them in your phone settings, in Apps > Joplin > Permissions"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"- Storage: to allow attaching files to notes and to enable filesystem "
|
||||
"synchronisation."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Camera: to allow taking a picture and attaching it to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Location: to allow attaching geo-location information to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Joplin website"
|
||||
msgstr "Joplin hjemmeside"
|
||||
|
||||
@@ -1404,6 +1432,9 @@ msgstr "Vedhæft foto"
|
||||
msgid "Attach any file"
|
||||
msgstr "Vedhæft fil"
|
||||
|
||||
msgid "Share"
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert to note"
|
||||
msgstr "Konverter til note"
|
||||
|
||||
|
@@ -835,6 +835,10 @@ msgstr "Trenne jede Markierung mit einem Komma."
|
||||
msgid "Rename notebook:"
|
||||
msgstr "Notizbuch umbenennen:"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Rename tag:"
|
||||
msgstr "Umbenennen"
|
||||
|
||||
msgid "Set alarm:"
|
||||
msgstr "Alarm erstellen:"
|
||||
|
||||
@@ -889,10 +893,21 @@ msgstr "Öffne..."
|
||||
msgid "Save as..."
|
||||
msgstr "Sichern unter..."
|
||||
|
||||
msgid "Copy path to clipboard"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unsupported link or message: %s"
|
||||
msgstr "Nicht unterstützter Link oder Nachricht: %s"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
"Diese Notiz hat keinen Inhalt. Klicke auf „%s“ um den Editor zu aktivieren "
|
||||
"und die Notiz zu bearbeiten."
|
||||
|
||||
msgid "Attach file"
|
||||
msgstr "Datei anhängen"
|
||||
|
||||
@@ -902,14 +917,6 @@ msgstr "Markierungen"
|
||||
msgid "Set alarm"
|
||||
msgstr "Alarm erstellen"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
"Diese Notiz hat keinen Inhalt. Klicke auf „%s“ um den Editor zu aktivieren "
|
||||
"und die Notiz zu bearbeiten."
|
||||
|
||||
msgid "to-do"
|
||||
msgstr "To-Do"
|
||||
|
||||
@@ -1141,6 +1148,10 @@ msgstr "Dunkel"
|
||||
msgid "Uncompleted to-dos on top"
|
||||
msgstr "Zeige unvollständige To-Dos an oberster Stelle"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Show completed to-dos"
|
||||
msgstr "Zeige unvollständige To-Dos an oberster Stelle"
|
||||
|
||||
msgid "Sort notes by"
|
||||
msgstr "Sortiere Notizen nach"
|
||||
|
||||
@@ -1174,9 +1185,10 @@ msgstr "Zoomstufe der Benutzeroberfläche"
|
||||
msgid "Editor font family"
|
||||
msgstr "Editor Schriftenfamilie"
|
||||
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"The font name will not be checked. If incorrect or empty, it will default to "
|
||||
"a generic monospace font."
|
||||
"This must be *monospace* font or it will not work properly. If the font is "
|
||||
"incorrect or empty, it will default to a generic monospace font."
|
||||
msgstr ""
|
||||
"Der Name der Schrift wird nicht überprüft. Ist dieser inkorrekt oder leer "
|
||||
"wird eine generische Monospace Schrift verwendet."
|
||||
@@ -1377,6 +1389,22 @@ msgstr "Neue Markierungen:"
|
||||
msgid "Type new tags or select from list"
|
||||
msgstr "Neue Markierungen eingeben oder aus der Liste auswählen"
|
||||
|
||||
msgid ""
|
||||
"To work correctly, the app needs the following permissions. Please enable "
|
||||
"them in your phone settings, in Apps > Joplin > Permissions"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"- Storage: to allow attaching files to notes and to enable filesystem "
|
||||
"synchronisation."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Camera: to allow taking a picture and attaching it to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Location: to allow attaching geo-location information to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Joplin website"
|
||||
msgstr "Website von Joplin"
|
||||
|
||||
@@ -1440,6 +1468,9 @@ msgstr "Foto anhängen"
|
||||
msgid "Attach any file"
|
||||
msgstr "Beliebige Datei anhängen"
|
||||
|
||||
msgid "Share"
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert to note"
|
||||
msgstr "In eine Notiz umwandeln"
|
||||
|
||||
|
@@ -728,6 +728,9 @@ msgstr ""
|
||||
msgid "Rename notebook:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Rename tag:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Set alarm:"
|
||||
msgstr ""
|
||||
|
||||
@@ -777,10 +780,19 @@ msgstr ""
|
||||
msgid "Save as..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Copy path to clipboard"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unsupported link or message: %s"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Attach file"
|
||||
msgstr ""
|
||||
|
||||
@@ -790,12 +802,6 @@ msgstr ""
|
||||
msgid "Set alarm"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
|
||||
msgid "to-do"
|
||||
msgstr ""
|
||||
|
||||
@@ -1013,6 +1019,9 @@ msgstr ""
|
||||
msgid "Uncompleted to-dos on top"
|
||||
msgstr ""
|
||||
|
||||
msgid "Show completed to-dos"
|
||||
msgstr ""
|
||||
|
||||
msgid "Sort notes by"
|
||||
msgstr ""
|
||||
|
||||
@@ -1047,8 +1056,8 @@ msgid "Editor font family"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The font name will not be checked. If incorrect or empty, it will default to "
|
||||
"a generic monospace font."
|
||||
"This must be *monospace* font or it will not work properly. If the font is "
|
||||
"incorrect or empty, it will default to a generic monospace font."
|
||||
msgstr ""
|
||||
|
||||
msgid "Automatically update the application"
|
||||
@@ -1234,6 +1243,22 @@ msgstr ""
|
||||
msgid "Type new tags or select from list"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"To work correctly, the app needs the following permissions. Please enable "
|
||||
"them in your phone settings, in Apps > Joplin > Permissions"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"- Storage: to allow attaching files to notes and to enable filesystem "
|
||||
"synchronisation."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Camera: to allow taking a picture and attaching it to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Location: to allow attaching geo-location information to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Joplin website"
|
||||
msgstr ""
|
||||
|
||||
@@ -1297,6 +1322,9 @@ msgstr ""
|
||||
msgid "Attach any file"
|
||||
msgstr ""
|
||||
|
||||
msgid "Share"
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert to note"
|
||||
msgstr ""
|
||||
|
||||
|
@@ -368,13 +368,12 @@ msgstr "Elimina la libreta dada."
|
||||
msgid "Deletes the notebook without asking for confirmation."
|
||||
msgstr "Elimina una libreta sin pedir confirmación."
|
||||
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"Delete notebook? All notes and sub-notebooks within this notebook will also "
|
||||
"be deleted."
|
||||
msgstr ""
|
||||
"¿Desea eliminar la libreta? Todas las notas dentro de esta libreta también "
|
||||
"serán eliminadas."
|
||||
"¿Desea eliminar la libreta? Todas las notas y sublibretas dentro de esta "
|
||||
"libreta también serán eliminadas."
|
||||
|
||||
msgid "Deletes the notes matching <note-pattern>."
|
||||
msgstr "Elimina las notas que coinciden con <note-pattern>."
|
||||
@@ -820,6 +819,10 @@ msgstr "Separar cada etiqueta por una coma."
|
||||
msgid "Rename notebook:"
|
||||
msgstr "Renombrar libreta:"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Rename tag:"
|
||||
msgstr "Renombrar"
|
||||
|
||||
msgid "Set alarm:"
|
||||
msgstr "Ajustar alarma:"
|
||||
|
||||
@@ -869,10 +872,21 @@ msgstr "Abrir..."
|
||||
msgid "Save as..."
|
||||
msgstr "Guardar como..."
|
||||
|
||||
msgid "Copy path to clipboard"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unsupported link or message: %s"
|
||||
msgstr "Enlace o mensaje no soportado: %s"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
"Esta nota no tiene contenido. Pulse en \"%s\" para cambiar al editor y "
|
||||
"editar la nota."
|
||||
|
||||
msgid "Attach file"
|
||||
msgstr "Adjuntar archivo"
|
||||
|
||||
@@ -882,14 +896,6 @@ msgstr "Etiquetas"
|
||||
msgid "Set alarm"
|
||||
msgstr "Establecer alarma"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
"Esta nota no tiene contenido. Pulse en \"%s\" para cambiar al editor y "
|
||||
"editar la nota."
|
||||
|
||||
msgid "to-do"
|
||||
msgstr "lista de tareas"
|
||||
|
||||
@@ -1061,9 +1067,8 @@ msgstr "Los elementos cifrados no pueden ser modificados"
|
||||
msgid "Conflicts"
|
||||
msgstr "Conflictos"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Cannot move notebook to this location"
|
||||
msgstr "No se ha podido mover la nota a la libreta «%s»"
|
||||
msgstr "No se puede mover la libreta a este lugar"
|
||||
|
||||
#, javascript-format
|
||||
msgid "A notebook with this title already exists: \"%s\""
|
||||
@@ -1119,6 +1124,10 @@ msgstr "Oscuro"
|
||||
msgid "Uncompleted to-dos on top"
|
||||
msgstr "Mostrar tareas incompletas al inicio de las listas"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Show completed to-dos"
|
||||
msgstr "Mostrar tareas incompletas al inicio de las listas"
|
||||
|
||||
msgid "Sort notes by"
|
||||
msgstr "Ordenar notas por"
|
||||
|
||||
@@ -1152,9 +1161,10 @@ msgstr "Establecer el porcentaje de aumento de la aplicación"
|
||||
msgid "Editor font family"
|
||||
msgstr "Fuente del editor"
|
||||
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"The font name will not be checked. If incorrect or empty, it will default to "
|
||||
"a generic monospace font."
|
||||
"This must be *monospace* font or it will not work properly. If the font is "
|
||||
"incorrect or empty, it will default to a generic monospace font."
|
||||
msgstr ""
|
||||
"El nombre de la fuente no se comprobado. Si es incorrecto o está vacío, se "
|
||||
"utilizará una fuente genérica monoespaciada."
|
||||
@@ -1352,6 +1362,22 @@ msgstr "Nuevas etiquetas:"
|
||||
msgid "Type new tags or select from list"
|
||||
msgstr "Escriba nuevas etiquetas o seleccionelas de la lista"
|
||||
|
||||
msgid ""
|
||||
"To work correctly, the app needs the following permissions. Please enable "
|
||||
"them in your phone settings, in Apps > Joplin > Permissions"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"- Storage: to allow attaching files to notes and to enable filesystem "
|
||||
"synchronisation."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Camera: to allow taking a picture and attaching it to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Location: to allow attaching geo-location information to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Joplin website"
|
||||
msgstr "Sitio web de Joplin"
|
||||
|
||||
@@ -1416,6 +1442,9 @@ msgstr "Adjuntar foto"
|
||||
msgid "Attach any file"
|
||||
msgstr "Adjuntar cualquier archivo"
|
||||
|
||||
msgid "Share"
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert to note"
|
||||
msgstr "Convertir a nota"
|
||||
|
||||
|
@@ -820,6 +820,10 @@ msgstr "Banatu etiketak koma erabiliaz."
|
||||
msgid "Rename notebook:"
|
||||
msgstr "Berrizendatu koadernoa:"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Rename tag:"
|
||||
msgstr "Berrizendatu"
|
||||
|
||||
msgid "Set alarm:"
|
||||
msgstr "Ezarri alarma:"
|
||||
|
||||
@@ -871,10 +875,19 @@ msgstr ""
|
||||
msgid "Save as..."
|
||||
msgstr "Gorde aldaketak"
|
||||
|
||||
msgid "Copy path to clipboard"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unsupported link or message: %s"
|
||||
msgstr "Esteka edo mezu ez dago onartua: %s"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Attach file"
|
||||
msgstr "Erantsi fitxategia"
|
||||
|
||||
@@ -884,12 +897,6 @@ msgstr "Etiketak"
|
||||
msgid "Set alarm"
|
||||
msgstr "Ezarri alarma"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "to-do"
|
||||
msgstr "Zeregin berria"
|
||||
@@ -1125,6 +1132,10 @@ msgstr "Iluna"
|
||||
msgid "Uncompleted to-dos on top"
|
||||
msgstr "Bete gabeko zereginak erakutsi zerrendaren goiko partean"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Show completed to-dos"
|
||||
msgstr "Bete gabeko zereginak erakutsi zerrendaren goiko partean"
|
||||
|
||||
msgid "Sort notes by"
|
||||
msgstr ""
|
||||
|
||||
@@ -1163,8 +1174,8 @@ msgid "Editor font family"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The font name will not be checked. If incorrect or empty, it will default to "
|
||||
"a generic monospace font."
|
||||
"This must be *monospace* font or it will not work properly. If the font is "
|
||||
"incorrect or empty, it will default to a generic monospace font."
|
||||
msgstr ""
|
||||
|
||||
msgid "Automatically update the application"
|
||||
@@ -1362,6 +1373,22 @@ msgstr ""
|
||||
msgid "Type new tags or select from list"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"To work correctly, the app needs the following permissions. Please enable "
|
||||
"them in your phone settings, in Apps > Joplin > Permissions"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"- Storage: to allow attaching files to notes and to enable filesystem "
|
||||
"synchronisation."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Camera: to allow taking a picture and attaching it to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Location: to allow attaching geo-location information to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Joplin website"
|
||||
msgstr ""
|
||||
|
||||
@@ -1426,6 +1453,9 @@ msgstr "Argazkia erantsi"
|
||||
msgid "Attach any file"
|
||||
msgstr "Erantsi fitxategiren bat"
|
||||
|
||||
msgid "Share"
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert to note"
|
||||
msgstr "Oharra bihurtu"
|
||||
|
||||
|
@@ -768,7 +768,7 @@ msgid ""
|
||||
"how the notes or notebooks were originally encrypted."
|
||||
msgstr ""
|
||||
"Note : seule une clef maître va être utilisée pour le cryptage (celle "
|
||||
"marquée comme \"actif\" ci-dessus). N'importe quel clef peut-être utilisée "
|
||||
"marquée comme \"actif\" ci-dessus). N'importe quelle clef peut être utilisée "
|
||||
"pour le décryptage, selon la façon dont les notes ou carnets étaient cryptés "
|
||||
"à l'origine."
|
||||
|
||||
@@ -825,6 +825,9 @@ msgstr "Séparez chaque étiquette par une virgule."
|
||||
msgid "Rename notebook:"
|
||||
msgstr "Renommer le carnet :"
|
||||
|
||||
msgid "Rename tag:"
|
||||
msgstr "Renommer étiquette :"
|
||||
|
||||
msgid "Set alarm:"
|
||||
msgstr "Régler alarme :"
|
||||
|
||||
@@ -877,10 +880,21 @@ msgstr "Ouvrir..."
|
||||
msgid "Save as..."
|
||||
msgstr "Enregistrer sous..."
|
||||
|
||||
msgid "Copy path to clipboard"
|
||||
msgstr "Copier le chemin"
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unsupported link or message: %s"
|
||||
msgstr "Lien ou message non géré : %s"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
"Cette note n'a pas de contenu. Cliquer sur \"%s\" pour basculer vers "
|
||||
"l'éditeur et éditer cette note."
|
||||
|
||||
msgid "Attach file"
|
||||
msgstr "Attacher un fichier"
|
||||
|
||||
@@ -890,14 +904,6 @@ msgstr "Étiquettes"
|
||||
msgid "Set alarm"
|
||||
msgstr "Régler alarme"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
"Cette note n'a pas de contenu. Cliquer sur \"%s\" pour basculer vers "
|
||||
"l'éditeur et éditer cette note."
|
||||
|
||||
msgid "to-do"
|
||||
msgstr "tâche"
|
||||
|
||||
@@ -1126,6 +1132,9 @@ msgstr "Sombre"
|
||||
msgid "Uncompleted to-dos on top"
|
||||
msgstr "Tâches non-terminées en haut"
|
||||
|
||||
msgid "Show completed to-dos"
|
||||
msgstr "Afficher les tâches complétées"
|
||||
|
||||
msgid "Sort notes by"
|
||||
msgstr "Trier les notes par"
|
||||
|
||||
@@ -1160,10 +1169,11 @@ msgid "Editor font family"
|
||||
msgstr "Police de l'éditeur"
|
||||
|
||||
msgid ""
|
||||
"The font name will not be checked. If incorrect or empty, it will default to "
|
||||
"a generic monospace font."
|
||||
"This must be *monospace* font or it will not work properly. If the font is "
|
||||
"incorrect or empty, it will default to a generic monospace font."
|
||||
msgstr ""
|
||||
"Le nom de la police ne sera pas vérifié. Si incorrect ou vide une police "
|
||||
"Il faut que ce soit une police *monospace* ou cela ne fonctionnera pas "
|
||||
"correctement. Si la police est incorrecte ou non-spécifiée, une police "
|
||||
"monospace sera utilisée par défaut."
|
||||
|
||||
msgid "Automatically update the application"
|
||||
@@ -1361,6 +1371,28 @@ msgstr "Nouvelles étiquettes :"
|
||||
msgid "Type new tags or select from list"
|
||||
msgstr "Entrez de nouvelles étiquettes ou sélectionnez de la liste"
|
||||
|
||||
msgid ""
|
||||
"To work correctly, the app needs the following permissions. Please enable "
|
||||
"them in your phone settings, in Apps > Joplin > Permissions"
|
||||
msgstr ""
|
||||
"Pour fonctionner correctement, l'appli a besoin des autorisations suivantes. "
|
||||
"Veuillez les activer dans les paramètres de votre téléphone, dans le menu "
|
||||
"Applications > Joplin > Autorisations"
|
||||
|
||||
msgid ""
|
||||
"- Storage: to allow attaching files to notes and to enable filesystem "
|
||||
"synchronisation."
|
||||
msgstr ""
|
||||
"- Stockage : Pour attacher des fichiers aux notes et pour activer la "
|
||||
"synchronisation vers le système de fichier."
|
||||
|
||||
msgid "- Camera: to allow taking a picture and attaching it to a note."
|
||||
msgstr ""
|
||||
"- Appareil photo : Pour pouvoir prendre une photo et l'attacher à une note."
|
||||
|
||||
msgid "- Location: to allow attaching geo-location information to a note."
|
||||
msgstr "- Position : Pour attacher à une note les coordonnées GPS."
|
||||
|
||||
msgid "Joplin website"
|
||||
msgstr "Site web de Joplin"
|
||||
|
||||
@@ -1425,6 +1457,9 @@ msgstr "Attacher une photo"
|
||||
msgid "Attach any file"
|
||||
msgstr "Attacher un fichier"
|
||||
|
||||
msgid "Share"
|
||||
msgstr "Partager"
|
||||
|
||||
msgid "Convert to note"
|
||||
msgstr "Convertir en note"
|
||||
|
||||
|
@@ -808,6 +808,10 @@ msgstr "Separar cada etiqueta por unha coma."
|
||||
msgid "Rename notebook:"
|
||||
msgstr "Renomear caderno:"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Rename tag:"
|
||||
msgstr "Renomear"
|
||||
|
||||
msgid "Set alarm:"
|
||||
msgstr "Estabelecer alarma:"
|
||||
|
||||
@@ -858,10 +862,21 @@ msgstr "Abrir…"
|
||||
msgid "Save as..."
|
||||
msgstr "Gardar como…"
|
||||
|
||||
msgid "Copy path to clipboard"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unsupported link or message: %s"
|
||||
msgstr "Ligazón ou mensaxe incompatíbeis: %s"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
"Esta nota non ten contido. Prema en «%s» para ir ao editor e modificar a "
|
||||
"nota."
|
||||
|
||||
msgid "Attach file"
|
||||
msgstr "Anexar un ficheiro"
|
||||
|
||||
@@ -871,14 +886,6 @@ msgstr "Etiquetas"
|
||||
msgid "Set alarm"
|
||||
msgstr "Estabelecer alarma"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
"Esta nota non ten contido. Prema en «%s» para ir ao editor e modificar a "
|
||||
"nota."
|
||||
|
||||
msgid "to-do"
|
||||
msgstr "tarefas pendentes"
|
||||
|
||||
@@ -1107,6 +1114,10 @@ msgstr "Escuro"
|
||||
msgid "Uncompleted to-dos on top"
|
||||
msgstr "Tarefas sen completar arriba"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Show completed to-dos"
|
||||
msgstr "Tarefas sen completar arriba"
|
||||
|
||||
msgid "Sort notes by"
|
||||
msgstr "Ordenar notas por"
|
||||
|
||||
@@ -1140,9 +1151,10 @@ msgstr "Porcentaxe de ampliación"
|
||||
msgid "Editor font family"
|
||||
msgstr "Familia de tipos de letra do editor"
|
||||
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"The font name will not be checked. If incorrect or empty, it will default to "
|
||||
"a generic monospace font."
|
||||
"This must be *monospace* font or it will not work properly. If the font is "
|
||||
"incorrect or empty, it will default to a generic monospace font."
|
||||
msgstr ""
|
||||
"Non se comprobará o nome da fonte. Se é incorrecto ou está baleiro, "
|
||||
"estabelecerase por omisión un tipo de letra xenérico de largura única."
|
||||
@@ -1339,6 +1351,22 @@ msgstr "Etiquetas novas:"
|
||||
msgid "Type new tags or select from list"
|
||||
msgstr "Escriba etiquetas novas ou seleccione da lista"
|
||||
|
||||
msgid ""
|
||||
"To work correctly, the app needs the following permissions. Please enable "
|
||||
"them in your phone settings, in Apps > Joplin > Permissions"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"- Storage: to allow attaching files to notes and to enable filesystem "
|
||||
"synchronisation."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Camera: to allow taking a picture and attaching it to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Location: to allow attaching geo-location information to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Joplin website"
|
||||
msgstr "Sitio web de Joplin"
|
||||
|
||||
@@ -1403,6 +1431,9 @@ msgstr "Anexar foto"
|
||||
msgid "Attach any file"
|
||||
msgstr "Adxuntar calquera ficheiro"
|
||||
|
||||
msgid "Share"
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert to note"
|
||||
msgstr "Converter para nota"
|
||||
|
||||
|
@@ -807,6 +807,10 @@ msgstr "Odvoji oznake zarezom."
|
||||
msgid "Rename notebook:"
|
||||
msgstr "Preimenuj bilježnicu:"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Rename tag:"
|
||||
msgstr "Preimenuj"
|
||||
|
||||
msgid "Set alarm:"
|
||||
msgstr "Postavi upozorenje:"
|
||||
|
||||
@@ -859,10 +863,19 @@ msgstr ""
|
||||
msgid "Save as..."
|
||||
msgstr "Spremi promjene"
|
||||
|
||||
msgid "Copy path to clipboard"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unsupported link or message: %s"
|
||||
msgstr "Nepodržana poveznica ili poruka: %s"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Attach file"
|
||||
msgstr "Priloži datoteku"
|
||||
|
||||
@@ -872,12 +885,6 @@ msgstr "Oznake"
|
||||
msgid "Set alarm"
|
||||
msgstr "Postavi upozorenje"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "to-do"
|
||||
msgstr "Novi zadatak"
|
||||
@@ -1108,6 +1115,10 @@ msgstr "Tamna"
|
||||
msgid "Uncompleted to-dos on top"
|
||||
msgstr "Prikaži nezavršene zadatke na vrhu liste"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Show completed to-dos"
|
||||
msgstr "Prikaži nezavršene zadatke na vrhu liste"
|
||||
|
||||
msgid "Sort notes by"
|
||||
msgstr ""
|
||||
|
||||
@@ -1146,8 +1157,8 @@ msgid "Editor font family"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The font name will not be checked. If incorrect or empty, it will default to "
|
||||
"a generic monospace font."
|
||||
"This must be *monospace* font or it will not work properly. If the font is "
|
||||
"incorrect or empty, it will default to a generic monospace font."
|
||||
msgstr ""
|
||||
|
||||
msgid "Automatically update the application"
|
||||
@@ -1338,6 +1349,22 @@ msgstr ""
|
||||
msgid "Type new tags or select from list"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"To work correctly, the app needs the following permissions. Please enable "
|
||||
"them in your phone settings, in Apps > Joplin > Permissions"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"- Storage: to allow attaching files to notes and to enable filesystem "
|
||||
"synchronisation."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Camera: to allow taking a picture and attaching it to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Location: to allow attaching geo-location information to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Joplin website"
|
||||
msgstr ""
|
||||
|
||||
@@ -1403,6 +1430,9 @@ msgstr "Priloži sliku"
|
||||
msgid "Attach any file"
|
||||
msgstr "Priloži datoteku"
|
||||
|
||||
msgid "Share"
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert to note"
|
||||
msgstr "Pretvori u bilješku"
|
||||
|
||||
|
@@ -792,6 +792,10 @@ msgstr "Separa ogni etichetta da una virgola."
|
||||
msgid "Rename notebook:"
|
||||
msgstr "Rinomina il blocco note:"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Rename tag:"
|
||||
msgstr "Rinomina"
|
||||
|
||||
msgid "Set alarm:"
|
||||
msgstr "Imposta allarme:"
|
||||
|
||||
@@ -845,10 +849,19 @@ msgstr ""
|
||||
msgid "Save as..."
|
||||
msgstr "Salva i cambiamenti"
|
||||
|
||||
msgid "Copy path to clipboard"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unsupported link or message: %s"
|
||||
msgstr "Collegamento o messaggio non supportato: %s"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Attach file"
|
||||
msgstr "Allega file"
|
||||
|
||||
@@ -858,12 +871,6 @@ msgstr "Etichette"
|
||||
msgid "Set alarm"
|
||||
msgstr "Imposta allarme"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "to-do"
|
||||
msgstr "Nuova attività"
|
||||
@@ -1097,6 +1104,10 @@ msgstr "Scuro"
|
||||
msgid "Uncompleted to-dos on top"
|
||||
msgstr "Mostra todo inclompleti in cima alla lista"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Show completed to-dos"
|
||||
msgstr "Mostra todo inclompleti in cima alla lista"
|
||||
|
||||
msgid "Sort notes by"
|
||||
msgstr ""
|
||||
|
||||
@@ -1135,8 +1146,8 @@ msgid "Editor font family"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The font name will not be checked. If incorrect or empty, it will default to "
|
||||
"a generic monospace font."
|
||||
"This must be *monospace* font or it will not work properly. If the font is "
|
||||
"incorrect or empty, it will default to a generic monospace font."
|
||||
msgstr ""
|
||||
|
||||
msgid "Automatically update the application"
|
||||
@@ -1327,6 +1338,22 @@ msgstr ""
|
||||
msgid "Type new tags or select from list"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"To work correctly, the app needs the following permissions. Please enable "
|
||||
"them in your phone settings, in Apps > Joplin > Permissions"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"- Storage: to allow attaching files to notes and to enable filesystem "
|
||||
"synchronisation."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Camera: to allow taking a picture and attaching it to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Location: to allow attaching geo-location information to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Joplin website"
|
||||
msgstr ""
|
||||
|
||||
@@ -1392,6 +1419,9 @@ msgstr "Allega foto"
|
||||
msgid "Attach any file"
|
||||
msgstr "Allega qualsiasi file"
|
||||
|
||||
msgid "Share"
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert to note"
|
||||
msgstr "Converti in nota"
|
||||
|
||||
|
@@ -797,6 +797,10 @@ msgstr "それぞれのタグをカンマ(,)で区切ってください。"
|
||||
msgid "Rename notebook:"
|
||||
msgstr "ノートブックの名前を変更:"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Rename tag:"
|
||||
msgstr "名前の変更"
|
||||
|
||||
msgid "Set alarm:"
|
||||
msgstr "アラームをセット:"
|
||||
|
||||
@@ -849,10 +853,19 @@ msgstr ""
|
||||
msgid "Save as..."
|
||||
msgstr "変更を保存"
|
||||
|
||||
msgid "Copy path to clipboard"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unsupported link or message: %s"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Attach file"
|
||||
msgstr "ファイルを添付"
|
||||
|
||||
@@ -862,12 +875,6 @@ msgstr "タグ"
|
||||
msgid "Set alarm"
|
||||
msgstr "アラームをセット"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "to-do"
|
||||
msgstr "新しいToDo"
|
||||
@@ -1102,6 +1109,10 @@ msgstr "暗い"
|
||||
msgid "Uncompleted to-dos on top"
|
||||
msgstr "未完のToDoをリストの上部に表示"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Show completed to-dos"
|
||||
msgstr "未完のToDoをリストの上部に表示"
|
||||
|
||||
msgid "Sort notes by"
|
||||
msgstr ""
|
||||
|
||||
@@ -1140,8 +1151,8 @@ msgid "Editor font family"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The font name will not be checked. If incorrect or empty, it will default to "
|
||||
"a generic monospace font."
|
||||
"This must be *monospace* font or it will not work properly. If the font is "
|
||||
"incorrect or empty, it will default to a generic monospace font."
|
||||
msgstr ""
|
||||
|
||||
msgid "Automatically update the application"
|
||||
@@ -1332,6 +1343,22 @@ msgstr ""
|
||||
msgid "Type new tags or select from list"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"To work correctly, the app needs the following permissions. Please enable "
|
||||
"them in your phone settings, in Apps > Joplin > Permissions"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"- Storage: to allow attaching files to notes and to enable filesystem "
|
||||
"synchronisation."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Camera: to allow taking a picture and attaching it to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Location: to allow attaching geo-location information to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Joplin website"
|
||||
msgstr ""
|
||||
|
||||
@@ -1397,6 +1424,9 @@ msgstr "写真を添付"
|
||||
msgid "Attach any file"
|
||||
msgstr "ファイルを添付"
|
||||
|
||||
msgid "Share"
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert to note"
|
||||
msgstr "ノートに変換"
|
||||
|
||||
|
@@ -728,6 +728,9 @@ msgstr ""
|
||||
msgid "Rename notebook:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Rename tag:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Set alarm:"
|
||||
msgstr ""
|
||||
|
||||
@@ -777,10 +780,19 @@ msgstr ""
|
||||
msgid "Save as..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Copy path to clipboard"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unsupported link or message: %s"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Attach file"
|
||||
msgstr ""
|
||||
|
||||
@@ -790,12 +802,6 @@ msgstr ""
|
||||
msgid "Set alarm"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
|
||||
msgid "to-do"
|
||||
msgstr ""
|
||||
|
||||
@@ -1013,6 +1019,9 @@ msgstr ""
|
||||
msgid "Uncompleted to-dos on top"
|
||||
msgstr ""
|
||||
|
||||
msgid "Show completed to-dos"
|
||||
msgstr ""
|
||||
|
||||
msgid "Sort notes by"
|
||||
msgstr ""
|
||||
|
||||
@@ -1047,8 +1056,8 @@ msgid "Editor font family"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The font name will not be checked. If incorrect or empty, it will default to "
|
||||
"a generic monospace font."
|
||||
"This must be *monospace* font or it will not work properly. If the font is "
|
||||
"incorrect or empty, it will default to a generic monospace font."
|
||||
msgstr ""
|
||||
|
||||
msgid "Automatically update the application"
|
||||
@@ -1234,6 +1243,22 @@ msgstr ""
|
||||
msgid "Type new tags or select from list"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"To work correctly, the app needs the following permissions. Please enable "
|
||||
"them in your phone settings, in Apps > Joplin > Permissions"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"- Storage: to allow attaching files to notes and to enable filesystem "
|
||||
"synchronisation."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Camera: to allow taking a picture and attaching it to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Location: to allow attaching geo-location information to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Joplin website"
|
||||
msgstr ""
|
||||
|
||||
@@ -1297,6 +1322,9 @@ msgstr ""
|
||||
msgid "Attach any file"
|
||||
msgstr ""
|
||||
|
||||
msgid "Share"
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert to note"
|
||||
msgstr ""
|
||||
|
||||
|
@@ -822,6 +822,10 @@ msgstr "Scheid iedere tag met een komma."
|
||||
msgid "Rename notebook:"
|
||||
msgstr "Hernoem notitieboek:"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Rename tag:"
|
||||
msgstr "Hernoem"
|
||||
|
||||
msgid "Set alarm:"
|
||||
msgstr "Stel melding in:"
|
||||
|
||||
@@ -875,10 +879,19 @@ msgstr ""
|
||||
msgid "Save as..."
|
||||
msgstr "Sla wijzigingen op"
|
||||
|
||||
msgid "Copy path to clipboard"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unsupported link or message: %s"
|
||||
msgstr "Link of bericht \"%s\" wordt niet ondersteund"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Attach file"
|
||||
msgstr "Voeg bestand toe"
|
||||
|
||||
@@ -888,12 +901,6 @@ msgstr "Tags"
|
||||
msgid "Set alarm"
|
||||
msgstr "Zet melding"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "to-do"
|
||||
msgstr "Nieuwe to-do"
|
||||
@@ -1129,6 +1136,10 @@ msgstr "Donker"
|
||||
msgid "Uncompleted to-dos on top"
|
||||
msgstr "Toon onvoltooide to-do's aan de top van de lijsten"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Show completed to-dos"
|
||||
msgstr "Toon onvoltooide to-do's aan de top van de lijsten"
|
||||
|
||||
msgid "Sort notes by"
|
||||
msgstr ""
|
||||
|
||||
@@ -1166,8 +1177,8 @@ msgid "Editor font family"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The font name will not be checked. If incorrect or empty, it will default to "
|
||||
"a generic monospace font."
|
||||
"This must be *monospace* font or it will not work properly. If the font is "
|
||||
"incorrect or empty, it will default to a generic monospace font."
|
||||
msgstr ""
|
||||
|
||||
msgid "Automatically update the application"
|
||||
@@ -1364,6 +1375,22 @@ msgstr ""
|
||||
msgid "Type new tags or select from list"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"To work correctly, the app needs the following permissions. Please enable "
|
||||
"them in your phone settings, in Apps > Joplin > Permissions"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"- Storage: to allow attaching files to notes and to enable filesystem "
|
||||
"synchronisation."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Camera: to allow taking a picture and attaching it to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Location: to allow attaching geo-location information to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Joplin website"
|
||||
msgstr ""
|
||||
|
||||
@@ -1428,6 +1455,9 @@ msgstr "Voeg foto toe"
|
||||
msgid "Attach any file"
|
||||
msgstr "Voeg bestand toe"
|
||||
|
||||
msgid "Share"
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert to note"
|
||||
msgstr "Converteer naar notitie"
|
||||
|
||||
|
@@ -818,6 +818,10 @@ msgstr "Separe cada tag por vírgula."
|
||||
msgid "Rename notebook:"
|
||||
msgstr "Renomear caderno:"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Rename tag:"
|
||||
msgstr "Renomear"
|
||||
|
||||
msgid "Set alarm:"
|
||||
msgstr "Definir alarme:"
|
||||
|
||||
@@ -868,10 +872,21 @@ msgstr "Abrir..."
|
||||
msgid "Save as..."
|
||||
msgstr "Salvar como..."
|
||||
|
||||
msgid "Copy path to clipboard"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unsupported link or message: %s"
|
||||
msgstr "Link ou mensagem não suportada: %s"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
"Esta nota não possui conteúdo. Clique em \"%s\" para alternar para o editor, "
|
||||
"e edite a nota."
|
||||
|
||||
msgid "Attach file"
|
||||
msgstr "Anexar arquivo"
|
||||
|
||||
@@ -881,14 +896,6 @@ msgstr "Tags"
|
||||
msgid "Set alarm"
|
||||
msgstr "Definir alarme"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
"Esta nota não possui conteúdo. Clique em \"%s\" para alternar para o editor, "
|
||||
"e edite a nota."
|
||||
|
||||
msgid "to-do"
|
||||
msgstr "tarefa"
|
||||
|
||||
@@ -1120,6 +1127,10 @@ msgstr "Dark"
|
||||
msgid "Uncompleted to-dos on top"
|
||||
msgstr "Mostrar tarefas incompletas no topo"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Show completed to-dos"
|
||||
msgstr "Mostrar tarefas incompletas no topo"
|
||||
|
||||
msgid "Sort notes by"
|
||||
msgstr "Ordenar notas por"
|
||||
|
||||
@@ -1153,9 +1164,10 @@ msgstr "Porcentagem global do zoom"
|
||||
msgid "Editor font family"
|
||||
msgstr "Família de fontes do editor"
|
||||
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"The font name will not be checked. If incorrect or empty, it will default to "
|
||||
"a generic monospace font."
|
||||
"This must be *monospace* font or it will not work properly. If the font is "
|
||||
"incorrect or empty, it will default to a generic monospace font."
|
||||
msgstr ""
|
||||
"O nomes da fonte não será verificado. Se estiver incorreto ou vazio, será "
|
||||
"usado por default uma fonte genérica monospace."
|
||||
@@ -1353,6 +1365,22 @@ msgstr "Novas tags:"
|
||||
msgid "Type new tags or select from list"
|
||||
msgstr "Digite novsa tags, ou selecione da lista"
|
||||
|
||||
msgid ""
|
||||
"To work correctly, the app needs the following permissions. Please enable "
|
||||
"them in your phone settings, in Apps > Joplin > Permissions"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"- Storage: to allow attaching files to notes and to enable filesystem "
|
||||
"synchronisation."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Camera: to allow taking a picture and attaching it to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Location: to allow attaching geo-location information to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Joplin website"
|
||||
msgstr "Site do Joplin"
|
||||
|
||||
@@ -1416,6 +1444,9 @@ msgstr "Anexar foto"
|
||||
msgid "Attach any file"
|
||||
msgstr "Anexar qualquer arquivo"
|
||||
|
||||
msgid "Share"
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert to note"
|
||||
msgstr "Converter para nota"
|
||||
|
||||
|
@@ -817,6 +817,10 @@ msgstr "Каждый тег отделяется запятой."
|
||||
msgid "Rename notebook:"
|
||||
msgstr "Переименовать блокнот:"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Rename tag:"
|
||||
msgstr "Переименовать"
|
||||
|
||||
msgid "Set alarm:"
|
||||
msgstr "Установить напоминание:"
|
||||
|
||||
@@ -868,10 +872,21 @@ msgstr "Открыть..."
|
||||
msgid "Save as..."
|
||||
msgstr "Сохранить как..."
|
||||
|
||||
msgid "Copy path to clipboard"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unsupported link or message: %s"
|
||||
msgstr "Неподдерживаемая ссыка или сообщение: %s"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
"Заметка пуста. Нажмите на «%s», чтобы переключиться в редактор и "
|
||||
"отредактировать её."
|
||||
|
||||
msgid "Attach file"
|
||||
msgstr "Прикрепить файл"
|
||||
|
||||
@@ -881,14 +896,6 @@ msgstr "Теги"
|
||||
msgid "Set alarm"
|
||||
msgstr "Установить напоминание"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr ""
|
||||
"Заметка пуста. Нажмите на «%s», чтобы переключиться в редактор и "
|
||||
"отредактировать её."
|
||||
|
||||
msgid "to-do"
|
||||
msgstr "задача"
|
||||
|
||||
@@ -1117,6 +1124,10 @@ msgstr "Тёмная"
|
||||
msgid "Uncompleted to-dos on top"
|
||||
msgstr "Незавершённые задачи сверху"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Show completed to-dos"
|
||||
msgstr "Незавершённые задачи сверху"
|
||||
|
||||
msgid "Sort notes by"
|
||||
msgstr "Сортировать заметки по"
|
||||
|
||||
@@ -1150,9 +1161,10 @@ msgstr "Глобальный масштаб в процентах"
|
||||
msgid "Editor font family"
|
||||
msgstr "Семейство шрифтов редактора"
|
||||
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"The font name will not be checked. If incorrect or empty, it will default to "
|
||||
"a generic monospace font."
|
||||
"This must be *monospace* font or it will not work properly. If the font is "
|
||||
"incorrect or empty, it will default to a generic monospace font."
|
||||
msgstr ""
|
||||
"Название шрифта не проверяется. Если оно указано некорректно или не задано, "
|
||||
"будет использоваться стандартный моноширинный шрифт."
|
||||
@@ -1350,6 +1362,22 @@ msgstr ""
|
||||
msgid "Type new tags or select from list"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"To work correctly, the app needs the following permissions. Please enable "
|
||||
"them in your phone settings, in Apps > Joplin > Permissions"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"- Storage: to allow attaching files to notes and to enable filesystem "
|
||||
"synchronisation."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Camera: to allow taking a picture and attaching it to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Location: to allow attaching geo-location information to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Joplin website"
|
||||
msgstr "Сайт Joplin"
|
||||
|
||||
@@ -1414,6 +1442,9 @@ msgstr "Прикрепить фото"
|
||||
msgid "Attach any file"
|
||||
msgstr "Прикрепить любой файл"
|
||||
|
||||
msgid "Share"
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert to note"
|
||||
msgstr "Преобразовать в заметку"
|
||||
|
||||
|
@@ -777,6 +777,10 @@ msgstr "用逗号\",\"分开每个标签。"
|
||||
msgid "Rename notebook:"
|
||||
msgstr "重命名笔记本:"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Rename tag:"
|
||||
msgstr "重命名"
|
||||
|
||||
msgid "Set alarm:"
|
||||
msgstr "设置提醒:"
|
||||
|
||||
@@ -827,10 +831,19 @@ msgstr "打开…"
|
||||
msgid "Save as..."
|
||||
msgstr "另存为…"
|
||||
|
||||
msgid "Copy path to clipboard"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unsupported link or message: %s"
|
||||
msgstr "不支持的链接或信息:%s"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr "此笔记没有任何内容。点击 \"%s\" 切换至编辑器并编辑笔记。"
|
||||
|
||||
msgid "Attach file"
|
||||
msgstr "附加文件"
|
||||
|
||||
@@ -840,12 +853,6 @@ msgstr "标签"
|
||||
msgid "Set alarm"
|
||||
msgstr "设置提醒"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
||||
"note."
|
||||
msgstr "此笔记没有任何内容。点击 \"%s\" 切换至编辑器并编辑笔记。"
|
||||
|
||||
msgid "to-do"
|
||||
msgstr "待办事项"
|
||||
|
||||
@@ -1069,6 +1076,10 @@ msgstr "深色"
|
||||
msgid "Uncompleted to-dos on top"
|
||||
msgstr "未完成的待办事项在顶端"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Show completed to-dos"
|
||||
msgstr "未完成的待办事项在顶端"
|
||||
|
||||
msgid "Sort notes by"
|
||||
msgstr "排序笔记"
|
||||
|
||||
@@ -1102,9 +1113,10 @@ msgstr "全局缩放比例"
|
||||
msgid "Editor font family"
|
||||
msgstr "编辑器字体"
|
||||
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"The font name will not be checked. If incorrect or empty, it will default to "
|
||||
"a generic monospace font."
|
||||
"This must be *monospace* font or it will not work properly. If the font is "
|
||||
"incorrect or empty, it will default to a generic monospace font."
|
||||
msgstr ""
|
||||
"此软件不会检查所设置字体的有效性。若无效或空白,此处将会设置成一款通用等宽字"
|
||||
"体。"
|
||||
@@ -1296,6 +1308,22 @@ msgstr ""
|
||||
msgid "Type new tags or select from list"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"To work correctly, the app needs the following permissions. Please enable "
|
||||
"them in your phone settings, in Apps > Joplin > Permissions"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"- Storage: to allow attaching files to notes and to enable filesystem "
|
||||
"synchronisation."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Camera: to allow taking a picture and attaching it to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "- Location: to allow attaching geo-location information to a note."
|
||||
msgstr ""
|
||||
|
||||
msgid "Joplin website"
|
||||
msgstr "Joplin官网"
|
||||
|
||||
@@ -1360,6 +1388,9 @@ msgstr "附加照片"
|
||||
msgid "Attach any file"
|
||||
msgstr "附加任何文件"
|
||||
|
||||
msgid "Share"
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert to note"
|
||||
msgstr "转换至笔记"
|
||||
|
||||
|
95
CliClient/package-lock.json
generated
95
CliClient/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "joplin",
|
||||
"version": "1.0.106",
|
||||
"version": "1.0.107",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -122,6 +122,15 @@
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"camel-case": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
|
||||
"integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=",
|
||||
"requires": {
|
||||
"no-case": "^2.2.0",
|
||||
"upper-case": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"camelcase": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
|
||||
@@ -163,6 +172,14 @@
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz",
|
||||
"integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE="
|
||||
},
|
||||
"clean-css": {
|
||||
"version": "4.1.11",
|
||||
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.11.tgz",
|
||||
"integrity": "sha1-Ls3xRaujj1R0DybO/Q/z4D4SXWo=",
|
||||
"requires": {
|
||||
"source-map": "0.5.x"
|
||||
}
|
||||
},
|
||||
"co": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
@@ -207,6 +224,11 @@
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.15.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
|
||||
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag=="
|
||||
},
|
||||
"compare-version": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz",
|
||||
@@ -573,6 +595,11 @@
|
||||
"sntp": "2.x.x"
|
||||
}
|
||||
},
|
||||
"he": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
|
||||
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0="
|
||||
},
|
||||
"highlight.js": {
|
||||
"version": "9.12.0",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.12.0.tgz",
|
||||
@@ -588,6 +615,20 @@
|
||||
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz",
|
||||
"integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8="
|
||||
},
|
||||
"html-minifier": {
|
||||
"version": "3.5.15",
|
||||
"resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.15.tgz",
|
||||
"integrity": "sha512-OZa4rfb6tZOZ3Z8Xf0jKxXkiDcFWldQePGYFDcgKqES2sXeWaEv9y6QQvWUtX3ySI3feApQi5uCsHLINQ6NoAw==",
|
||||
"requires": {
|
||||
"camel-case": "3.0.x",
|
||||
"clean-css": "4.1.x",
|
||||
"commander": "2.15.x",
|
||||
"he": "1.1.x",
|
||||
"param-case": "2.1.x",
|
||||
"relateurl": "0.2.x",
|
||||
"uglify-js": "3.3.x"
|
||||
}
|
||||
},
|
||||
"http-signature": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||
@@ -815,6 +856,11 @@
|
||||
"js-tokens": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"lower-case": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
|
||||
"integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw="
|
||||
},
|
||||
"lowlight": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.9.2.tgz",
|
||||
@@ -933,6 +979,14 @@
|
||||
"resolved": "https://registry.npmjs.org/nextgen-events/-/nextgen-events-0.11.3.tgz",
|
||||
"integrity": "sha512-dC4v/dOF6m8/M05eU712KXjRJ0e/187rx5CMS/fTnulv2QGPps1U/c/J1D3wtegEhK+EE7LuJc3jly3pyfV46g=="
|
||||
},
|
||||
"no-case": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
|
||||
"integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==",
|
||||
"requires": {
|
||||
"lower-case": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"node-bitmap": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/node-bitmap/-/node-bitmap-0.0.1.tgz",
|
||||
@@ -997,6 +1051,14 @@
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"param-case": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
|
||||
"integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=",
|
||||
"requires": {
|
||||
"no-case": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"parse-data-uri": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-data-uri/-/parse-data-uri-0.2.0.tgz",
|
||||
@@ -1107,6 +1169,11 @@
|
||||
"symbol-observable": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"relateurl": {
|
||||
"version": "0.2.7",
|
||||
"resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
|
||||
"integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk="
|
||||
},
|
||||
"request": {
|
||||
"version": "2.85.0",
|
||||
"resolved": "https://registry.npmjs.org/request/-/request-2.85.0.tgz",
|
||||
@@ -1233,6 +1300,11 @@
|
||||
"hoek": "4.x.x"
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
|
||||
},
|
||||
"sprintf-js": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz",
|
||||
@@ -2138,6 +2210,22 @@
|
||||
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
|
||||
"optional": true
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.3.25",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.25.tgz",
|
||||
"integrity": "sha512-hobogryjDV36VrLK3Y69ou4REyrTApzUblVFmdQOYRe8cYaSmFJXMb4dR9McdvYDSbeNdzUgYr2YVukJaErJcA==",
|
||||
"requires": {
|
||||
"commander": "~2.15.0",
|
||||
"source-map": "~0.6.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"unc-path-regex": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
|
||||
@@ -2153,6 +2241,11 @@
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz",
|
||||
"integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc="
|
||||
},
|
||||
"upper-case": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
|
||||
"integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg="
|
||||
},
|
||||
"url-parse": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.2.0.tgz",
|
||||
|
@@ -19,7 +19,7 @@
|
||||
],
|
||||
"owner": "Laurent Cozic"
|
||||
},
|
||||
"version": "1.0.106",
|
||||
"version": "1.0.107",
|
||||
"bin": {
|
||||
"joplin": "./main.js"
|
||||
},
|
||||
@@ -35,6 +35,7 @@
|
||||
"form-data": "^2.1.4",
|
||||
"fs-extra": "^5.0.0",
|
||||
"html-entities": "^1.2.1",
|
||||
"html-minifier": "^3.5.15",
|
||||
"jssha": "^2.3.0",
|
||||
"levenshtein": "^1.0.5",
|
||||
"lodash": "^4.17.4",
|
||||
|
@@ -8,8 +8,16 @@ rsync -a "$ROOT_DIR/../ReactNativeClient/lib/" "$BUILD_DIR/lib/"
|
||||
rsync -a "$ROOT_DIR/build/locales/" "$BUILD_DIR/locales/"
|
||||
mkdir -p "$BUILD_DIR/data"
|
||||
|
||||
if [[ $TEST_FILE == "" ]]; then
|
||||
(cd "$ROOT_DIR" && npm test tests-build/synchronizer.js tests-build/encryption.js tests-build/ArrayUtils.js tests-build/models_Setting.js tests-build/models_Note.js tests-build/models_Folder.js tests-build/services_InteropService.js)
|
||||
else
|
||||
if [[ $TEST_FILE != "" ]]; then
|
||||
(cd "$ROOT_DIR" && npm test tests-build/$TEST_FILE.js)
|
||||
fi
|
||||
exit
|
||||
fi
|
||||
|
||||
(cd "$ROOT_DIR" && npm test tests-build/synchronizer.js)
|
||||
(cd "$ROOT_DIR" && npm test tests-build/encryption.js)
|
||||
(cd "$ROOT_DIR" && npm test tests-build/ArrayUtils.js)
|
||||
(cd "$ROOT_DIR" && npm test tests-build/models_Setting.js)
|
||||
(cd "$ROOT_DIR" && npm test tests-build/models_Note.js)
|
||||
(cd "$ROOT_DIR" && npm test tests-build/models_Folder.js)
|
||||
(cd "$ROOT_DIR" && npm test tests-build/services_InteropService.js)
|
||||
(cd "$ROOT_DIR" && npm test tests-build/HtmlToMd.js)
|
@@ -44,4 +44,13 @@ describe('ArrayUtils', function() {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should compare arrays', async (done) => {
|
||||
expect(ArrayUtils.contentEquals([], [])).toBe(true);
|
||||
expect(ArrayUtils.contentEquals(['a'], ['a'])).toBe(true);
|
||||
expect(ArrayUtils.contentEquals(['b', 'a'], ['a', 'b'])).toBe(true);
|
||||
expect(ArrayUtils.contentEquals(['b'], ['a', 'b'])).toBe(false);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
62
CliClient/tests/HtmlToMd.js
Normal file
62
CliClient/tests/HtmlToMd.js
Normal file
@@ -0,0 +1,62 @@
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { filename } = require('lib/path-utils.js');
|
||||
const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const { enexXmlToMd } = require('lib/import-enex-md-gen.js');
|
||||
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 60 * 60 * 1000; // Can run for a while since everything is in the same test unit
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
});
|
||||
|
||||
describe('HtmlToMd', function() {
|
||||
|
||||
beforeEach(async (done) => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should convert from HTML to Markdown', asyncTest(async () => {
|
||||
const basePath = __dirname + '/html_to_md';
|
||||
const files = await shim.fsDriver().readDirStats(basePath);
|
||||
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const htmlFilename = files[i].path;
|
||||
if (htmlFilename.indexOf('.html') < 0) continue;
|
||||
|
||||
const htmlPath = basePath + '/' + htmlFilename;
|
||||
const mdPath = basePath + '/' + filename(htmlFilename) + '.md';
|
||||
|
||||
// if (htmlFilename !== 'text2.html') continue;
|
||||
|
||||
const html = await shim.fsDriver().readFile(htmlPath);
|
||||
const expectedMd = await shim.fsDriver().readFile(mdPath);
|
||||
|
||||
const actualMd = await enexXmlToMd('<div>' + html + '</div>', []);
|
||||
|
||||
if (actualMd !== expectedMd) {
|
||||
console.info('');
|
||||
console.info('Error converting file: ' + htmlFilename);
|
||||
console.info('--------------------------------- Got:');
|
||||
console.info(actualMd.split('\n'));
|
||||
console.info('--------------------------------- Expected:');
|
||||
console.info(expectedMd.split('\n'));
|
||||
console.info('--------------------------------------------');
|
||||
console.info('');
|
||||
|
||||
expect(false).toBe(true);
|
||||
// return;
|
||||
} else {
|
||||
expect(true).toBe(true)
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
});
|
16
CliClient/tests/html_to_md/code1.html
Normal file
16
CliClient/tests/html_to_md/code1.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<div>
|
||||
<p>For example, consider a web page like this:</p>
|
||||
|
||||
<pre class="brush: html line-numbers language-html"><code class=" language-html"><span class="token doctype"><!DOCTYPE html></span>
|
||||
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span>
|
||||
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span>
|
||||
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>content-type<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text/html; charset<span class="token punctuation">=</span>utf-8<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
|
||||
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span>
|
||||
|
||||
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span>
|
||||
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>page-scripts/page-script.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>
|
||||
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span>
|
||||
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span><span class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
|
||||
|
||||
<p>The script "page-script.js" does this:</p>
|
||||
</div>
|
14
CliClient/tests/html_to_md/code1.md
Normal file
14
CliClient/tests/html_to_md/code1.md
Normal file
@@ -0,0 +1,14 @@
|
||||
For example, consider a web page like this:
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script src="page-scripts/page-script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
The script "page-script.js" does this:
|
7
CliClient/tests/html_to_md/code2.html
Normal file
7
CliClient/tests/html_to_md/code2.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<p>Subshell:</p>
|
||||
<pre><code>(
|
||||
set -e
|
||||
false
|
||||
echo Unreachable
|
||||
) && echo Great success
|
||||
</code></pre>
|
7
CliClient/tests/html_to_md/code2.md
Normal file
7
CliClient/tests/html_to_md/code2.md
Normal file
@@ -0,0 +1,7 @@
|
||||
Subshell:
|
||||
|
||||
(
|
||||
set -e
|
||||
false
|
||||
echo Unreachable
|
||||
) && echo Great success
|
9
CliClient/tests/html_to_md/heading.html
Normal file
9
CliClient/tests/html_to_md/heading.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<div>
|
||||
<div class="note">
|
||||
<p>Values added to the global scope of a content script with</p>
|
||||
</div>
|
||||
|
||||
<h2 id="Loading_content_scripts">Loading content scripts</h2>
|
||||
|
||||
<p>You can load a content script into a web page in one of three ways:</p>
|
||||
</div>
|
5
CliClient/tests/html_to_md/heading.md
Normal file
5
CliClient/tests/html_to_md/heading.md
Normal file
@@ -0,0 +1,5 @@
|
||||
Values added to the global scope of a content script with
|
||||
|
||||
## Loading content scripts
|
||||
|
||||
You can load a content script into a web page in one of three ways:
|
3
CliClient/tests/html_to_md/inlineCode.html
Normal file
3
CliClient/tests/html_to_md/inlineCode.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<div>
|
||||
<p>Similarly, I need another regex to match double newlines (<code>\n\n</code>) that are not part of a longer run of newline characters like <code>\n\n\n</code> or <code>\n\n\n\n\n\n</code> etc.</p>
|
||||
</div>
|
1
CliClient/tests/html_to_md/inlineCode.md
Normal file
1
CliClient/tests/html_to_md/inlineCode.md
Normal file
@@ -0,0 +1 @@
|
||||
Similarly, I need another regex to match double newlines (`\n\n`) that are not part of a longer run of newline characters like `\n\n\n` or `\n\n\n\n\n\n` etc.
|
3
CliClient/tests/html_to_md/inlineCodeWithLink.html
Normal file
3
CliClient/tests/html_to_md/inlineCodeWithLink.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<div>
|
||||
<p>the <code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onConnect">runtime.onConnect</a></code> listener gets passed its own <code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/Port">runtime.Port</a></code> object.</p>
|
||||
</div>
|
1
CliClient/tests/html_to_md/inlineCodeWithLink.md
Normal file
1
CliClient/tests/html_to_md/inlineCodeWithLink.md
Normal file
@@ -0,0 +1 @@
|
||||
the `[runtime.onConnect](/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onConnect)` listener gets passed its own `[runtime.Port](/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/Port)` object.
|
4
CliClient/tests/html_to_md/link1.html
Normal file
4
CliClient/tests/html_to_md/link1.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<a href="https://arstechnica.com/civis/ucp.php?mode=login&return_to=%2Ftech-policy%2F2018%2F05%2Fjails-are-replacing-in-person-visits-with-video-calling-services-theyre-awful%2F" class="dropdown-toggle">
|
||||
Sign in
|
||||
<span class="icon dropdown-indicator icon-drop-indicator"></span>
|
||||
</a>
|
1
CliClient/tests/html_to_md/link1.md
Normal file
1
CliClient/tests/html_to_md/link1.md
Normal file
@@ -0,0 +1 @@
|
||||
[Sign in](https://arstechnica.com/civis/ucp.php?mode=login&return_to=%2Ftech-policy%2F2018%2F05%2Fjails-are-replacing-in-person-visits-with-video-calling-services-theyre-awful%2F)
|
17
CliClient/tests/html_to_md/list.html
Normal file
17
CliClient/tests/html_to_md/list.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<div>
|
||||
<p>Liste de courses</p>
|
||||
|
||||
<div>
|
||||
<div><en-todo checked="true"/>Pizzas</div>
|
||||
<div><en-todo checked="true"/>Pain</div>
|
||||
<div><en-todo checked="true"/>Jambon</div>
|
||||
</div>
|
||||
|
||||
<div><br/></div>
|
||||
|
||||
<div>
|
||||
<div><en-todo checked="true"/>On its own</div>
|
||||
</div>
|
||||
|
||||
<p>End</p>
|
||||
</div>
|
9
CliClient/tests/html_to_md/list.md
Normal file
9
CliClient/tests/html_to_md/list.md
Normal file
@@ -0,0 +1,9 @@
|
||||
Liste de courses
|
||||
|
||||
- [X] Pizzas
|
||||
- [X] Pain
|
||||
- [X] Jambon
|
||||
|
||||
- [X] On its own
|
||||
|
||||
End
|
1
CliClient/tests/html_to_md/list2.html
Normal file
1
CliClient/tests/html_to_md/list2.html
Normal file
@@ -0,0 +1 @@
|
||||
<ul class="find-me-on"><li><a href="https://github.com/zetter">Github</a></li><li><a href="https://twitter.com/czetter">Twitter</a></li><li><a href="http://lanyrd.com/profile/czetter/">Lanyrd</a></li></ul>
|
3
CliClient/tests/html_to_md/list2.md
Normal file
3
CliClient/tests/html_to_md/list2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
- [Github](https://github.com/zetter)
|
||||
- [Twitter](https://twitter.com/czetter)
|
||||
- [Lanyrd](http://lanyrd.com/profile/czetter/)
|
1
CliClient/tests/html_to_md/list3.html
Normal file
1
CliClient/tests/html_to_md/list3.html
Normal file
@@ -0,0 +1 @@
|
||||
<ul class="find-me-on"><li>Github</li><li>Twitter</li></ul>
|
2
CliClient/tests/html_to_md/list3.md
Normal file
2
CliClient/tests/html_to_md/list3.md
Normal file
@@ -0,0 +1,2 @@
|
||||
- Github
|
||||
- Twitter
|
11
CliClient/tests/html_to_md/paragraph.html
Normal file
11
CliClient/tests/html_to_md/paragraph.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<div>
|
||||
<p>Short paragraphs are merged together:</p>
|
||||
<p>Something something</p>
|
||||
<p>Blablbla blabla lbla</p>
|
||||
<p>Last line</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p>Longer ones are separated by new lines. In 1894 Joplin arrived in Sedalia, Missouri. At first, Joplin stayed with the family of Arthur Marshall, at the time a 13-year-old boy but later one of Joplin's students and a rag-time composer in his own right.[26] There is no record of Joplin having a permanent residence in the town until 1904, as Joplin was making a living as a touring musician.</p>
|
||||
<p>There is little precise evidence known about Joplin's activities at this time, although he performed as a solo musician at dances and at the major black clubs in Sedalia, the Black 400 club and the Maple Leaf Club. He performed in the Queen City Cornet Band, and his own six-piece dance orchestra.</p>
|
||||
</div>
|
8
CliClient/tests/html_to_md/paragraph.md
Normal file
8
CliClient/tests/html_to_md/paragraph.md
Normal file
@@ -0,0 +1,8 @@
|
||||
Short paragraphs are merged together:
|
||||
Something something
|
||||
Blablbla blabla lbla
|
||||
Last line
|
||||
|
||||
Longer ones are separated by new lines. In 1894 Joplin arrived in Sedalia, Missouri. At first, Joplin stayed with the family of Arthur Marshall, at the time a 13-year-old boy but later one of Joplin's students and a rag-time composer in his own right.[26] There is no record of Joplin having a permanent residence in the town until 1904, as Joplin was making a living as a touring musician.
|
||||
|
||||
There is little precise evidence known about Joplin's activities at this time, although he performed as a solo musician at dances and at the major black clubs in Sedalia, the Black 400 club and the Maple Leaf Club. He performed in the Queen City Cornet Band, and his own six-piece dance orchestra.
|
12
CliClient/tests/html_to_md/table1.html
Normal file
12
CliClient/tests/html_to_md/table1.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<div>$ sudo ethtool --set-priv-flags p2p1 mlx4_rss_xor_hash_function on</div>
|
||||
<div># Three empty lines follow</div>
|
||||
<div><br/></div>
|
||||
<div><br/></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Some text
|
5
CliClient/tests/html_to_md/table1.md
Normal file
5
CliClient/tests/html_to_md/table1.md
Normal file
@@ -0,0 +1,5 @@
|
||||
| |
|
||||
| --- |
|
||||
| $ sudo ethtool --set-priv-flags p2p1 mlx4_rss_xor_hash_function on<br># Three empty lines follow |
|
||||
|
||||
Some text
|
3
CliClient/tests/html_to_md/tableWithNewLines.html
Normal file
3
CliClient/tests/html_to_md/tableWithNewLines.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<div>
|
||||
<table style="-evernote-table:true;border-collapse:collapse;width:100%;table-layout:fixed;margin-left:0px;"><tr><td style="border-style:solid;border-width:1px;border-color:rgb(211,211,211);padding:10px;margin:0px;width:50%;"><div>line 1</div><div>line 2</div></td><td style="border-style:solid;border-width:1px;border-color:rgb(211,211,211);padding:10px;margin:0px;width:50%;"><div><br/></div></td></tr><tr><td style="border-style:solid;border-width:1px;border-color:rgb(211,211,211);padding:10px;margin:0px;width:50%;"><div>aaaaaa</div></td><td style="border-style:solid;border-width:1px;border-color:rgb(211,211,211);padding:10px;margin:0px;width:50%;"><div>line 3</div><div>line 4</div></td></tr></table>
|
||||
</div>
|
4
CliClient/tests/html_to_md/tableWithNewLines.md
Normal file
4
CliClient/tests/html_to_md/tableWithNewLines.md
Normal file
@@ -0,0 +1,4 @@
|
||||
| | |
|
||||
| --- | --- |
|
||||
| line 1<br>line 2 | |
|
||||
| aaaaaa | line 3<br>line 4 |
|
@@ -56,8 +56,8 @@ SyncTargetRegistry.addClass(SyncTargetOneDrive);
|
||||
SyncTargetRegistry.addClass(SyncTargetNextcloud);
|
||||
SyncTargetRegistry.addClass(SyncTargetDropbox);
|
||||
|
||||
// const syncTargetId_ = SyncTargetRegistry.nameToId("nextcloud");
|
||||
const syncTargetId_ = SyncTargetRegistry.nameToId("memory");
|
||||
const syncTargetId_ = SyncTargetRegistry.nameToId("nextcloud");
|
||||
// const syncTargetId_ = SyncTargetRegistry.nameToId("memory");
|
||||
//const syncTargetId_ = SyncTargetRegistry.nameToId('filesystem');
|
||||
// const syncTargetId_ = SyncTargetRegistry.nameToId('dropbox');
|
||||
const syncDir = __dirname + '/../tests/sync';
|
||||
|
@@ -69,7 +69,7 @@ class ElectronAppWrapper {
|
||||
}))
|
||||
|
||||
// Uncomment this to view errors if the application does not start
|
||||
// if (this.env_ === 'dev') this.win_.webContents.openDevTools();
|
||||
if (this.env_ === 'dev') this.win_.webContents.openDevTools();
|
||||
|
||||
this.win_.on('close', (event) => {
|
||||
// If it's on macOS, the app is completely closed only if the user chooses to close the app (willQuitApp_ will be true)
|
||||
|
@@ -152,6 +152,9 @@ class Application extends BaseApplication {
|
||||
async generalMiddleware(store, next, action) {
|
||||
if (action.type == 'SETTING_UPDATE_ONE' && action.key == 'locale' || action.type == 'SETTING_UPDATE_ALL') {
|
||||
setLocale(Setting.value('locale'));
|
||||
// The bridge runs within the main process, with its own instance of locale.js
|
||||
// so it needs to be set too here.
|
||||
bridge().setLocale(Setting.value('locale'));
|
||||
this.refreshMenu();
|
||||
}
|
||||
|
||||
@@ -164,7 +167,7 @@ class Application extends BaseApplication {
|
||||
}
|
||||
|
||||
if (["NOTE_UPDATE_ONE", "NOTE_DELETE", "FOLDER_UPDATE_ONE", "FOLDER_DELETE"].indexOf(action.type) >= 0) {
|
||||
if (!await reg.syncTarget().syncStarted()) reg.scheduleSync(30, { syncSteps: ["update_remote", "delete_remote"] });
|
||||
if (!await reg.syncTarget().syncStarted()) reg.scheduleSync(30 * 1000, { syncSteps: ["update_remote", "delete_remote"] });
|
||||
}
|
||||
|
||||
if (['EVENT_NOTE_ALARM_FIELD_CHANGE', 'NOTE_DELETE'].indexOf(action.type) >= 0) {
|
||||
@@ -437,6 +440,14 @@ class Application extends BaseApplication {
|
||||
click: () => {
|
||||
Setting.setValue('uncompletedTodosOnTop', !Setting.value('uncompletedTodosOnTop'));
|
||||
},
|
||||
}, {
|
||||
label: Setting.settingMetadata('showCompletedTodos').label(),
|
||||
type: 'checkbox',
|
||||
checked: Setting.value('showCompletedTodos'),
|
||||
screens: ['Main'],
|
||||
click: () => {
|
||||
Setting.setValue('showCompletedTodos', !Setting.value('showCompletedTodos'));
|
||||
},
|
||||
}],
|
||||
}, {
|
||||
label: _('Tools'),
|
||||
@@ -524,6 +535,21 @@ class Application extends BaseApplication {
|
||||
if (('submenu' in t) && isEmptyMenu(t.submenu)) continue;
|
||||
output.push(t);
|
||||
}
|
||||
|
||||
// Remove empty separator for now empty sections
|
||||
let temp = [];
|
||||
let previous = null;
|
||||
for (let i = 0; i < output.length; i++) {
|
||||
const t = Object.assign({}, output[i]);
|
||||
if (t.type === 'separator') {
|
||||
if (!previous) continue;
|
||||
if (previous.type === 'separator') continue;
|
||||
}
|
||||
temp.push(t);
|
||||
previous = t;
|
||||
}
|
||||
output = temp;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _, setLocale } = require('lib/locale.js');
|
||||
const { dirname } = require('lib/path-utils.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
|
||||
@@ -95,6 +95,10 @@ class Bridge {
|
||||
return result === 0;
|
||||
}
|
||||
|
||||
setLocale(locale) {
|
||||
setLocale(locale);
|
||||
}
|
||||
|
||||
get Menu() {
|
||||
return require('electron').Menu;
|
||||
}
|
||||
|
@@ -1,7 +1,9 @@
|
||||
const { dialog } = require('electron')
|
||||
const { shim } = require('lib/shim');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const fetch = require('node-fetch');
|
||||
const { fileExtension } = require('lib/path-utils.js');
|
||||
const packageInfo = require('./packageInfo.js');
|
||||
const compareVersions = require('compare-versions');
|
||||
|
||||
@@ -43,11 +45,16 @@ async function fetchLatestRelease() {
|
||||
for (let i = 0; i < json.assets.length; i++) {
|
||||
const asset = json.assets[i];
|
||||
let found = false;
|
||||
if (platform === 'win32' && asset.name.indexOf('.exe') >= 0 && asset.name.indexOf('Setup') >= 0) {
|
||||
const ext = fileExtension(asset.name);
|
||||
if (platform === 'win32' && ext === 'exe') {
|
||||
if (shim.isPortable()) {
|
||||
found = asset.name == 'JoplinPortable.exe';
|
||||
} else {
|
||||
found = !!asset.name.match(/^Joplin-Setup-[\d.]+\.exe$/);
|
||||
}
|
||||
} else if (platform === 'darwin' && ext === 'dmg') {
|
||||
found = true;
|
||||
} else if (platform === 'darwin' && asset.name.indexOf('.dmg') >= 0) {
|
||||
found = true;
|
||||
} else if (platform === 'linux' && asset.name.indexOf('.AppImage') >= 0) {
|
||||
} else if (platform === 'linux' && ext === '.AppImage') {
|
||||
found = true;
|
||||
}
|
||||
|
||||
@@ -57,12 +64,11 @@ async function fetchLatestRelease() {
|
||||
}
|
||||
}
|
||||
|
||||
if (!downloadUrl) throw new Error('Cannot find download Url: ' + JSON.stringify(json).substr(0,500));
|
||||
|
||||
return {
|
||||
version: version,
|
||||
downloadUrl: downloadUrl,
|
||||
notes: json.body,
|
||||
pageUrl: json.html_url,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -86,6 +92,9 @@ function checkForUpdates(inBackground, window, logFilePath) {
|
||||
checkInBackground_ = inBackground;
|
||||
|
||||
fetchLatestRelease().then(release => {
|
||||
autoUpdateLogger_.info('Current version: ' + packageInfo.version);
|
||||
autoUpdateLogger_.info('Latest version: ' + release.version);
|
||||
|
||||
if (compareVersions(release.version, packageInfo.version) <= 0) {
|
||||
if (!checkInBackground_) dialog.showMessageBox({ message: _('Current version is up-to-date.') })
|
||||
} else {
|
||||
@@ -97,7 +106,7 @@ function checkForUpdates(inBackground, window, logFilePath) {
|
||||
buttons: [_('Yes'), _('No')]
|
||||
});
|
||||
|
||||
if (buttonIndex === 0) require('electron').shell.openExternal(release.downloadUrl);
|
||||
if (buttonIndex === 0) require('electron').shell.openExternal(release.downloadUrl ? release.downloadUrl : release.pageUrl);
|
||||
}
|
||||
}).catch(error => {
|
||||
autoUpdateLogger_.error(error);
|
||||
|
@@ -145,6 +145,28 @@ class MainScreenComponent extends React.Component {
|
||||
}
|
||||
},
|
||||
});
|
||||
} else if (command.name === 'renameTag') {
|
||||
const tag = await Tag.load(command.id);
|
||||
if(!tag) return;
|
||||
|
||||
this.setState({
|
||||
promptOptions: {
|
||||
label: _('Rename tag:'),
|
||||
value: tag.title,
|
||||
onClose: async (answer) => {
|
||||
if (answer !== null) {
|
||||
try {
|
||||
tag.title = answer;
|
||||
await Tag.save(tag, { fields: ['title'], userSideValidation: true });
|
||||
} catch (error) {
|
||||
bridge().showErrorMessageBox(error.message);
|
||||
}
|
||||
}
|
||||
this.setState({promptOptions: null });
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
} else if (command.name === 'search') {
|
||||
|
||||
if (!this.searchId_) this.searchId_ = uuid.create();
|
||||
|
@@ -20,6 +20,10 @@ const MenuItem = bridge().MenuItem;
|
||||
const { shim } = require('lib/shim.js');
|
||||
const eventManager = require('../eventManager');
|
||||
const fs = require('fs-extra');
|
||||
const {clipboard} = require('electron')
|
||||
const md5 = require('md5');
|
||||
const mimeUtils = require('lib/mime-utils.js').mime;
|
||||
const ArrayUtils = require('lib/ArrayUtils');
|
||||
|
||||
require('brace/mode/markdown');
|
||||
// https://ace.c9.io/build/kitchen-sink.html
|
||||
@@ -47,6 +51,7 @@ class NoteTextComponent extends React.Component {
|
||||
// changed by the user, this variable contains that note ID. Used
|
||||
// to automatically set the title.
|
||||
newAndNoTitleChangeNoteId: null,
|
||||
bodyHtml: '',
|
||||
};
|
||||
|
||||
this.lastLoadedNoteId_ = null;
|
||||
@@ -55,6 +60,8 @@ class NoteTextComponent extends React.Component {
|
||||
this.ignoreNextEditorScroll_ = false;
|
||||
this.scheduleSaveTimeout_ = null;
|
||||
this.restoreScrollTop_ = null;
|
||||
this.lastSetHtml_ = '';
|
||||
this.lastSetMarkers_ = [];
|
||||
|
||||
// Complicated but reliable method to get editor content height
|
||||
// https://github.com/ajaxorg/ace/issues/2046
|
||||
@@ -72,6 +79,62 @@ class NoteTextComponent extends React.Component {
|
||||
this.onAlarmChange_ = (event) => { if (event.noteId === this.props.noteId) this.reloadNote(this.props); }
|
||||
this.onNoteTypeToggle_ = (event) => { if (event.noteId === this.props.noteId) this.reloadNote(this.props); }
|
||||
this.onTodoToggle_ = (event) => { if (event.noteId === this.props.noteId) this.reloadNote(this.props); }
|
||||
|
||||
this.onEditorPaste_ = async (event) => {
|
||||
const formats = clipboard.availableFormats();
|
||||
for (let i = 0; i < formats.length; i++) {
|
||||
const format = formats[i].toLowerCase();
|
||||
const formatType = format.split('/')[0]
|
||||
if (formatType === 'image') {
|
||||
event.preventDefault();
|
||||
|
||||
const image = clipboard.readImage();
|
||||
|
||||
const fileExt = mimeUtils.toFileExtension(format);
|
||||
const filePath = Setting.value('tempDir') + '/' + md5(Date.now()) + '.' + fileExt;
|
||||
|
||||
await shim.writeImageToFile(image, format, filePath);
|
||||
await this.commandAttachFile([filePath]);
|
||||
await shim.fsDriver().remove(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.onDrop_ = async (event) => {
|
||||
const files = event.dataTransfer.files;
|
||||
if (!files || !files.length) return;
|
||||
|
||||
const filesToAttach = [];
|
||||
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
if (!file.path) continue;
|
||||
filesToAttach.push(file.path);
|
||||
}
|
||||
|
||||
await this.commandAttachFile(filesToAttach);
|
||||
}
|
||||
}
|
||||
|
||||
cursorPosition() {
|
||||
if (!this.editor_ || !this.editor_.editor || !this.state.note || !this.state.note.body) return 0;
|
||||
|
||||
const cursorPos = this.editor_.editor.getCursorPosition();
|
||||
const noteLines = this.state.note.body.split('\n');
|
||||
|
||||
let pos = 0;
|
||||
for (let i = 0; i < noteLines.length; i++) {
|
||||
if (i > 0) pos++; // Need to add the newline that's been removed in the split() call above
|
||||
|
||||
if (i === cursorPos.row) {
|
||||
pos += cursorPos.column;
|
||||
break;
|
||||
} else {
|
||||
pos += noteLines[i].length;
|
||||
}
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
mdToHtml() {
|
||||
@@ -102,6 +165,8 @@ class NoteTextComponent extends React.Component {
|
||||
|
||||
this.lastLoadedNoteId_ = note ? note.id : null;
|
||||
|
||||
this.updateHtml(note && note.body ? note.body : '');
|
||||
|
||||
eventManager.on('alarmChange', this.onAlarmChange_);
|
||||
eventManager.on('noteTypeToggle', this.onNoteTypeToggle_);
|
||||
eventManager.on('todoToggle', this.onTodoToggle_);
|
||||
@@ -243,7 +308,12 @@ class NoteTextComponent extends React.Component {
|
||||
newState.newAndNoTitleChangeNoteId = null;
|
||||
}
|
||||
|
||||
this.lastSetHtml_ = '';
|
||||
this.lastSetMarkers_ = [];
|
||||
|
||||
this.setState(newState);
|
||||
|
||||
this.updateHtml(newState.note ? newState.note.body : '');
|
||||
}
|
||||
|
||||
async componentWillReceiveProps(nextProps) {
|
||||
@@ -327,6 +397,12 @@ class NoteTextComponent extends React.Component {
|
||||
if (!filePath) return;
|
||||
await fs.copy(resourcePath, filePath);
|
||||
}}));
|
||||
|
||||
menu.append(new MenuItem({label: _('Copy path to clipboard'), click: async () => {
|
||||
const { clipboard } = require('electron');
|
||||
const { toSystemSlashes } = require('lib/path-utils.js');
|
||||
clipboard.writeText(toSystemSlashes(resourcePath));
|
||||
}}));
|
||||
} else {
|
||||
reg.logger().error('Unhandled item type: ' + itemType);
|
||||
return;
|
||||
@@ -421,6 +497,7 @@ class NoteTextComponent extends React.Component {
|
||||
|
||||
if (this.editor_) {
|
||||
this.editor_.editor.renderer.off('afterRender', this.onAfterEditorRender_);
|
||||
document.querySelector('#note-editor').removeEventListener('paste', this.onEditorPaste_, true);
|
||||
}
|
||||
|
||||
this.editor_ = element;
|
||||
@@ -428,7 +505,14 @@ class NoteTextComponent extends React.Component {
|
||||
if (this.editor_) {
|
||||
this.editor_.editor.renderer.on('afterRender', this.onAfterEditorRender_);
|
||||
|
||||
const cancelledKeys = ['Ctrl+F', 'Ctrl+T', 'Ctrl+P', 'Ctrl+Q', 'Ctrl+L', 'Ctrl+,'];
|
||||
const cancelledKeys = [];
|
||||
const letters = ['F', 'T', 'P', 'Q', 'L', ','];
|
||||
for (let i = 0; i < letters.length; i++) {
|
||||
const l = letters[i];
|
||||
cancelledKeys.push('Ctrl+' + l);
|
||||
cancelledKeys.push('Command+' + l);
|
||||
}
|
||||
|
||||
for (let i = 0; i < cancelledKeys.length; i++) {
|
||||
const k = cancelledKeys[i];
|
||||
this.editor_.editor.commands.bindKey(k, () => {
|
||||
@@ -439,6 +523,8 @@ class NoteTextComponent extends React.Component {
|
||||
throw new Error('HACK: Overriding Ace Editor shortcut: ' + k);
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelector('#note-editor').addEventListener('paste', this.onEditorPaste_, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,9 +559,52 @@ class NoteTextComponent extends React.Component {
|
||||
|
||||
aceEditor_change(body) {
|
||||
shared.noteComponent_change(this, 'body', body);
|
||||
this.scheduleHtmlUpdate();
|
||||
this.scheduleSave();
|
||||
}
|
||||
|
||||
scheduleHtmlUpdate(timeout = 500) {
|
||||
if (this.scheduleHtmlUpdateIID_) {
|
||||
clearTimeout(this.scheduleHtmlUpdateIID_);
|
||||
this.scheduleHtmlUpdateIID_ = null;
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
this.scheduleHtmlUpdateIID_ = setTimeout(() => {
|
||||
this.updateHtml();
|
||||
}, timeout);
|
||||
} else {
|
||||
this.updateHtml();
|
||||
}
|
||||
}
|
||||
|
||||
updateHtml(body = null) {
|
||||
const mdOptions = {
|
||||
onResourceLoaded: () => {
|
||||
this.updateHtml();
|
||||
this.forceUpdate();
|
||||
},
|
||||
postMessageSyntax: 'ipcRenderer.sendToHost',
|
||||
};
|
||||
|
||||
const theme = themeStyle(this.props.theme);
|
||||
|
||||
let bodyToRender = body;
|
||||
if (bodyToRender === null) bodyToRender = this.state.note && this.state.note.body ? this.state.note.body : '';
|
||||
let bodyHtml = '';
|
||||
|
||||
const visiblePanes = this.props.visiblePanes || ['editor', 'viewer'];
|
||||
|
||||
if (!bodyToRender.trim() && visiblePanes.indexOf('viewer') >= 0 && visiblePanes.indexOf('editor') < 0) {
|
||||
// Fixes https://github.com/laurent22/joplin/issues/217
|
||||
bodyToRender = '*' + _('This note has no content. Click on "%s" to toggle the editor and edit the note.', _('Layout')) + '*';
|
||||
}
|
||||
|
||||
bodyHtml = this.mdToHtml().render(bodyToRender, theme, mdOptions);
|
||||
|
||||
this.setState({ bodyHtml: bodyHtml });
|
||||
}
|
||||
|
||||
async doCommand(command) {
|
||||
if (!command) return;
|
||||
|
||||
@@ -509,25 +638,31 @@ class NoteTextComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
async commandAttachFile() {
|
||||
const filePaths = bridge().showOpenDialog({
|
||||
properties: ['openFile', 'createDirectory', 'multiSelections'],
|
||||
});
|
||||
if (!filePaths || !filePaths.length) return;
|
||||
async commandAttachFile(filePaths = null) {
|
||||
if (!filePaths) {
|
||||
filePaths = bridge().showOpenDialog({
|
||||
properties: ['openFile', 'createDirectory', 'multiSelections'],
|
||||
});
|
||||
if (!filePaths || !filePaths.length) return;
|
||||
}
|
||||
|
||||
await this.saveIfNeeded(true);
|
||||
let note = await Note.load(this.state.note.id);
|
||||
|
||||
const position = this.cursorPosition();
|
||||
|
||||
for (let i = 0; i < filePaths.length; i++) {
|
||||
const filePath = filePaths[i];
|
||||
try {
|
||||
reg.logger().info('Attaching ' + filePath);
|
||||
note = await shim.attachFileToNote(note, filePath);
|
||||
note = await shim.attachFileToNote(note, filePath, position);
|
||||
reg.logger().info('File was attached.');
|
||||
this.setState({
|
||||
note: Object.assign({}, note),
|
||||
lastSavedNote: Object.assign({}, note),
|
||||
});
|
||||
|
||||
this.updateHtml(note.body);
|
||||
} catch (error) {
|
||||
reg.logger().error(error);
|
||||
bridge().showErrorMessageBox(error.message);
|
||||
@@ -675,25 +810,21 @@ class NoteTextComponent extends React.Component {
|
||||
}
|
||||
|
||||
if (this.state.webviewReady) {
|
||||
const mdOptions = {
|
||||
onResourceLoaded: () => {
|
||||
this.forceUpdate();
|
||||
},
|
||||
postMessageSyntax: 'ipcRenderer.sendToHost',
|
||||
};
|
||||
let html = this.state.bodyHtml;
|
||||
|
||||
let bodyToRender = body;
|
||||
if (!bodyToRender.trim() && visiblePanes.indexOf('viewer') >= 0 && visiblePanes.indexOf('editor') < 0) {
|
||||
// Fixes https://github.com/laurent22/joplin/issues/217
|
||||
bodyToRender = '*' + _('This note has no content. Click on "%s" to toggle the editor and edit the note.', _('Layout')) + '*';
|
||||
const htmlHasChanged = this.lastSetHtml_ !== html;
|
||||
if (htmlHasChanged) {
|
||||
this.webview_.send('setHtml', html);
|
||||
this.lastSetHtml_ = html;
|
||||
}
|
||||
|
||||
const html = this.mdToHtml().render(bodyToRender, theme, mdOptions);
|
||||
this.webview_.send('setHtml', html);
|
||||
|
||||
const search = BaseModel.byId(this.props.searches, this.props.selectedSearchId);
|
||||
const keywords = search ? Search.keywords(search.query_pattern) : [];
|
||||
this.webview_.send('setMarkers', keywords);
|
||||
|
||||
if (htmlHasChanged || !ArrayUtils.contentEquals(this.lastSetMarkers_, keywords)) {
|
||||
this.lastSetMarkers_ = [];
|
||||
this.webview_.send('setMarkers', keywords);
|
||||
}
|
||||
}
|
||||
|
||||
const toolbarItems = [];
|
||||
@@ -739,7 +870,7 @@ class NoteTextComponent extends React.Component {
|
||||
|
||||
const titleBarDate = <span style={Object.assign({}, theme.textStyle, {color: theme.colorFaded})}>{time.formatMsToLocal(note.user_updated_time)}</span>
|
||||
|
||||
const viewer = <webview
|
||||
const viewer = <webview
|
||||
style={viewerStyle}
|
||||
nodeintegration="1"
|
||||
src="gui/note-viewer/index.html"
|
||||
@@ -794,7 +925,7 @@ class NoteTextComponent extends React.Component {
|
||||
/>
|
||||
|
||||
return (
|
||||
<div style={rootStyle}>
|
||||
<div style={rootStyle} onDrop={this.onDrop_}>
|
||||
<div style={titleBarStyle}>
|
||||
{ titleEditor }
|
||||
{ titleBarDate }
|
||||
|
@@ -250,6 +250,21 @@ class SideBarComponent extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
if (itemType === BaseModel.TYPE_TAG) {
|
||||
menu.append(
|
||||
new MenuItem({
|
||||
label: _('Rename'),
|
||||
click: async () => {
|
||||
this.props.dispatch({
|
||||
type: "WINDOW_COMMAND",
|
||||
name: "renameTag",
|
||||
id: itemId
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
menu.popup(bridge().window());
|
||||
}
|
||||
|
||||
|
@@ -20,6 +20,10 @@
|
||||
color: white;
|
||||
}
|
||||
|
||||
ul ul, ul ol, ol ul, ol ol {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.katex { font-size: 1.3em; } /* This controls the global Katex font size*/
|
||||
</style>
|
||||
</head>
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2357
ElectronClient/app/package-lock.json
generated
2357
ElectronClient/app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Joplin",
|
||||
"version": "1.0.89",
|
||||
"version": "1.0.94",
|
||||
"description": "Joplin for Desktop",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
@@ -48,6 +48,9 @@
|
||||
"oneClick": false,
|
||||
"allowToChangeInstallationDirectory": true
|
||||
},
|
||||
"portable": {
|
||||
"artifactName": "${productName}Portable.${ext}"
|
||||
},
|
||||
"mac": {
|
||||
"icon": "../../Assets/macOs.icns",
|
||||
"asar": false
|
||||
@@ -92,6 +95,7 @@
|
||||
"markdown-it": "^8.4.0",
|
||||
"markdown-it-katex": "^2.0.3",
|
||||
"md5": "^2.2.1",
|
||||
"mermaid": "^8.0.0-rc.8",
|
||||
"mime": "^2.0.3",
|
||||
"moment": "^2.19.1",
|
||||
"node-fetch": "^1.7.3",
|
||||
|
47
README.md
47
README.md
@@ -20,15 +20,17 @@ Three types of applications are available: for the **desktop** (Windows, macOS a
|
||||
|
||||
Operating System | Download | Alternative
|
||||
-----------------|--------|-------------------
|
||||
Windows (32 and 64-bit) | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.89/Joplin-1.0.89.exe'><img alt='Get it on Windows' height="40px" src='https://joplin.cozic.net/images/BadgeWindows.png'/></a> |
|
||||
macOS | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.89/Joplin-1.0.89.dmg'><img alt='Get it on macOS' height="40px" src='https://joplin.cozic.net/images/BadgeMacOS.png'/></a> |
|
||||
Linux | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.89/Joplin-1.0.89-x86_64.AppImage'><img alt='Get it on Linux' height="40px" src='https://joplin.cozic.net/images/BadgeLinux.png'/></a> | An Arch Linux package [is also available](#terminal-application).
|
||||
Windows (32 and 64-bit) | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.93/Joplin-Setup-1.0.93.exe'><img alt='Get it on Windows' height="40px" src='https://joplin.cozic.net/images/BadgeWindows.png'/></a> | or Get the <a href='https://github.com/laurent22/joplin/releases/download/v1.0.93/JoplinPortable.exe'>Portable version</a><br>(to run from a USB key, etc.)
|
||||
macOS | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.93/Joplin-1.0.93.dmg'><img alt='Get it on macOS' height="40px" src='https://joplin.cozic.net/images/BadgeMacOS.png'/></a> |
|
||||
Linux | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.93/Joplin-1.0.93-x86_64.AppImage'><img alt='Get it on Linux' height="40px" src='https://joplin.cozic.net/images/BadgeLinux.png'/></a> | An Arch Linux package<br>[is also available](#terminal-application).
|
||||
|
||||
The [portable application](https://en.wikipedia.org/wiki/Portable_application) allows installing the software on a portable device such as a USB key. Simply copy the file JoplinPortable.exe in any directory on that USB key ; the application will then create a directory called "JoplinProfile" next to the executable file.
|
||||
|
||||
## Mobile applications
|
||||
|
||||
Operating System | Download | Alt. Download
|
||||
-----------------|----------|----------------
|
||||
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://joplin.cozic.net/images/BadgeAndroid.png'/></a> | or [Download APK File](https://github.com/laurent22/joplin-android/releases/download/android-v1.0.123/joplin-v1.0.123.apk)
|
||||
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://joplin.cozic.net/images/BadgeAndroid.png'/></a> | or [Download APK File](https://github.com/laurent22/joplin-android/releases/download/android-v1.0.125/joplin-v1.0.125.apk)
|
||||
iOS | <a href='https://itunes.apple.com/us/app/joplin/id1315599797'><img alt='Get it on the App Store' height="40px" src='https://joplin.cozic.net/images/BadgeIOS.png'/></a> | -
|
||||
|
||||
## Terminal application
|
||||
@@ -124,7 +126,7 @@ Joplin can export to the JEX format (Joplin Export file), which is a tar file th
|
||||
|
||||
One of the goals of Joplin was to avoid being tied to any particular company or service, whether it is Evernote, Google or Microsoft. As such the synchronisation is designed without any hard dependency to any particular service. Most of the synchronisation process is done at an abstract level and access to external services, such as Nextcloud or Dropbox, is done via lightweight drivers. It is easy to support new services by creating simple drivers that provide a filesystem-like interface, i.e. the ability to read, write, delete and list items. It is also simple to switch from one service to another or to even sync to multiple services at once. Each note, notebook, tags, as well as the relation between items is transmitted as plain text files during synchronisation, which means the data can also be moved to a different application, can be easily backed up, inspected, etc.
|
||||
|
||||
Currently, synchronisation is possible with Nextcloud, Dropbox (by default) or the local filesystem. To setup synchronisation please follow the instructions below. After that, the application will synchronise in the background whenever it is running, or you can click on "Synchronise" to start a synchronisation manually.
|
||||
Currently, synchronisation is possible with Nextcloud, Dropbox (by default), OneDrive or the local filesystem. To setup synchronisation please follow the instructions below. After that, the application will synchronise in the background whenever it is running, or you can click on "Synchronise" to start a synchronisation manually.
|
||||
|
||||
## Nextcloud synchronisation
|
||||
|
||||
@@ -183,7 +185,9 @@ For a more technical description, mostly relevant for development or to review t
|
||||
|
||||
Any kind of file can be attached to a note. In Markdown, links to these files are represented as a simple ID to the resource. In the note viewer, these files, if they are images, will be displayed or, if they are other files (PDF, text files, etc.) they will be displayed as links. Clicking on this link will open the file in the default application.
|
||||
|
||||
Resources that are not attached to any note will be automatically deleted after a day or two.
|
||||
On the **desktop application**, images can be attached either by clicking on "Attach file" or by pasting (with Ctrl+V) an image directly in the editor, or by drag and dropping an image.
|
||||
|
||||
Resources that are not attached to any note will be automatically deleted after a day or two (see [rationale](https://github.com/laurent22/joplin/issues/154#issuecomment-356582366)).
|
||||
|
||||
**Important:** Resources larger than 10 MB are not currently supported on mobile. They will crash the application when synchronising so it is recommended not to attach such resources at the moment. The issue is being looked at.
|
||||
|
||||
@@ -253,6 +257,10 @@ Checkboxes can be added like so:
|
||||
|
||||
The checkboxes can then be ticked in the mobile and desktop applications.
|
||||
|
||||
## HTML support
|
||||
|
||||
Only the `<br>` tag is supported - it can be used to force a new line, which is convenient to insert new lines inside table cells. For security reasons, other HTML tags are not supported.
|
||||
|
||||
# Donations
|
||||
|
||||
Donations to Joplin support the development of the project. Developing quality applications mostly takes time, but there are also some expenses, such as digital certificates to sign the applications, app store fees, hosting, etc. Most of all, your donation will make it possible to keep up the current development standard.
|
||||
@@ -262,6 +270,7 @@ Please see the [donation page](https://joplin.cozic.net/donate/) for information
|
||||
# Community
|
||||
|
||||
- For general discussion about Joplin, user support, software development questions, and to discuss new features, go to the [Joplin Forum](https://discourse.joplin.cozic.net/). It is possible to login with your GitHub account.
|
||||
- Also see here for information about [the latest releases and general news](https://discourse.joplin.cozic.net/c/news).
|
||||
- For bug reports and feature requests, go to the [GitHub Issue Tracker](https://github.com/laurent22/joplin/issues).
|
||||
- The latest news are often posted [on this Twitter account](https://twitter.com/laurent2233).
|
||||
|
||||
@@ -287,21 +296,21 @@ Current translations:
|
||||
<!-- LOCALE-TABLE-AUTO-GENERATED -->
|
||||
| Language | Po File | Last translator | Percent done
|
||||
---|---|---|---|---
|
||||
 | Basque | [eu](https://github.com/laurent22/joplin/blob/master/CliClient/locales/eu.po) | juan.abasolo@ehu.eus | 75%
|
||||
 | Croatian | [hr_HR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/hr_HR.po) | Hrvoje Mandić <trbuhom@net.hr> | 61%
|
||||
 | Czech | [cs_CZ](https://github.com/laurent22/joplin/blob/master/CliClient/locales/cs_CZ.po) | Lukas Helebrandt <lukas@aiya.cz> | 95%
|
||||
 | Dansk | [da_DK](https://github.com/laurent22/joplin/blob/master/CliClient/locales/da_DK.po) | Morten Juhl-Johansen Zölde-Fejér <mjjzf@syntaktisk. | 97%
|
||||
 | Deutsch | [de_DE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/de_DE.po) | Philipp Zumstein <zuphilip@gmail.com> | 98%
|
||||
 | Basque | [eu](https://github.com/laurent22/joplin/blob/master/CliClient/locales/eu.po) | juan.abasolo@ehu.eus | 73%
|
||||
 | Croatian | [hr_HR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/hr_HR.po) | Hrvoje Mandić (trbuhom@net.hr) | 59%
|
||||
 | Czech | [cs_CZ](https://github.com/laurent22/joplin/blob/master/CliClient/locales/cs_CZ.po) | Lukas Helebrandt (lukas@aiya.cz) | 93%
|
||||
 | Dansk | [da_DK](https://github.com/laurent22/joplin/blob/master/CliClient/locales/da_DK.po) | Morten Juhl-Johansen Zölde-Fejér (mjjzf@syntaktisk. | 95%
|
||||
 | Deutsch | [de_DE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/de_DE.po) | Philipp Zumstein (zuphilip@gmail.com) | 96%
|
||||
 | English | [en_GB](https://github.com/laurent22/joplin/blob/master/CliClient/locales/en_GB.po) | | 100%
|
||||
 | Español | [es_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/es_ES.po) | Fernando Martín <f@mrtn.es> | 99%
|
||||
 | Español | [es_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/es_ES.po) | Fernando Martín (f@mrtn.es) | 97%
|
||||
 | Français | [fr_FR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/fr_FR.po) | Laurent Cozic | 98%
|
||||
 | Galician | [gl_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/gl_ES.po) | Marcos Lans <marcoslansgarza@gmail.com> | 95%
|
||||
 | Italiano | [it_IT](https://github.com/laurent22/joplin/blob/master/CliClient/locales/it_IT.po) | | 63%
|
||||
 | Nederlands | [nl_BE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nl_BE.po) | | 76%
|
||||
 | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/pt_BR.po) | Renato Nunes Bastos <rnbastos@gmail.com> | 97%
|
||||
 | Русский | [ru_RU](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ru_RU.po) | Artyom Karlov <artyom.karlov@gmail.com> | 94%
|
||||
 | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_CN.po) | | 91%
|
||||
 | 日本語 | [ja_JP](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ja_JP.po) | | 61%
|
||||
 | Galician | [gl_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/gl_ES.po) | Marcos Lans (marcoslansgarza@gmail.com) | 93%
|
||||
 | Italiano | [it_IT](https://github.com/laurent22/joplin/blob/master/CliClient/locales/it_IT.po) | | 61%
|
||||
 | Nederlands | [nl_BE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nl_BE.po) | | 74%
|
||||
 | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/pt_BR.po) | Renato Nunes Bastos (rnbastos@gmail.com) | 95%
|
||||
 | Русский | [ru_RU](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ru_RU.po) | Artyom Karlov (artyom.karlov@gmail.com) | 92%
|
||||
 | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_CN.po) | | 89%
|
||||
 | 日本語 | [ja_JP](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ja_JP.po) | | 59%
|
||||
<!-- LOCALE-TABLE-AUTO-GENERATED -->
|
||||
|
||||
# Known bugs
|
||||
|
@@ -90,8 +90,8 @@ android {
|
||||
applicationId "net.cozic.joplin"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 26
|
||||
versionCode 2097301
|
||||
versionName "1.0.123"
|
||||
versionCode 2097303
|
||||
versionName "1.0.125"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86"
|
||||
}
|
||||
|
@@ -38,15 +38,6 @@
|
||||
<!-- ==================================== -->
|
||||
<!-- START react-native-push-notification -->
|
||||
<!-- ==================================== -->
|
||||
<receiver
|
||||
android:name="com.google.android.gms.gcm.GcmReceiver"
|
||||
android:exported="true"
|
||||
android:permission="com.google.android.c2dm.permission.SEND" >
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
|
||||
<category android:name="${applicationId}" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationPublisher" />
|
||||
<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationBootEventReceiver">
|
||||
@@ -55,13 +46,6 @@
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<service android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationRegistrationService"/>
|
||||
<service
|
||||
android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationListenerService"
|
||||
android:exported="false" >
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<!-- ================================== -->
|
||||
<!-- END react-native-push-notification -->
|
||||
<!-- ================================== -->
|
||||
|
@@ -17,11 +17,11 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>10.0.21</string>
|
||||
<string>10.0.22</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>21</string>
|
||||
<string>22</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
|
@@ -45,4 +45,17 @@ ArrayUtils.findByKey = function(array, key, value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ArrayUtils.contentEquals = function(array1, array2) {
|
||||
if (array1 === array2) return true;
|
||||
if (!array1.length && !array2.length) return true;
|
||||
if (array1.length !== array2.length) return false;
|
||||
|
||||
for (let i = 0; i < array1.length; i++) {
|
||||
const a1 = array1[i];
|
||||
if (array2.indexOf(a1) < 0) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports = ArrayUtils;
|
@@ -199,6 +199,7 @@ class BaseApplication {
|
||||
let options = {
|
||||
order: stateUtils.notesOrder(state.settings),
|
||||
uncompletedTodosOnTop: Setting.value('uncompletedTodosOnTop'),
|
||||
showCompletedTodos: Setting.value('showCompletedTodos'),
|
||||
caseInsensitive: true,
|
||||
};
|
||||
|
||||
@@ -283,6 +284,10 @@ class BaseApplication {
|
||||
refreshNotes = true;
|
||||
}
|
||||
|
||||
if (this.hasGui() && ((action.type == 'SETTING_UPDATE_ONE' && action.key == 'showCompletedTodos') || action.type == 'SETTING_UPDATE_ALL')) {
|
||||
refreshNotes = true;
|
||||
}
|
||||
|
||||
if (this.hasGui() && ((action.type == 'SETTING_UPDATE_ONE' && action.key.indexOf('notes.sortOrder') === 0) || action.type == 'SETTING_UPDATE_ALL')) {
|
||||
refreshNotes = true;
|
||||
}
|
||||
@@ -372,6 +377,14 @@ class BaseApplication {
|
||||
return flags.matched;
|
||||
}
|
||||
|
||||
determineProfileDir(initArgs) {
|
||||
if (initArgs.profileDir) return initArgs.profileDir;
|
||||
|
||||
if (process && process.env && process.env.PORTABLE_EXECUTABLE_DIR) return process.env.PORTABLE_EXECUTABLE_DIR + '/JoplinProfile';
|
||||
|
||||
return os.homedir() + '/.config/' + Setting.value('appName');
|
||||
}
|
||||
|
||||
async start(argv) {
|
||||
let startFlags = await this.handleStartFlags_(argv);
|
||||
|
||||
@@ -379,17 +392,11 @@ class BaseApplication {
|
||||
let initArgs = startFlags.matched;
|
||||
if (argv.length) this.showPromptString_ = false;
|
||||
|
||||
// if (process.argv[1].indexOf('joplindev') >= 0) {
|
||||
// if (!initArgs.profileDir) initArgs.profileDir = '/mnt/d/Temp/TestNotes2';
|
||||
// initArgs.logLevel = Logger.LEVEL_DEBUG;
|
||||
// initArgs.env = 'dev';
|
||||
// }
|
||||
|
||||
let appName = initArgs.env == 'dev' ? 'joplindev' : 'joplin';
|
||||
if (Setting.value('appId').indexOf('-desktop') >= 0) appName += '-desktop';
|
||||
Setting.setConstant('appName', appName);
|
||||
|
||||
const profileDir = initArgs.profileDir ? initArgs.profileDir : os.homedir() + '/.config/' + Setting.value('appName');
|
||||
const profileDir = this.determineProfileDir(initArgs);
|
||||
const resourceDir = profileDir + '/resources';
|
||||
const tempDir = profileDir + '/tmp';
|
||||
|
||||
|
@@ -196,7 +196,7 @@ class DropboxApi {
|
||||
return loadResponseJson();
|
||||
} catch (error) {
|
||||
tryCount++;
|
||||
if (error.code.indexOf('too_many_write_operations') >= 0) {
|
||||
if (error && error.code && error.code.indexOf('too_many_write_operations') >= 0) {
|
||||
this.logger().warn('too_many_write_operations ' + tryCount);
|
||||
if (tryCount >= 3) {
|
||||
throw error;
|
||||
|
14
ReactNativeClient/lib/HtmlToMarkdownParser.js
Normal file
14
ReactNativeClient/lib/HtmlToMarkdownParser.js
Normal file
@@ -0,0 +1,14 @@
|
||||
const { enexXmlToMd } = require('lib/import-enex-md-gen.js');
|
||||
|
||||
class HtmlToMarkdownParser {
|
||||
|
||||
async parse(html, options = {}) {
|
||||
if (!options.baseUrl) options.baseUrl = '';
|
||||
|
||||
const markdown = await enexXmlToMd(html, [], options);
|
||||
return markdown;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = HtmlToMarkdownParser;
|
@@ -157,9 +157,12 @@ class MdToHtml {
|
||||
rendererPlugin_(language) {
|
||||
if (!language) return null;
|
||||
|
||||
const handlers = {};
|
||||
handlers['katex'] = new MdToHtml_Katex();
|
||||
return language in handlers ? handlers[language] : null;
|
||||
if (!this.rendererPlugins_) {
|
||||
this.rendererPlugins_ = {};
|
||||
this.rendererPlugins_['katex'] = new MdToHtml_Katex();
|
||||
}
|
||||
|
||||
return language in this.rendererPlugins_ ? this.rendererPlugins_[language] : null;
|
||||
}
|
||||
|
||||
parseInlineCodeLanguage_(content) {
|
||||
@@ -389,7 +392,7 @@ class MdToHtml {
|
||||
const md = new MarkdownIt({
|
||||
breaks: true,
|
||||
linkify: true,
|
||||
html: true,
|
||||
html: false, // For security, HTML tags are not supported - https://github.com/laurent22/joplin/issues/500
|
||||
});
|
||||
|
||||
// This is currently used only so that the $expression$ and $$\nexpression\n$$ blocks are translated
|
||||
@@ -435,6 +438,9 @@ class MdToHtml {
|
||||
}
|
||||
}
|
||||
|
||||
// Support <br> tag to allow newlines inside table cells
|
||||
renderedBody = renderedBody.replace(/<br>/gi, '<br>');
|
||||
|
||||
// https://necolas.github.io/normalize.css/
|
||||
const normalizeCss = `
|
||||
html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
|
@@ -5,15 +5,28 @@ const Setting = require('lib/models/Setting');
|
||||
|
||||
class MdToHtml_Katex {
|
||||
|
||||
constructor() {
|
||||
this.cache_ = {};
|
||||
this.assetsLoaded_ = false;
|
||||
}
|
||||
|
||||
name() {
|
||||
return 'katex';
|
||||
}
|
||||
|
||||
processContent(renderedTokens, content, tagType) {
|
||||
try {
|
||||
let renderered = katex.renderToString(content, {
|
||||
displayMode: tagType === 'block',
|
||||
});
|
||||
const cacheKey = tagType + '_' + content;
|
||||
let renderered = null;
|
||||
|
||||
if (this.cache_[cacheKey]) {
|
||||
renderered = this.cache_[cacheKey];
|
||||
} else {
|
||||
renderered = katex.renderToString(content, {
|
||||
displayMode: tagType === 'block',
|
||||
});
|
||||
this.cache_[cacheKey] = renderered;
|
||||
}
|
||||
|
||||
if (tagType === 'block') renderered = '<p>' + renderered + '</p>';
|
||||
|
||||
@@ -29,6 +42,8 @@ class MdToHtml_Katex {
|
||||
}
|
||||
|
||||
async loadAssets() {
|
||||
if (this.assetsLoaded_) return;
|
||||
|
||||
// In node, the fonts are simply copied using copycss to where Katex expects to find them, which is under app/gui/note-viewer/fonts
|
||||
|
||||
// In React Native, it's more complicated and we need to download and copy them to the right directory. Ideally, we should embed
|
||||
@@ -43,6 +58,8 @@ class MdToHtml_Katex {
|
||||
await shim.fetchBlob('https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0-beta1/fonts/KaTeX_Math-Italic.woff2', { overwrite: false, path: baseDir + '/fonts/KaTeX_Math-Italic.woff2' });
|
||||
await shim.fetchBlob('https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0-beta1/fonts/KaTeX_Size1-Regular.woff2', { overwrite: false, path: baseDir + '/fonts/KaTeX_Size1-Regular.woff2' });
|
||||
}
|
||||
|
||||
this.assetsLoaded_ = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -39,6 +39,19 @@ class NoteBodyViewer extends Component {
|
||||
}, 100);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
// To address https://github.com/laurent22/joplin/issues/433
|
||||
// If a checkbox in a note is ticked, the body changes, which normally would trigger a re-render
|
||||
// of this component, which has the unfortunate side effect of making the view scroll back to the top.
|
||||
// This re-rendering however is uncessary since the component is already visually updated via JS.
|
||||
// So here, if the note has not changed, we prevent the component from updating.
|
||||
// This fixes the above issue. A drawback of this is if the note is updated via sync, this change
|
||||
// will not be displayed immediately.
|
||||
const currentNoteId = this.props && this.props.note ? this.props.note.id : null;
|
||||
const nextNoteId = nextProps && nextProps.note ? nextProps.note.id : null;
|
||||
return currentNoteId !== nextNoteId || nextState.webViewLoaded !== this.state.webViewLoaded;
|
||||
}
|
||||
|
||||
render() {
|
||||
const note = this.props.note;
|
||||
const style = this.props.style;
|
||||
@@ -109,7 +122,7 @@ class NoteBodyViewer extends Component {
|
||||
let msg = event.nativeEvent.data;
|
||||
|
||||
if (msg.indexOf('checkboxclick:') === 0) {
|
||||
const newBody = this.mdToHtml_.handleCheckboxClick(msg, note.body);
|
||||
const newBody = this.mdToHtml_.handleCheckboxClick(msg, this.props.note.body);
|
||||
if (onCheckboxChange) onCheckboxChange(newBody);
|
||||
} else if (msg.indexOf('bodyscroll:') === 0) {
|
||||
//msg = msg.split(':');
|
||||
|
@@ -70,6 +70,12 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
||||
fontSize: theme.fontSize,
|
||||
flex: 1,
|
||||
},
|
||||
permissionText: {
|
||||
color: theme.color,
|
||||
fontSize: theme.fontSize,
|
||||
flex: 1,
|
||||
marginTop: 10,
|
||||
},
|
||||
settingControl: {
|
||||
color: theme.color,
|
||||
flex: 1,
|
||||
@@ -201,6 +207,21 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
||||
</View>);
|
||||
}
|
||||
|
||||
if (Platform.OS === 'android' && Platform.Version >= 23) {
|
||||
// Note: `PermissionsAndroid` doesn't work so we have to ask the user to manually
|
||||
// set these permissions. https://stackoverflow.com/questions/49771084/permission-always-returns-never-ask-again
|
||||
settingComps.push(
|
||||
<View key="permission_info" style={this.styles().settingContainer}>
|
||||
<View key="permission_info_wrapper">
|
||||
<Text key="perm1a" style={this.styles().settingText}>{_('To work correctly, the app needs the following permissions. Please enable them in your phone settings, in Apps > Joplin > Permissions')}</Text>
|
||||
<Text key="perm2" style={this.styles().permissionText}>{_('- Storage: to allow attaching files to notes and to enable filesystem synchronisation.')}</Text>
|
||||
<Text key="perm3" style={this.styles().permissionText}>{_('- Camera: to allow taking a picture and attaching it to a note.')}</Text>
|
||||
<Text key="perm4" style={this.styles().permissionText}>{_('- Location: to allow attaching geo-location information to a note.')}</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
settingComps.push(
|
||||
<View key="donate_link" style={this.styles().settingContainer}>
|
||||
<TouchableOpacity onPress={() => { Linking.openURL('https://joplin.cozic.net/donate/') }}>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
const React = require('react'); const Component = React.Component;
|
||||
const { View, Button, Text, TextInput, TouchableOpacity, StyleSheet } = require('react-native');
|
||||
const { View, Button, Text, TextInput, TouchableOpacity, StyleSheet, ScrollView } = require('react-native');
|
||||
const { connect } = require('react-redux');
|
||||
const { ScreenHeader } = require('lib/components/screen-header.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
@@ -53,7 +53,7 @@ class DropboxLoginScreenComponent extends BaseScreenComponent {
|
||||
<View style={this.styles().screen}>
|
||||
<ScreenHeader title={_('Login with Dropbox')}/>
|
||||
|
||||
<View style={this.styles().container}>
|
||||
<ScrollView style={this.styles().container}>
|
||||
<Text style={this.styles().stepText}>{_('To allow Joplin to synchronise with Dropbox, please follow the steps below:')}</Text>
|
||||
<Text style={this.styles().stepText}>{_('Step 1: Open this URL in your browser to authorise the application:')}</Text>
|
||||
<View>
|
||||
@@ -65,7 +65,10 @@ class DropboxLoginScreenComponent extends BaseScreenComponent {
|
||||
<TextInput selectionColor={theme.textSelectionColor} value={this.state.authCode} onChangeText={this.shared_.authCodeInput_change} style={theme.lineInput}/>
|
||||
|
||||
<Button disabled={this.state.checkingAuthToken} title={_("Submit")} onPress={this.shared_.submit_click}></Button>
|
||||
</View>
|
||||
|
||||
{/* Add this extra padding to make sure the view is scrollable when the keyboard is visible on small screens (iPhone SE) */}
|
||||
<View style={{ height: 200 }}></View>
|
||||
</ScrollView>
|
||||
|
||||
<DialogBox ref={dialogbox => { this.dialogbox = dialogbox }}/>
|
||||
</View>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
const React = require('react'); const Component = React.Component;
|
||||
const { Platform, Clipboard, Keyboard, BackHandler, View, Button, TextInput, WebView, Text, StyleSheet, Linking, Image } = require('react-native');
|
||||
const { Platform, Clipboard, Keyboard, BackHandler, View, Button, TextInput, WebView, Text, StyleSheet, Linking, Image, Share } = require('react-native');
|
||||
const { connect } = require('react-redux');
|
||||
const { uuid } = require('lib/uuid.js');
|
||||
const RNFS = require('react-native-fs');
|
||||
@@ -409,6 +409,13 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
this.setState({ noteTagDialogShown: true });
|
||||
}
|
||||
|
||||
async share_onPress() {
|
||||
await Share.share({
|
||||
message: this.state.note.title + '\n\n' + this.state.note.body,
|
||||
title: this.state.note.title,
|
||||
});
|
||||
}
|
||||
|
||||
setAlarm_onPress() {
|
||||
this.setState({ alarmDialogShown: true });
|
||||
}
|
||||
@@ -468,6 +475,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
output.push({ title: _('Set alarm'), onPress: () => { this.setState({ alarmDialogShown: true }) }});;
|
||||
}
|
||||
|
||||
output.push({ title: _('Share'), onPress: () => { this.share_onPress(); } });
|
||||
if (isSaved) output.push({ title: _('Tags'), onPress: () => { this.tags_onPress(); } });
|
||||
output.push({ title: isTodo ? _('Convert to note') : _('Convert to todo'), onPress: () => { this.toggleIsTodo_onPress(); } });
|
||||
if (isSaved) output.push({ title: _('Copy Markdown link'), onPress: () => { this.copyMarkdownLink_onPress(); } });
|
||||
@@ -512,7 +520,13 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
this.saveOneProperty('body', newBody);
|
||||
};
|
||||
|
||||
bodyComponent = <NoteBodyViewer onJoplinLinkClick={this.onJoplinLinkClick_} style={this.styles().noteBodyViewer} webViewStyle={theme} note={note} onCheckboxChange={(newBody) => { onCheckboxChange(newBody) }}/>
|
||||
bodyComponent = <NoteBodyViewer
|
||||
onJoplinLinkClick={this.onJoplinLinkClick_}
|
||||
style={this.styles().noteBodyViewer}
|
||||
webViewStyle={theme}
|
||||
note={note}
|
||||
onCheckboxChange={(newBody) => { onCheckboxChange(newBody) }}
|
||||
/>
|
||||
} else {
|
||||
const focusBody = !isNew && !!note.title;
|
||||
|
||||
|
@@ -53,6 +53,11 @@ class NotesScreenComponent extends BaseScreenComponent {
|
||||
id: { name: 'uncompletedTodosOnTop', value: !Setting.value('uncompletedTodosOnTop') },
|
||||
});
|
||||
|
||||
buttons.push({
|
||||
text: makeCheckboxText(Setting.value('showCompletedTodos'), 'tick', '[ ' + Setting.settingMetadata('showCompletedTodos').label() + ' ]'),
|
||||
id: { name: 'showCompletedTodos', value: !Setting.value('showCompletedTodos') },
|
||||
});
|
||||
|
||||
const r = await dialogs.pop(this, Setting.settingMetadata('notes.sortOrder.field').label(), buttons);
|
||||
if (!r) return;
|
||||
|
||||
@@ -79,6 +84,7 @@ class NotesScreenComponent extends BaseScreenComponent {
|
||||
let options = {
|
||||
order: props.notesOrder,
|
||||
uncompletedTodosOnTop: props.uncompletedTodosOnTop,
|
||||
showCompletedTodos: props.showCompletedTodos,
|
||||
caseInsensitive: true,
|
||||
};
|
||||
|
||||
@@ -219,6 +225,7 @@ const NotesScreen = connect(
|
||||
notes: state.notes,
|
||||
notesSource: state.notesSource,
|
||||
uncompletedTodosOnTop: state.settings.uncompletedTodosOnTop,
|
||||
showCompletedTodos: state.settings.showCompletedTodos,
|
||||
theme: state.settings.theme,
|
||||
noteSelectionEnabled: state.noteSelectionEnabled,
|
||||
notesOrder: stateUtils.notesOrder(state.settings),
|
||||
|
@@ -2,6 +2,7 @@ const { reg } = require('lib/registry.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
|
||||
const shared = {};
|
||||
|
||||
@@ -20,7 +21,9 @@ shared.saveNoteButton_press = async function(comp, folderId = null) {
|
||||
if (folderId) {
|
||||
note.parent_id = folderId;
|
||||
} else if (!note.parent_id) {
|
||||
let folder = await Folder.defaultFolder();
|
||||
const activeFolderId = Setting.value('activeFolderId');
|
||||
let folder = await Folder.load(activeFolderId);
|
||||
if (!folder) folder = await Folder.defaultFolder();
|
||||
if (!folder) return;
|
||||
note.parent_id = folder.id;
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
const Setting = require('lib/models/Setting');
|
||||
const { reg } = require('lib/registry.js');
|
||||
|
||||
const reduxSharedMiddleware = function(store, next, action) {
|
||||
const newState = store.getState();
|
||||
@@ -6,6 +7,11 @@ const reduxSharedMiddleware = function(store, next, action) {
|
||||
if (action.type == 'FOLDER_SET_COLLAPSED' || action.type == 'FOLDER_TOGGLE') {
|
||||
Setting.setValue('collapsedFolderIds', newState.collapsedFolderIds);
|
||||
}
|
||||
|
||||
if (action.type === 'SETTING_UPDATE_ONE' && !!action.key.match(/^sync\.\d+\.path$/)) {
|
||||
reg.resetSyncTarget();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = reduxSharedMiddleware;
|
@@ -22,6 +22,11 @@ class FileApiDriverDropbox {
|
||||
return '/' + path;
|
||||
}
|
||||
|
||||
hasErrorCode_(error, errorCode) {
|
||||
if (!error || !error.code) return false;
|
||||
return error.code.indexOf(errorCode) >= 0;
|
||||
}
|
||||
|
||||
async stat(path) {
|
||||
try {
|
||||
const metadata = await this.api().exec('POST', 'files/get_metadata', {
|
||||
@@ -30,7 +35,7 @@ class FileApiDriverDropbox {
|
||||
|
||||
return this.metadataToStat_(metadata, path);
|
||||
} catch (error) {
|
||||
if (error.code.indexOf('not_found') >= 0) {
|
||||
if (this.hasErrorCode_(error, 'not_found')) {
|
||||
// ignore
|
||||
} else {
|
||||
throw error;
|
||||
@@ -83,7 +88,7 @@ class FileApiDriverDropbox {
|
||||
} catch (error) {
|
||||
// If there's an error related to an invalid cursor, clear the cursor and retry.
|
||||
if (cursor) {
|
||||
if (error.httpStatus === 400 || error.code.indexOf('reset') >= 0) {
|
||||
if ((error && error.httpStatus === 400) || this.hasErrorCode_(error, 'reset')) {
|
||||
// console.info('Clearing cursor and retrying', error);
|
||||
cursor = null;
|
||||
continue;
|
||||
@@ -126,7 +131,7 @@ class FileApiDriverDropbox {
|
||||
}, options);
|
||||
return response;
|
||||
} catch (error) {
|
||||
if (error.code.indexOf('not_found') >= 0) {
|
||||
if (this.hasErrorCode_(error, 'not_found')) {
|
||||
return null;
|
||||
} else {
|
||||
throw error;
|
||||
@@ -140,7 +145,7 @@ class FileApiDriverDropbox {
|
||||
path: this.makePath_(path),
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.code.indexOf('path/conflict') >= 0) {
|
||||
if (this.hasErrorCode_(error, 'path/conflict')) {
|
||||
// Ignore
|
||||
} else {
|
||||
throw error;
|
||||
@@ -166,7 +171,7 @@ class FileApiDriverDropbox {
|
||||
path: this.makePath_(path),
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.code.indexOf('not_found') >= 0) {
|
||||
if (this.hasErrorCode_(error, 'not_found')) {
|
||||
// ignore
|
||||
} else {
|
||||
throw error;
|
||||
|
@@ -51,6 +51,10 @@ class FileApi {
|
||||
return 0;
|
||||
}
|
||||
|
||||
baseDir() {
|
||||
return this.baseDir_;
|
||||
}
|
||||
|
||||
tempDirName() {
|
||||
if (this.tempDirName_ === null) throw Error('Temp dir not set!');
|
||||
return this.tempDirName_;
|
||||
@@ -88,7 +92,7 @@ class FileApi {
|
||||
|
||||
fullPath_(path) {
|
||||
let output = [];
|
||||
if (this.baseDir_) output.push(this.baseDir_);
|
||||
if (this.baseDir()) output.push(this.baseDir());
|
||||
if (path) output.push(path);
|
||||
return output.join('/');
|
||||
}
|
||||
@@ -99,9 +103,9 @@ class FileApi {
|
||||
if (!('includeHidden' in options)) options.includeHidden = false;
|
||||
if (!('context' in options)) options.context = null;
|
||||
|
||||
this.logger().debug('list ' + this.baseDir_);
|
||||
this.logger().debug('list ' + this.baseDir());
|
||||
|
||||
const result = await tryAndRepeat(() => this.driver_.list(this.baseDir_, options), this.requestRepeatCount());
|
||||
const result = await tryAndRepeat(() => this.driver_.list(this.baseDir(), options), this.requestRepeatCount());
|
||||
|
||||
if (!options.includeHidden) {
|
||||
let temp = [];
|
||||
@@ -112,17 +116,6 @@ class FileApi {
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
// return this.driver_.list(this.baseDir_, options).then((result) => {
|
||||
// if (!options.includeHidden) {
|
||||
// let temp = [];
|
||||
// for (let i = 0; i < result.items.length; i++) {
|
||||
// if (!isHidden(result.items[i].path)) temp.push(result.items[i]);
|
||||
// }
|
||||
// result.items = temp;
|
||||
// }
|
||||
// return result;
|
||||
// });
|
||||
}
|
||||
|
||||
// Deprectated
|
||||
@@ -187,7 +180,7 @@ class FileApi {
|
||||
}
|
||||
|
||||
clearRoot() {
|
||||
return tryAndRepeat(() => this.driver_.clearRoot(this.baseDir_), this.requestRepeatCount());
|
||||
return tryAndRepeat(() => this.driver_.clearRoot(this.baseDir()), this.requestRepeatCount());
|
||||
}
|
||||
|
||||
delta(path, options = null) {
|
||||
@@ -311,6 +304,7 @@ async function basicDelta(path, getDirStatFn, options) {
|
||||
// Clear temporary info from context. It's especially important to remove deletedItemsProcessed
|
||||
// so that they are processed again on the next sync.
|
||||
newContext.statsCache = null;
|
||||
newContext.statIdsCache = null;
|
||||
delete newContext.deletedItemsProcessed;
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
const stringPadding = require('string-padding');
|
||||
const stringToStream = require('string-to-stream')
|
||||
|
||||
const BLOCK_OPEN = "[[BLOCK_OPEN]]";
|
||||
const BLOCK_CLOSE = "[[BLOCK_CLOSE]]";
|
||||
@@ -103,7 +104,142 @@ function processMdArrayNewLines(md) {
|
||||
|
||||
if (!output.trim().length) return '';
|
||||
|
||||
return output;
|
||||
// To simplify the result, we only allow up to one empty line between blocks of text
|
||||
const mergeMultipleNewLines = function(lines) {
|
||||
let output = [];
|
||||
let newlineCount = 0;
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
if (!line.trim()) {
|
||||
newlineCount++;
|
||||
} else {
|
||||
newlineCount = 0;
|
||||
}
|
||||
|
||||
if (newlineCount >= 2) continue;
|
||||
|
||||
output.push(line);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
let lines = output.replace(/\\r/g, '').split('\n');
|
||||
lines = formatMdLayout(lines)
|
||||
lines = mergeMultipleNewLines(lines);
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
// While the processMdArrayNewLines() function adds newlines in a way that's technically correct, the resulting Markdown can look messy.
|
||||
// This is because while a "block" element should be surrounded by newlines, in practice, some should be surrounded by TWO new lines, while
|
||||
// others by only ONE.
|
||||
//
|
||||
// For instance, this:
|
||||
//
|
||||
// <li>one</li>
|
||||
// <li>two</li>
|
||||
// <li>three</li>
|
||||
//
|
||||
// should result in this:
|
||||
//
|
||||
// - one
|
||||
// - two
|
||||
// - three
|
||||
//
|
||||
// While this:
|
||||
//
|
||||
// <p>Some long paragraph</p><p>And another one</p><p>And the last paragraph</p>
|
||||
//
|
||||
// should result in this:
|
||||
//
|
||||
// Some long paragraph
|
||||
//
|
||||
// And another one
|
||||
//
|
||||
// And the last paragraph
|
||||
//
|
||||
// So in one case, one newline between tags, and in another two newlines. In HTML this would be done via CSS, but in Markdown we need
|
||||
// to add new lines. It's also important to get these newlines right because two blocks of text next to each others might be renderered
|
||||
// differently than if there's a newlines between them. So the function below parses the almost final MD and add new lines depending
|
||||
// on various rules.
|
||||
|
||||
const isHeading = function(line) {
|
||||
return !!line.match(/^#+\s/);
|
||||
}
|
||||
|
||||
const isListItem = function(line) {
|
||||
return line && line.trim().indexOf('- ') === 0;
|
||||
}
|
||||
|
||||
const isCodeLine = function(line) {
|
||||
return line && line.indexOf('\t') === 0;
|
||||
}
|
||||
|
||||
const isTableLine = function(line) {
|
||||
return line.indexOf('| ') === 0;
|
||||
}
|
||||
|
||||
const isPlainParagraph = function(line) {
|
||||
// Note: if a line is no longer than 80 characters, we don't consider it's a paragraph, which
|
||||
// means no newlines will be added before or after. This is to handle text that has been
|
||||
// written with "hard" new lines.
|
||||
if (!line || line.length < 80) return false;
|
||||
|
||||
if (isListItem(line)) return false;
|
||||
if (isHeading(line)) return false;
|
||||
if (isCodeLine(line)) return false;
|
||||
if (isTableLine(line)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function formatMdLayout(lines) {
|
||||
let previous = '';
|
||||
let newLines = [];
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
|
||||
// Add a new line at the end of a list of items
|
||||
if (isListItem(previous) && line && !isListItem(line)) {
|
||||
newLines.push('');
|
||||
|
||||
// Add a new line at the beginning of a list of items
|
||||
} else if (isListItem(line) && previous && !isListItem(previous)) {
|
||||
newLines.push('');
|
||||
|
||||
// Add a new line before a heading
|
||||
} else if (isHeading(line) && previous) {
|
||||
newLines.push('');
|
||||
|
||||
// Add a new line after a heading
|
||||
} else if (isHeading(previous) && line) {
|
||||
newLines.push('');
|
||||
|
||||
} else if (isCodeLine(line) && !isCodeLine(previous)) {
|
||||
newLines.push('');
|
||||
|
||||
} else if (!isCodeLine(line) && isCodeLine(previous)) {
|
||||
newLines.push('');
|
||||
|
||||
} else if (isTableLine(line) && !isTableLine(previous)) {
|
||||
newLines.push('');
|
||||
|
||||
} else if (!isTableLine(line) && isTableLine(previous)) {
|
||||
newLines.push('');
|
||||
|
||||
// Add a new line at beginning of paragraph
|
||||
} else if (isPlainParagraph(line) && previous) {
|
||||
newLines.push('');
|
||||
|
||||
// Add a new line at end of paragraph
|
||||
} else if (isPlainParagraph(previous) && line) {
|
||||
newLines.push('');
|
||||
}
|
||||
|
||||
newLines.push(line);
|
||||
previous = newLines[newLines.length - 1];
|
||||
}
|
||||
|
||||
return newLines;
|
||||
}
|
||||
|
||||
function isWhiteSpace(c) {
|
||||
@@ -133,8 +269,7 @@ function simplifyString(s) {
|
||||
}
|
||||
|
||||
function collapseWhiteSpaceAndAppend(lines, state, text) {
|
||||
if (state.inCode) {
|
||||
text = "\t" + text;
|
||||
if (state.inCode.length) {
|
||||
lines.push(text);
|
||||
} else {
|
||||
// Remove all \n and \r from the left and right of the text
|
||||
@@ -226,28 +361,6 @@ function isNewLineOnlyEndTag(n) {
|
||||
return ["div", "p", "li", "h1", "h2", "h3", "h4", "h5", 'h6', "dl", "dd", 'dt', "center", 'address'].indexOf(n) >= 0;
|
||||
}
|
||||
|
||||
function isCodeTag(n) {
|
||||
// NOTE: This handles "code" tags that were copied and pasted from a browser to Evernote. Evernote also has its own code block, which
|
||||
// of course is way more complicated and currently not fully supported (the code will be imported and indented properly, but it won't
|
||||
// have the extra Markdown indentation that identifies the block as code). For reference this is an example of Evernote-style code block:
|
||||
//
|
||||
// <div style="-en-codeblock: true; box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, "Courier New",
|
||||
// monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius:
|
||||
// 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902); background-position:
|
||||
// initial initial; background-repeat: initial initial;"><div>function justTesting() {</div><div> someCodeBlock();</div>
|
||||
// <div> return true;</div><div>}</div></div>
|
||||
//
|
||||
// Which in normal HTML would be:
|
||||
//
|
||||
// <code>
|
||||
// function justTesting() {
|
||||
// someCodeBlock();
|
||||
// return true;
|
||||
// }
|
||||
// <code>
|
||||
return n == "pre" || n == "code";
|
||||
}
|
||||
|
||||
function isInlineCodeTag(n) {
|
||||
return ['samp', 'kbd'].indexOf(n) >= 0;
|
||||
}
|
||||
@@ -285,7 +398,8 @@ function enexXmlToMdArray(stream, resources) {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let state = {
|
||||
inCode: false,
|
||||
inCode: [],
|
||||
inPre: false,
|
||||
inQuote: false,
|
||||
lists: [],
|
||||
anchorAttributes: [],
|
||||
@@ -366,7 +480,7 @@ function enexXmlToMdArray(stream, resources) {
|
||||
} else if (n == 'li') {
|
||||
section.lines.push(BLOCK_OPEN);
|
||||
if (!state.lists.length) {
|
||||
reject("Found <li> tag without being inside a list"); // TODO: could be a warning, but nothing to handle warnings at the moment
|
||||
console.warn("Found <li> tag without being inside a list");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -395,7 +509,9 @@ function enexXmlToMdArray(stream, resources) {
|
||||
}
|
||||
} else if (isAnchor(n)) {
|
||||
state.anchorAttributes.push(nodeAttributes);
|
||||
section.lines.push('[');
|
||||
// Need to add the '[' via this function to make sure that links within code blocks
|
||||
// are handled correctly.
|
||||
collapseWhiteSpaceAndAppend(section.lines, state, '[');
|
||||
} else if (isEmTag(n)) {
|
||||
section.lines.push("*");
|
||||
} else if (n == "en-todo") {
|
||||
@@ -404,7 +520,7 @@ function enexXmlToMdArray(stream, resources) {
|
||||
} else if (n == "hr") {
|
||||
// Needs to be surrounded by new lines so that it's properly rendered as a line when converting to HTML
|
||||
section.lines.push(NEWLINE);
|
||||
section.lines.push('----------------------------------------');
|
||||
section.lines.push('* * *');
|
||||
section.lines.push(NEWLINE);
|
||||
section.lines.push(NEWLINE);
|
||||
} else if (n == "h1") {
|
||||
@@ -422,9 +538,21 @@ function enexXmlToMdArray(stream, resources) {
|
||||
} else if (n == 'blockquote') {
|
||||
section.lines.push(BLOCK_OPEN);
|
||||
state.inQuote = true;
|
||||
} else if (isCodeTag(n, nodeAttributes)) {
|
||||
} else if (n === 'code') {
|
||||
state.inCode.push(true);
|
||||
state.currentCode = '';
|
||||
|
||||
let newSection = {
|
||||
type: 'code',
|
||||
lines: [],
|
||||
parent: section,
|
||||
}
|
||||
|
||||
section.lines.push(newSection);
|
||||
section = newSection;
|
||||
} else if (n === 'pre') {
|
||||
section.lines.push(BLOCK_OPEN);
|
||||
state.inCode = true;
|
||||
state.inPre = true;
|
||||
} else if (n == "br") {
|
||||
section.lines.push(NEWLINE);
|
||||
} else if (n == "en-media") {
|
||||
@@ -540,8 +668,25 @@ function enexXmlToMdArray(stream, resources) {
|
||||
} else if (n == 'blockquote') {
|
||||
section.lines.push(BLOCK_OPEN);
|
||||
state.inQuote = false;
|
||||
} else if (isCodeTag(n)) {
|
||||
state.inCode = false;
|
||||
} else if (n === 'code') {
|
||||
state.inCode.pop();
|
||||
|
||||
if (!state.inCode.length) {
|
||||
const codeLines = section.lines.join('').split('\n');
|
||||
section.lines = [];
|
||||
if (codeLines.length > 1) {
|
||||
for (let i = 0; i < codeLines.length; i++) {
|
||||
if (i > 0) section.lines.push('\n');
|
||||
section.lines.push('\t' + codeLines[i]);
|
||||
}
|
||||
} else {
|
||||
section.lines.push('`' + codeLines.join('') + '`');
|
||||
}
|
||||
|
||||
if (section && section.parent) section = section.parent;
|
||||
}
|
||||
} else if (n === 'pre') {
|
||||
state.inPre = false;
|
||||
section.lines.push(BLOCK_CLOSE);
|
||||
} else if (isAnchor(n)) {
|
||||
let attributes = state.anchorAttributes.pop();
|
||||
@@ -553,10 +698,47 @@ function enexXmlToMdArray(stream, resources) {
|
||||
// put the URL as is (don't wrap it in [](url)). The markdown parser, using
|
||||
// GitHub flavour, will turn this URL into a link. This is to generate slightly
|
||||
// cleaner markdown.
|
||||
let previous = section.lines[section.lines.length - 1];
|
||||
|
||||
// Need to loop on the previous tags so as to skip the special ones, which are not relevant for the below algorithm.
|
||||
let previous = null;
|
||||
for (let i = section.lines.length - 1; i >= 0; i--) {
|
||||
previous = section.lines[i];
|
||||
if ([BLOCK_OPEN, BLOCK_CLOSE, NEWLINE, NEWLINE_MERGED, SPACE].indexOf(previous) >= 0 || !previous) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (previous == '[') {
|
||||
section.lines.pop();
|
||||
section.lines.push(url);
|
||||
// We have a link that had some content but, after parsing, nothing is left. The content was most likely
|
||||
// something that shows up via CSS and which we cannot support. For example:
|
||||
//
|
||||
// <a onclick="return vote()" href="vote?id=17045576">
|
||||
// <div class="votearrow" title="upvote"></div>
|
||||
// </a>
|
||||
//
|
||||
// In the case above the arrow is displayed via CSS.
|
||||
// It is useless to display the full URL since often it is not relevant for a note (for example
|
||||
// it's interactive bits) and it's not user-generated content such as a URL that would appear in a comment.
|
||||
// So in this case, we still want to preserve the information but display it in a discreet way as a simple [L].
|
||||
|
||||
// Need to pop everything inside the current [] because it can only be special chars that we don't want (they would create uncessary newlines)
|
||||
for (let i = section.lines.length - 1; i >= 0; i--) {
|
||||
if (section.lines[i] !== '[') {
|
||||
section.lines.pop();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!url) {
|
||||
// If there's no URL and no content, pop the [ and don't save any content.
|
||||
section.lines.pop();
|
||||
} else {
|
||||
section.lines.push('(L)');
|
||||
section.lines.push('](' + url + ')');
|
||||
}
|
||||
} else if (!previous || previous == url) {
|
||||
section.lines.pop();
|
||||
section.lines.pop();
|
||||
@@ -585,6 +767,42 @@ function enexXmlToMdArray(stream, resources) {
|
||||
}
|
||||
section.lines.push(url);
|
||||
} else {
|
||||
|
||||
// Eg. converts:
|
||||
// [ Sign in ](https://example.com)
|
||||
// to:
|
||||
// [Sign in](https://example.com)
|
||||
const trimTextStartAndEndSpaces = function(lines) {
|
||||
let firstBracketIndex = 0;
|
||||
let foundFirstNonWhite = false;
|
||||
for (let i = lines.length - 1; i >= 0; i--) {
|
||||
const l = lines[i];
|
||||
if (!foundFirstNonWhite && (l === SPACE || l === ' ' || !l)) {
|
||||
lines.pop();
|
||||
} else {
|
||||
foundFirstNonWhite = true;
|
||||
}
|
||||
|
||||
if (l === '[') {
|
||||
firstBracketIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = firstBracketIndex + 1; i < lines.length; i++) {
|
||||
const l = lines[i];
|
||||
if (l === SPACE || l === ' ' ||!l) {
|
||||
lines.splice(i, 1);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
section.lines = trimTextStartAndEndSpaces(section.lines);
|
||||
|
||||
section.lines.push('](' + url + ')');
|
||||
}
|
||||
}
|
||||
@@ -619,6 +837,8 @@ function enexXmlToMdArray(stream, resources) {
|
||||
function tableHasSubTables(table) {
|
||||
for (let trIndex = 0; trIndex < table.lines.length; trIndex++) {
|
||||
const tr = table.lines[trIndex];
|
||||
if (!tr || !tr.lines) continue;
|
||||
|
||||
for (let tdIndex = 0; tdIndex < tr.lines.length; tdIndex++) {
|
||||
const td = tr.lines[tdIndex];
|
||||
for (let i = 0; i < td.lines.length; i++) {
|
||||
@@ -670,7 +890,7 @@ function drawTable(table) {
|
||||
// In here, recursively render the tables
|
||||
for (let i = 0; i < td.lines.length; i++) {
|
||||
const c = td.lines[i];
|
||||
if (typeof c === 'object') { // This is a table
|
||||
if (typeof c === 'object' && ['table', 'td', 'tr', 'th'].indexOf(c.type) >= 0) { // This is a table
|
||||
renderCurrentCells();
|
||||
currentCells = currentCells.concat(drawTable(c));
|
||||
} else { // This is plain text
|
||||
@@ -685,7 +905,10 @@ function drawTable(table) {
|
||||
|
||||
// A cell in a Markdown table cannot have actual new lines so replace
|
||||
// them with <br>, which are supported by the markdown renderers.
|
||||
let cellText = processMdArrayNewLines(td.lines).replace(/\n+/g, "<br>");
|
||||
let cellText = processMdArrayNewLines(td.lines, true)
|
||||
let lines = cellText.split('\n');
|
||||
lines = postProcessMarkdown(lines);
|
||||
cellText = lines.join('\n').replace(/\n+/g, "<br>");
|
||||
|
||||
// Inside tables cells, "|" needs to be escaped
|
||||
cellText = cellText.replace(/\|/g, "\\|");
|
||||
@@ -735,17 +958,75 @@ function drawTable(table) {
|
||||
return flatRender ? lines : lines.join('<<<<:D>>>>' + NEWLINE + '<<<<:D>>>>').split('<<<<:D>>>>');
|
||||
}
|
||||
|
||||
async function enexXmlToMd(stream, resources) {
|
||||
let result = await enexXmlToMdArray(stream, resources);
|
||||
function postProcessMarkdown(lines) {
|
||||
// After importing HTML, the resulting Markdown often has empty lines at the beginning and end due to
|
||||
// block start/end or elements that were ignored, etc. If these white spaces were intended it's not really
|
||||
// possible to detect it, so simply trim them all so that the result is more deterministic and can be
|
||||
// easily unit tested.
|
||||
const trimEmptyLines = function(lines) {
|
||||
while (lines.length) {
|
||||
if (!lines[0].trim()) {
|
||||
lines.splice(0, 1);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (lines.length) {
|
||||
if (!lines[lines.length - 1].trim()) {
|
||||
lines.pop();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
function cleanUpSpaces(lines) {
|
||||
const output = [];
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
let line = lines[i];
|
||||
|
||||
if (line.length) {
|
||||
// eg. " - Some list item" => " - Some list item"
|
||||
// Note that spaces before the "-" are preserved
|
||||
line = line.replace(/^(\s+|)-\s+/, '$1- ')
|
||||
|
||||
// eg "Some text " => "Some text"
|
||||
line = line.replace(/^(.*?)\s+$/, '$1')
|
||||
}
|
||||
|
||||
output.push(line);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
lines = trimEmptyLines(lines)
|
||||
lines = cleanUpSpaces(lines)
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
async function enexXmlToMd(xmlString, resources, options = {}) {
|
||||
const stream = stringToStream(xmlString);
|
||||
let result = await enexXmlToMdArray(stream, resources, options);
|
||||
|
||||
let mdLines = [];
|
||||
|
||||
for (let i = 0; i < result.content.lines.length; i++) {
|
||||
let line = result.content.lines[i];
|
||||
if (typeof line === 'object') { // A table
|
||||
if (typeof line === 'object' && line.type === 'table') { // A table
|
||||
const table = line;
|
||||
const tableLines = drawTable(table);
|
||||
mdLines = mdLines.concat(tableLines);
|
||||
} else if (typeof line === 'object' && line.type === 'code') {
|
||||
mdLines = mdLines.concat(line.lines);
|
||||
} else if (typeof line === 'object') {
|
||||
console.warn('Unhandled object type:', line);
|
||||
mdLines = mdLines.concat(line.lines);
|
||||
} else { // an actual line
|
||||
mdLines.push(line);
|
||||
}
|
||||
@@ -760,7 +1041,11 @@ async function enexXmlToMd(stream, resources) {
|
||||
firstAttachment = false;
|
||||
}
|
||||
|
||||
return processMdArrayNewLines(mdLines);
|
||||
let output = processMdArrayNewLines(mdLines).split('\n')
|
||||
|
||||
output = postProcessMarkdown(output);
|
||||
|
||||
return output.join('\n');
|
||||
}
|
||||
|
||||
module.exports = { enexXmlToMd, processMdArrayNewLines, NEWLINE, addResourceTag };
|
@@ -15,7 +15,6 @@ const md5 = require('md5');
|
||||
|
||||
//const Promise = require('promise');
|
||||
const fs = require('fs-extra');
|
||||
const stringToStream = require('string-to-stream')
|
||||
|
||||
function dateToTimestamp(s, zeroIfInvalid = false) {
|
||||
let m = moment(s, 'YYYYMMDDTHHmmssZ');
|
||||
@@ -219,8 +218,7 @@ function importEnex(parentFolderId, filePath, importOptions = null) {
|
||||
|
||||
while (notes.length) {
|
||||
let note = notes.shift();
|
||||
const contentStream = stringToStream(note.bodyXml);
|
||||
const body = await enexXmlToMd(contentStream, note.resources);
|
||||
const body = await enexXmlToMd(note.bodyXml, note.resources);
|
||||
delete note.bodyXml;
|
||||
|
||||
// console.info('*************************************************************************');
|
||||
|
@@ -237,6 +237,7 @@ class Note extends BaseItem {
|
||||
if (!options.conditionsParams) options.conditionsParams = [];
|
||||
if (!options.fields) options.fields = this.previewFields();
|
||||
if (!options.uncompletedTodosOnTop) options.uncompletedTodosOnTop = false;
|
||||
if (!('showCompletedTodos' in options)) options.showCompletedTodos = true;
|
||||
|
||||
if (parentId == BaseItem.getClass('Folder').conflictFolderId()) {
|
||||
options.conditions.push('is_conflict = 1');
|
||||
@@ -265,6 +266,10 @@ class Note extends BaseItem {
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.showCompletedTodos) {
|
||||
options.conditions.push('todo_completed <= 0');
|
||||
}
|
||||
|
||||
if (options.uncompletedTodosOnTop && hasTodos) {
|
||||
let cond = options.conditions.slice();
|
||||
cond.push('is_todo = 1');
|
||||
|
@@ -61,6 +61,7 @@ class Setting extends BaseModel {
|
||||
return output;
|
||||
}},
|
||||
'uncompletedTodosOnTop': { value: true, type: Setting.TYPE_BOOL, public: true, appTypes: ['cli'], label: () => _('Uncompleted to-dos on top') },
|
||||
'showCompletedTodos': { value: true, type: Setting.TYPE_BOOL, public: true, appTypes: ['cli'], label: () => _('Show completed to-dos') },
|
||||
'notes.sortOrder.field': { value: 'user_updated_time', type: Setting.TYPE_STRING, isEnum: true, public: true, appTypes: ['cli'], label: () => _('Sort notes by'), options: () => {
|
||||
const Note = require('lib/models/Note');
|
||||
const noteSortFields = ['user_updated_time', 'user_created_time', 'title'];
|
||||
@@ -99,7 +100,7 @@ class Setting extends BaseModel {
|
||||
'encryption.activeMasterKeyId': { value: '', type: Setting.TYPE_STRING, public: false },
|
||||
'encryption.passwordCache': { value: {}, type: Setting.TYPE_OBJECT, public: false },
|
||||
'style.zoom': {value: "100", type: Setting.TYPE_INT, public: true, appTypes: ['desktop'], label: () => _('Global zoom percentage'), minimum: "50", maximum: "500", step: "10"},
|
||||
'style.editor.fontFamily': {value: "", type: Setting.TYPE_STRING, public: true, appTypes: ['desktop'], label: () => _('Editor font family'), description: () => _('The font name will not be checked. If incorrect or empty, it will default to a generic monospace font.')},
|
||||
'style.editor.fontFamily': {value: "", type: Setting.TYPE_STRING, public: true, appTypes: ['desktop'], label: () => _('Editor font family'), description: () => _('This must be *monospace* font or it will not work properly. If the font is incorrect or empty, it will default to a generic monospace font.')},
|
||||
'autoUpdateEnabled': { value: true, type: Setting.TYPE_BOOL, public: true, appTypes: ['desktop'], label: () => _('Automatically update the application') },
|
||||
'sync.interval': { value: 300, type: Setting.TYPE_INT, isEnum: true, public: true, label: () => _('Synchronisation interval'), options: () => {
|
||||
return {
|
||||
|
@@ -40,7 +40,8 @@ function safeFileExtension(e) {
|
||||
return e.replace(/[^a-zA-Z0-9]/g, '')
|
||||
}
|
||||
|
||||
function toSystemSlashes(path, os) {
|
||||
function toSystemSlashes(path, os = null) {
|
||||
if (os === null) os = process.platform;
|
||||
if (os === 'win32') return path.replace(/\//g, "\\");
|
||||
return path.replace(/\\/g, "/");
|
||||
}
|
||||
|
@@ -30,6 +30,11 @@ reg.showErrorMessageBox = (message) => {
|
||||
reg.showErrorMessageBoxHandler_(message);
|
||||
}
|
||||
|
||||
reg.resetSyncTarget = (syncTargetId = null) => {
|
||||
if (syncTargetId === null) syncTargetId = Setting.value('sync.target');
|
||||
delete reg.syncTargets_[syncTargetId];
|
||||
}
|
||||
|
||||
reg.syncTarget = (syncTargetId = null) => {
|
||||
if (syncTargetId === null) syncTargetId = Setting.value('sync.target');
|
||||
if (reg.syncTargets_[syncTargetId]) return reg.syncTargets_[syncTargetId];
|
||||
@@ -59,10 +64,10 @@ reg.scheduleSync = async (delay = null, syncOptions = null) => {
|
||||
|
||||
reg.logger().info('Scheduling sync operation...');
|
||||
|
||||
if (Setting.value("env") === "dev" && delay !== 0) {
|
||||
reg.logger().info("Schedule sync DISABLED!!!");
|
||||
return;
|
||||
}
|
||||
// if (Setting.value("env") === "dev" && delay !== 0) {
|
||||
// reg.logger().info("Schedule sync DISABLED!!!");
|
||||
// return;
|
||||
// }
|
||||
|
||||
const timeoutCallback = async () => {
|
||||
reg.scheduleSyncId_ = null;
|
||||
|
@@ -35,6 +35,23 @@ function shimInit() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
// For Electron only
|
||||
shim.writeImageToFile = async function(nativeImage, mime, targetPath) {
|
||||
let buffer = null;
|
||||
|
||||
mime = mime.toLowerCase();
|
||||
|
||||
if (mime === 'image/png') {
|
||||
buffer = nativeImage.toPNG();
|
||||
} else if (mime === 'image/jpg' || mime === 'image/jpeg') {
|
||||
buffer = nativeImage.toJPEG(90);
|
||||
}
|
||||
|
||||
if (!buffer) throw new Error('Cannot reisze image because mime type "' + mime + '" is not supported: ' + targetPath);
|
||||
|
||||
await shim.fsDriver().writeFile(targetPath, buffer, 'buffer');
|
||||
}
|
||||
|
||||
const resizeImage_ = async function(filePath, targetPath, mime) {
|
||||
if (shim.isElectron()) { // For Electron
|
||||
const nativeImage = require('electron').nativeImage;
|
||||
@@ -58,17 +75,7 @@ function shimInit() {
|
||||
|
||||
image = image.resize(options);
|
||||
|
||||
let buffer = null;
|
||||
|
||||
if (mime === 'image/png') {
|
||||
buffer = image.toPNG();
|
||||
} else if (mime === 'image/jpg' || mime === 'image/jpeg') {
|
||||
buffer = image.toJPEG(90);
|
||||
}
|
||||
|
||||
if (!buffer) throw new Error('Cannot reisze image because mime type "' + mime + '" is not supported: ' + targetPath);
|
||||
|
||||
await shim.fsDriver().writeFile(targetPath, buffer, 'buffer');
|
||||
await shim.writeImageToFile(image, mime, targetPath);
|
||||
} else { // For the CLI tool
|
||||
const sharp = require('sharp');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
@@ -89,7 +96,7 @@ function shimInit() {
|
||||
}
|
||||
}
|
||||
|
||||
shim.attachFileToNote = async function(note, filePath) {
|
||||
shim.attachFileToNote = async function(note, filePath, position = null) {
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const { uuid } = require('lib/uuid.js');
|
||||
const { basename, fileExtension, safeFileExtension } = require('lib/path-utils.js');
|
||||
@@ -120,8 +127,14 @@ function shimInit() {
|
||||
await Resource.save(resource, { isNew: true });
|
||||
|
||||
const newBody = [];
|
||||
if (note.body) newBody.push(note.body);
|
||||
|
||||
if (position === null) {
|
||||
position = note.body ? note.body.length : 0;
|
||||
}
|
||||
|
||||
if (note.body && position) newBody.push(note.body.substr(0, position));
|
||||
newBody.push(Resource.markdownTag(resource));
|
||||
if (note.body) newBody.push(note.body.substr(position));
|
||||
|
||||
const newNote = Object.assign({}, note, {
|
||||
body: newBody.join('\n\n'),
|
||||
@@ -171,7 +184,7 @@ function shimInit() {
|
||||
|
||||
const requestOptions = {
|
||||
protocol: url.protocol,
|
||||
host: url.host,
|
||||
host: url.hostname,
|
||||
port: url.port,
|
||||
method: method,
|
||||
path: url.path + (url.query ? '?' + url.query : ''),
|
||||
@@ -180,9 +193,29 @@ function shimInit() {
|
||||
|
||||
const doFetchOperation = async () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let file = null;
|
||||
|
||||
const cleanUpOnError = (error) => {
|
||||
// We ignore any unlink error as we only want to report on the main error
|
||||
fs.unlink(filePath).catch(() => {}).then(() => {
|
||||
if (file) {
|
||||
file.close(() => {
|
||||
file = null;
|
||||
reject(error);
|
||||
});
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Note: relative paths aren't supported
|
||||
const file = fs.createWriteStream(filePath);
|
||||
file = fs.createWriteStream(filePath);
|
||||
|
||||
file.on('error', function(error) {
|
||||
cleanUpOnError(error);
|
||||
});
|
||||
|
||||
const request = http.request(requestOptions, function(response) {
|
||||
response.pipe(file);
|
||||
@@ -195,14 +228,12 @@ function shimInit() {
|
||||
})
|
||||
|
||||
request.on('error', function(error) {
|
||||
fs.unlink(filePath);
|
||||
reject(error);
|
||||
cleanUpOnError(error);
|
||||
});
|
||||
|
||||
request.end();
|
||||
} catch(error) {
|
||||
fs.unlink(filePath);
|
||||
reject(error);
|
||||
} catch (error) {
|
||||
cleanUpOnError(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@@ -50,6 +50,10 @@ shim.isElectron = () => {
|
||||
return false;
|
||||
}
|
||||
|
||||
shim.isPortable = function() {
|
||||
return typeof process !== 'undefined' && typeof process.env === 'object' && !!process.env.PORTABLE_EXECUTABLE_DIR;
|
||||
}
|
||||
|
||||
// Node requests can go wrong is so many different ways and with so
|
||||
// many different error messages... This handler inspects the error
|
||||
// and decides whether the request can safely be repeated or not.
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user