1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-09-05 20:56:22 +02:00

Compare commits

...

81 Commits

Author SHA1 Message Date
Laurent Cozic
92b71d3eb2 Electron release v1.0.94 2018-05-21 20:17:13 +01:00
Laurent Cozic
c32d7de7c4 Updated French transaltion 2018-05-21 20:17:00 +01:00
Laurent Cozic
c83a61d45d Android: Resolves #538 (kind of): Added info to ask user to set app permissions 2018-05-21 17:24:09 +01:00
Laurent Cozic
429f2d5aab All: Fixes #536: Allow changing sync target file path 2018-05-21 16:26:01 +01:00
Laurent Cozic
ed70cf571c Electron: partially resolves #532: Better handling of Dropbox error message 2018-05-21 16:24:57 +01:00
Laurent Cozic
fd77671575 Electron: Fixes #535: Note preview was not always updated when it should 2018-05-21 15:29:35 +01:00
Laurent Cozic
9d915a916e Fixed test 2018-05-21 15:21:08 +01:00
Laurent Cozic
acb90935c7 Fixed tests 2018-05-20 13:33:26 +01:00
Laurent Cozic
6301ba0a12 Electron: Fixes #527: Remove empty section separators from menus 2018-05-20 13:01:07 +01:00
Laurent Cozic
44e1245416 Electron: Fixes #528: Set translation in bridge functions too 2018-05-20 12:54:42 +01:00
Laurent Cozic
6527d9db83 Update translation 2018-05-20 12:31:54 +01:00
Laurent Cozic
2bcaf62a2f Merge pull request #543 from Abijeet/tag-rename
Adds functionality to allow for renaming of tags.
2018-05-20 12:24:15 +01:00
Laurent Cozic
2db3998f11 Merge pull request #540 from Abijeet/master
Added styles to fix margin bottom for  nested lists
2018-05-20 12:24:01 +01:00
Laurent Cozic
04724c58d1 All: Fixes #491: Handle non-standard ports and better handling of fetchBlob errors 2018-05-20 12:20:15 +01:00
Abijeet
48883bfa13 Adds functionality to allow for renaming of tags.
Towards #374

Signed-off-by: Abijeet <abijeetpatro@gmail.com>
2018-05-20 13:09:32 +05:30
Abijeet
b6d9e695d1 Added styles to fix margin bottom for nested lists
Fixes #482

Signed-off-by: Abijeet <abijeetpatro@gmail.com>
2018-05-19 23:39:27 +05:30
Laurent Cozic
43bab3c1bd Merge branch 'master' of github.com:laurent22/joplin 2018-05-16 15:34:09 +01:00
Laurent Cozic
dd67602b87 ios v10.0.22 2018-05-16 15:33:52 +01:00
Laurent Cozic
c4ca9cde32 Electron: Allow copying path of resources 2018-05-16 14:16:43 +01:00
Laurent Cozic
795fd8b58c HtmlToMD: Finish restoring codes that could be saved, and scoped tests to ENEX-like data 2018-05-16 10:56:30 +01:00
Laurent Cozic
c226940792 HtmlToMd: Re-applied recent changes that can be kept 2018-05-16 10:09:46 +01:00
Laurent Cozic
bdd0a6106f HtmlToMd: Revert to version a677b2e844 2018-05-16 10:09:07 +01:00
Laurent Cozic
d1ea7ad3ea HtmlToMd: Minor fix on table 2018-05-16 09:54:13 +01:00
Laurent Cozic
a2b1181f7c HtmlToMd: Fixed handling of inline tags 2018-05-15 13:26:53 +01:00
Laurent Cozic
8cce2f17d5 HtmlToMd: Improved import of inline and code block 2018-05-15 13:13:55 +01:00
Laurent Cozic
658b911513 HtmlToMd: Minor tweak on LI tag 2018-05-15 09:52:56 +01:00
Laurent Cozic
3c95979d94 HtmlToMd: Fix LI tags that contains blocks 2018-05-14 23:13:17 +01:00
Laurent Cozic
2e32211a28 HtmlToMd: Fix conversion of tables 2018-05-14 23:04:40 +01:00
Laurent Cozic
ba2874173d HtmlToMd: Added more test cases and fixed some issues 2018-05-14 19:48:52 +01:00
Laurent Cozic
ba9598682c HtmlToMd: Fixed various tests 2018-05-14 18:46:04 +01:00
Laurent Cozic
30bfd82683 Update website 2018-05-14 12:47:51 +01:00
Laurent Cozic
10c6774c28 Fix download links 2018-05-14 12:47:36 +01:00
Laurent Cozic
c4ad9019aa Update website 2018-05-14 12:42:06 +01:00
Laurent Cozic
7c99ab9947 Update Readme for portable app 2018-05-14 12:41:45 +01:00
Laurent Cozic
feb7778fe4 Electron release v1.0.93 2018-05-14 12:18:40 +01:00
Laurent Cozic
b45185780f Fixed auto-update download url 2018-05-14 12:18:00 +01:00
Laurent Cozic
4e032c0c55 Electron release v1.0.92 2018-05-14 11:36:40 +01:00
Laurent Cozic
2e2b35dfeb Fixed import of tables and added tests 2018-05-14 11:36:02 +01:00
Laurent Cozic
526ef7e1d2 Don't add new table when processing a table 2018-05-14 11:24:19 +01:00
Laurent Cozic
a37005446a Fixed tests 2018-05-14 11:23:18 +01:00
Laurent Cozic
e012b927dc Merge branch 'clipper' 2018-05-14 11:09:57 +01:00
Laurent Cozic
359b8d5545 Various tweaks to build portable version 2018-05-14 11:08:33 +01:00
Laurent Cozic
23c592b322 Merge branch 'master' of github.com:laurent22/joplin 2018-05-14 10:31:32 +01:00
Laurent Cozic
9aeddf86f4 Updated PortableApps build files 2018-05-14 10:31:16 +01:00
FoxMaSk
0e1887988e add OneDrive
add OneDrive to the general description of the supported services
2018-05-13 14:42:36 +02:00
Laurent Cozic
394f2df664 All: More robust HTML to MD conversion and started adding test units for it 2018-05-12 11:48:39 +01:00
Laurent Cozic
2a04378a0d Merge branch 'clipper' 2018-05-11 15:32:45 +01:00
Laurent Cozic
bac68f2c42 All: Various changes to improve import of HTML 2018-05-11 15:29:44 +01:00
Laurent Cozic
0f0ff86ffa Merge pull request #409 from petrz12/better-evernote-import
Merge monospace text lines when importing from Evernote
2018-05-11 15:25:26 +01:00
Laurent Cozic
8b38752cbf Fix downloads 2018-05-11 14:36:52 +01:00
Laurent Cozic
3c24589450 Fixed deployment script 2018-05-11 14:36:22 +01:00
Laurent Cozic
65065a62d8 CLI v1.0.107 2018-05-11 13:50:19 +01:00
Laurent Cozic
482e9340bc Android release v1.0.125 2018-05-10 21:31:58 +01:00
Laurent Cozic
69d490996e Mobile: Remove uneeded GCM and C2DM dependencies from Android to make it acceptable for F-Droid 2018-05-10 21:25:06 +01:00
Laurent Cozic
3494937e34 Mobile: Resolves #503: Share note with other apps 2018-05-10 20:39:41 +01:00
Laurent Cozic
41ba1043be All: Fixed incorrect timeout for sync-after-save (was using ms instead of sec). Removed needless caching of note IDs in database. 2018-05-10 19:50:44 +01:00
Laurent Cozic
cc57de60c0 Update website 2018-05-10 15:48:16 +01:00
Laurent Cozic
60a2b9e5c6 Electron release v1.0.91 2018-05-10 15:24:46 +01:00
Laurent Cozic
8e1fb666a5 Electron: Fixes #510: Removed reference to missing file 2018-05-10 15:24:38 +01:00
Laurent Cozic
f4ad777bbf Update website 2018-05-10 14:32:33 +01:00
Laurent Cozic
2eacf6146a Android release v1.0.124 2018-05-10 12:24:36 +01:00
Laurent Cozic
fe2ba34cb4 Electron release v1.0.90 2018-05-10 12:22:33 +01:00
Laurent Cozic
84daa0db61 Update readme 2018-05-10 12:22:14 +01:00
Laurent Cozic
b9118a90be All: Resolves #443: Various optimisations to make dealing with large notes easier and make Katex re-rendering faster 2018-05-10 12:02:39 +01:00
Laurent Cozic
ef2ffd4e52 Electron: Resolves #200, Resolves #416: Allow attaching images by pasting them in. Allow attaching files by drag and dropping them. Insert attachement at cursor position. 2018-05-10 10:45:44 +01:00
Laurent Cozic
5e3063abe0 Updated translations 2018-05-09 21:05:52 +01:00
Laurent Cozic
f460b2497a Merge pull request #506 from fmrtn/master
Updated Spanish translation
2018-05-09 21:04:20 +01:00
Laurent Cozic
c080d7054f Merge branch 'master' of github.com:laurent22/joplin 2018-05-09 21:00:33 +01:00
Laurent Cozic
61dd4cefbc All: Resolves #345: Option to hide completed todos 2018-05-09 21:00:05 +01:00
Laurent Cozic
63d99b2d70 Mobile: Fixes #497: Make sure Dropbox text input is visible when keyboard is visible on iPhone SE 2018-05-09 19:11:48 +01:00
Laurent Cozic
55332d7671 Electron: Fixes #481: Shortcuts were not working when text editor had focus 2018-05-09 18:41:32 +01:00
Laurent Cozic
16635defcd Mobile: Fixes #455: Use active folder when creating new note from Welcome screen 2018-05-09 18:12:00 +01:00
Laurent Cozic
595cf3fcad Mobile: Fixes #433: Don't scroll note back to top when changing checkbox state 2018-05-09 18:04:48 +01:00
Fernando
c9b9f82130 Updated Spanish translation 2018-05-09 18:48:32 +02:00
Laurent Cozic
f5bca733d7 Fixed translator email encoding issue 2018-05-09 17:06:02 +01:00
Laurent Cozic
494e235e18 Electron: Resolves #500: Fixed XSS security vulnerability 2018-05-09 16:59:33 +01:00
petrz12
9f2666aef9 Evernote code block conversion added, better handling of monospace empty lines 2018-05-04 00:23:19 +02:00
petrz12
f136664c11 codeblock detection added 2018-05-02 22:39:50 +02:00
petrz12
c8c9f80cc5 Making monospace merging permanent 2018-04-21 13:55:18 +02:00
petrz12
69ddcc6e30 Remove debug lines, fixing some comments 2018-04-18 14:13:19 +02:00
petrz12
16554b22c7 Merge monospace text lines when importing from Evernote 2018-04-10 22:59:45 +02:00
125 changed files with 3546 additions and 1552 deletions

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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 ""

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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 "ノートに変換"

View File

@@ -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 ""

View File

@@ -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"

View File

@@ -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"

View File

@@ -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 "Преобразовать в заметку"

View File

@@ -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 "转换至笔记"

View File

@@ -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",

View File

@@ -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",

View File

@@ -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)

View File

@@ -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();
});
});

View 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)
}
}
}));
});

View 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">&lt;!DOCTYPE html&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">/&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&gt;</span></span><span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">&gt;</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>

View 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:

View File

@@ -0,0 +1,7 @@
<p>Subshell:</p>
<pre><code>(
set -e
false
echo Unreachable
) &amp;&amp; echo Great success
</code></pre>

View File

@@ -0,0 +1,7 @@
Subshell:
(
set -e
false
echo Unreachable
) && echo Great success

View 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>

View 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:

View 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>

View 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.

View File

@@ -0,0 +1,3 @@
<div>
<p>the&nbsp;<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>

View 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.

View 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>

View 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)

View 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>

View File

@@ -0,0 +1,9 @@
Liste de courses
- [X] Pizzas
- [X] Pain
- [X] Jambon
- [X] On its own
End

View 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>

View File

@@ -0,0 +1,3 @@
- [Github](https://github.com/zetter)
- [Twitter](https://twitter.com/czetter)
- [Lanyrd](http://lanyrd.com/profile/czetter/)

View File

@@ -0,0 +1 @@
<ul class="find-me-on"><li>Github</li><li>Twitter</li></ul>

View File

@@ -0,0 +1,2 @@
- Github
- Twitter

View 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>

View 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.

View 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

View File

@@ -0,0 +1,5 @@
| |
| --- |
| $ sudo ethtool --set-priv-flags p2p1 mlx4_rss_xor_hash_function on<br># Three empty lines follow |
Some text

View 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>

View File

@@ -0,0 +1,4 @@
| | |
| --- | --- |
| line 1<br>line 2 | |
| aaaaaa | line 3<br>line 4 |

View File

@@ -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';

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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();

View File

@@ -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 }

View File

@@ -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());
}

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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",

View File

@@ -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 -->
&nbsp; | Language | Po File | Last translator | Percent done
---|---|---|---|---
![](https://joplin.cozic.net/images/flags/es/basque_country.png) | Basque | [eu](https://github.com/laurent22/joplin/blob/master/CliClient/locales/eu.po) | juan.abasolo@ehu.eus | 75%
![](https://joplin.cozic.net/images/flags/country-4x3/hr.png) | Croatian | [hr_HR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/hr_HR.po) | Hrvoje Mandić <trbuhom@net.hr> | 61%
![](https://joplin.cozic.net/images/flags/country-4x3/cz.png) | Czech | [cs_CZ](https://github.com/laurent22/joplin/blob/master/CliClient/locales/cs_CZ.po) | Lukas Helebrandt <lukas@aiya.cz> | 95%
![](https://joplin.cozic.net/images/flags/country-4x3/dk.png) | 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%
![](https://joplin.cozic.net/images/flags/country-4x3/de.png) | Deutsch | [de_DE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/de_DE.po) | Philipp Zumstein <zuphilip@gmail.com> | 98%
![](https://joplin.cozic.net/images/flags/es/basque_country.png) | Basque | [eu](https://github.com/laurent22/joplin/blob/master/CliClient/locales/eu.po) | juan.abasolo@ehu.eus | 73%
![](https://joplin.cozic.net/images/flags/country-4x3/hr.png) | Croatian | [hr_HR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/hr_HR.po) | Hrvoje Mandić (trbuhom@net.hr) | 59%
![](https://joplin.cozic.net/images/flags/country-4x3/cz.png) | Czech | [cs_CZ](https://github.com/laurent22/joplin/blob/master/CliClient/locales/cs_CZ.po) | Lukas Helebrandt (lukas@aiya.cz) | 93%
![](https://joplin.cozic.net/images/flags/country-4x3/dk.png) | 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%
![](https://joplin.cozic.net/images/flags/country-4x3/de.png) | Deutsch | [de_DE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/de_DE.po) | Philipp Zumstein (zuphilip@gmail.com) | 96%
![](https://joplin.cozic.net/images/flags/country-4x3/gb.png) | English | [en_GB](https://github.com/laurent22/joplin/blob/master/CliClient/locales/en_GB.po) | | 100%
![](https://joplin.cozic.net/images/flags/country-4x3/es.png) | Español | [es_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/es_ES.po) | Fernando Martín <f@mrtn.es> | 99%
![](https://joplin.cozic.net/images/flags/country-4x3/es.png) | Español | [es_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/es_ES.po) | Fernando Martín (f@mrtn.es) | 97%
![](https://joplin.cozic.net/images/flags/country-4x3/fr.png) | Français | [fr_FR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/fr_FR.po) | Laurent Cozic | 98%
![](https://joplin.cozic.net/images/flags/country-4x3/es.png) | Galician | [gl_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/gl_ES.po) | Marcos Lans <marcoslansgarza@gmail.com> | 95%
![](https://joplin.cozic.net/images/flags/country-4x3/it.png) | Italiano | [it_IT](https://github.com/laurent22/joplin/blob/master/CliClient/locales/it_IT.po) | | 63%
![](https://joplin.cozic.net/images/flags/country-4x3/be.png) | Nederlands | [nl_BE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nl_BE.po) | | 76%
![](https://joplin.cozic.net/images/flags/country-4x3/br.png) | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/pt_BR.po) | Renato Nunes Bastos <rnbastos@gmail.com> | 97%
![](https://joplin.cozic.net/images/flags/country-4x3/ru.png) | Русский | [ru_RU](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ru_RU.po) | Artyom Karlov <artyom.karlov@gmail.com> | 94%
![](https://joplin.cozic.net/images/flags/country-4x3/cn.png) | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_CN.po) | | 91%
![](https://joplin.cozic.net/images/flags/country-4x3/jp.png) | 日本語 | [ja_JP](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ja_JP.po) | | 61%
![](https://joplin.cozic.net/images/flags/country-4x3/es.png) | Galician | [gl_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/gl_ES.po) | Marcos Lans (marcoslansgarza@gmail.com) | 93%
![](https://joplin.cozic.net/images/flags/country-4x3/it.png) | Italiano | [it_IT](https://github.com/laurent22/joplin/blob/master/CliClient/locales/it_IT.po) | | 61%
![](https://joplin.cozic.net/images/flags/country-4x3/be.png) | Nederlands | [nl_BE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nl_BE.po) | | 74%
![](https://joplin.cozic.net/images/flags/country-4x3/br.png) | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/pt_BR.po) | Renato Nunes Bastos (rnbastos@gmail.com) | 95%
![](https://joplin.cozic.net/images/flags/country-4x3/ru.png) | Русский | [ru_RU](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ru_RU.po) | Artyom Karlov (artyom.karlov@gmail.com) | 92%
![](https://joplin.cozic.net/images/flags/country-4x3/cn.png) | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_CN.po) | | 89%
![](https://joplin.cozic.net/images/flags/country-4x3/jp.png) | 日本語 | [ja_JP](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ja_JP.po) | | 59%
<!-- LOCALE-TABLE-AUTO-GENERATED -->
# Known bugs

View File

@@ -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"
}

View File

@@ -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 -->
<!-- ================================== -->

View File

@@ -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>

View File

@@ -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;

View File

@@ -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';

View File

@@ -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;

View 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;

View File

@@ -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(/&lt;br&gt;/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}

View File

@@ -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;
}
}

View File

@@ -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(':');

View File

@@ -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/') }}>

View File

@@ -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>

View File

@@ -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;

View File

@@ -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),

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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, &quot;Courier New&quot;,
// 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>&nbsp; &nbsp; &nbsp;someCodeBlock();</div>
// <div>&nbsp; &nbsp; &nbsp;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 };

View File

@@ -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('*************************************************************************');

View File

@@ -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');

View File

@@ -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 {

View File

@@ -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, "/");
}

View File

@@ -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;

View File

@@ -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);
}
});
};

View File

@@ -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