1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-07-13 00:10:37 +02:00

Merge branch 'master' of github.com:laurent22/joplin

This commit is contained in:
Laurent Cozic
2018-03-23 18:19:46 +00:00
148 changed files with 5968 additions and 2033 deletions

View File

@ -3,6 +3,14 @@ if: tag IS present
rvm: 2.3.3 rvm: 2.3.3
# It's important to only build production branches otherwise Electron Builder
# might take assets from dev branches and overwrite those of production.
# https://docs.travis-ci.com/user/customizing-the-build/#Building-Specific-Branches
branches:
only:
- master
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
matrix: matrix:
include: include:
- os: osx - os: osx

View File

@ -8,7 +8,7 @@
brew install yarn node brew install yarn node
echo 'export PATH="/usr/local/opt/gettext/bin:$PATH"' >> ~/.bash_profile echo 'export PATH="/usr/local/opt/gettext/bin:$PATH"' >> ~/.bash_profile
source ~/.bash_profile source ~/.bash_profile
If you get a node-gyp related error you might need to manually install it: `npm install -g node-gyp` If you get a node-gyp related error you might need to manually install it: `npm install -g node-gyp`
## Linux and Windows (WSL) dependencies ## Linux and Windows (WSL) dependencies
@ -43,6 +43,17 @@ That will create the executable file in the `dist` directory.
From `/ElectronClient` you can also run `run.sh` to run the app for testing. From `/ElectronClient` you can also run `run.sh` to run the app for testing.
## Building Electron application on Windows
```
cd Tools
npm install
cd ..\ElectronClient\app
xcopy /C /I /H /R /Y /S ..\..\ReactNativeClient\lib lib
npm install
yarn dist
```
# Building the Mobile application # Building the Mobile application
First you need to setup React Native to build projects with native code. For this, follow the instructions on the [Get Started](https://facebook.github.io/react-native/docs/getting-started.html) tutorial, in the "Building Projects with Native Code" tab. First you need to setup React Native to build projects with native code. For this, follow the instructions on the [Get Started](https://facebook.github.io/react-native/docs/getting-started.html) tutorial, in the "Building Projects with Native Code" tab.

View File

@ -1,6 +1,6 @@
# Reporting a bug # Reporting a bug
Please check first that it [has not already been reported](https://github.com/laurent22/joplin/issues?utf8=%E2%9C%93&q=is%3Aissue). Also consider [enabling debug mode](https://github.com/laurent22/joplin/blob/master/README_debugging.md) before reporting the issue so that you can provide as much details as possible to help fix it. Please check first that it [has not already been reported](https://github.com/laurent22/joplin/issues?utf8=%E2%9C%93&q=is%3Aissue). Also consider [enabling debug mode](https://github.com/laurent22/joplin/blob/master/readme/debugging.md) before reporting the issue so that you can provide as much details as possible to help fix it.
If possible, **please provide a screenshot**. A screenshot showing the problem is often more useful than a paragraph describing it as it can make it immediately clear what the issue is. If possible, **please provide a screenshot**. A screenshot showing the problem is often more useful than a paragraph describing it as it can make it immediately clear what the issue is.
@ -10,11 +10,6 @@ Again, please check that it has not already been requested. If it has, simply **
# Adding new features # Adding new features
If you want to add a new feature, consider asking about it before implementing it to make sure it is within the scope of the project. Of course you are free to create the pull request directly but it is not guaranteed it is going to be accepted. If you want to add a new feature, consider asking about it before implementing it or checking existing discussions to make sure it is within the scope of the project. Of course you are free to create the pull request directly but it is not guaranteed it is going to be accepted.
Building the apps is relatively easy - please [see the build instructions](https://github.com/laurent22/joplin/blob/master/BUILD.md) for more details. Building the apps is relatively easy - please [see the build instructions](https://github.com/laurent22/joplin/blob/master/BUILD.md) for more details.
# Coding style
- Only use tabs for indentation, not spaces.
- Do not remove or add optional characters from other lines (such as colons or new line characters) as it can make the commit needlessly big, and create conflicts with other changes.

View File

@ -496,7 +496,7 @@ class AppGui {
cmd = cmd.trim(); cmd = cmd.trim();
if (!cmd.length) return; if (!cmd.length) return;
this.logger().info('Got command: ' + cmd); // this.logger().debug('Got command: ' + cmd);
try { try {
let note = this.widget('noteList').currentItem; let note = this.widget('noteList').currentItem;
@ -757,7 +757,7 @@ class AppGui {
if (statusBar.promptActive) processShortcutKeys = false; if (statusBar.promptActive) processShortcutKeys = false;
if (processShortcutKeys) { if (processShortcutKeys) {
this.logger().info('Shortcut:', shortcutKey, keymapItem); this.logger().debug('Shortcut:', shortcutKey, keymapItem);
this.currentShortcutKeys_ = []; this.currentShortcutKeys_ = [];

View File

@ -5,6 +5,7 @@ const { JoplinDatabase } = require('lib/joplin-database.js');
const { Database } = require('lib/database.js'); const { Database } = require('lib/database.js');
const { FoldersScreenUtils } = require('lib/folders-screen-utils.js'); const { FoldersScreenUtils } = require('lib/folders-screen-utils.js');
const { DatabaseDriverNode } = require('lib/database-driver-node.js'); const { DatabaseDriverNode } = require('lib/database-driver-node.js');
const ResourceService = require('lib/services/ResourceService');
const BaseModel = require('lib/BaseModel.js'); const BaseModel = require('lib/BaseModel.js');
const Folder = require('lib/models/Folder.js'); const Folder = require('lib/models/Folder.js');
const BaseItem = require('lib/models/BaseItem.js'); const BaseItem = require('lib/models/BaseItem.js');
@ -292,7 +293,7 @@ class Application extends BaseApplication {
async execCommand(argv) { async execCommand(argv) {
if (!argv.length) return this.execCommand(['help']); if (!argv.length) return this.execCommand(['help']);
reg.logger().info('execCommand()', argv); // reg.logger().debug('execCommand()', argv);
const commandName = argv[0]; const commandName = argv[0];
this.activeCommand_ = this.findCommandByName(commandName); this.activeCommand_ = this.findCommandByName(commandName);
@ -412,6 +413,8 @@ class Application extends BaseApplication {
const tags = await Tag.allWithNotes(); const tags = await Tag.allWithNotes();
ResourceService.runInBackground();
this.dispatch({ this.dispatch({
type: 'TAG_UPDATE_ALL', type: 'TAG_UPDATE_ALL',
items: tags, items: tags,

View File

@ -87,6 +87,13 @@ process.stdout.on('error', function( err ) {
application.start(process.argv).catch((error) => { application.start(process.argv).catch((error) => {
console.error(_('Fatal error:')); if (error.code == 'flagError') {
console.error(error); console.error(error.message);
console.error(_('Type `joplin help` for usage information.'));
} else {
console.error(_('Fatal error:'));
console.error(error);
}
process.exit(1);
}); });

1383
CliClient/locales/da_DK.po Normal file

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Joplin-CLI 1.0.0\n" "Project-Id-Version: Joplin-CLI 1.0.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"Last-Translator: Tobias Strobel <git@strobeltobias.de>\n" "Last-Translator: Tobias Grasse <mail@tobias-grasse.net>\n"
"Language-Team: \n" "Language-Team: \n"
"Language: de_DE\n" "Language: de_DE\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -18,7 +18,8 @@ msgstr ""
msgid "To delete a tag, untag the associated notes." msgid "To delete a tag, untag the associated notes."
msgstr "" msgstr ""
"Hebe die Markierungen zugehöriger Notizen auf, um eine Markierung zu löschen." "Um eine Markierung zu löschen, entferne diese bei allen damit verbundenen "
"Notizen."
msgid "Please select the note or notebook to be deleted first." msgid "Please select the note or notebook to be deleted first."
msgstr "" msgstr ""
@ -26,19 +27,18 @@ msgstr ""
"soll." "soll."
msgid "Press Ctrl+D or type \"exit\" to exit the application" msgid "Press Ctrl+D or type \"exit\" to exit the application"
msgstr "Drücke Strg+D oder tippe \"exit\", um das Programm zu verlassen" msgstr "Drücke Strg+D oder tippe \"exit\" um das Programm zu verlassen"
#, javascript-format #, javascript-format
msgid "More than one item match \"%s\". Please narrow down your query." msgid "More than one item match \"%s\". Please narrow down your query."
msgstr "" msgstr ""
"Mehr als eine Notiz stimmt mit \"%s\" überein. Bitte schränke deine Suche " "Mehr als eine Notiz stimmt mit \"%s\" überein. Bitte die Suche einschränken."
"ein."
msgid "No notebook selected." msgid "No notebook selected."
msgstr "Kein Notizbuch ausgewählt." msgstr "Kein Notizbuch ausgewählt."
msgid "No notebook has been specified." msgid "No notebook has been specified."
msgstr "Kein Notizbuch wurde angegeben." msgstr "Es wurde kein Notizbuch festgelegt."
msgid "Y" msgid "Y"
msgstr "J" msgstr "J"
@ -68,7 +68,7 @@ msgstr "Kann verschlüsseltes Objekt nicht ändern"
#, javascript-format #, javascript-format
msgid "Missing required argument: %s" msgid "Missing required argument: %s"
msgstr "Fehlendes benötigtes Argument: %s" msgstr "Fehlendes erforderliches Argument: %s"
#, javascript-format #, javascript-format
msgid "%s: %s" msgid "%s: %s"
@ -104,8 +104,7 @@ msgstr ""
"gegeben sind, wird eine Liste der momentanen Konfiguration angezeigt." "gegeben sind, wird eine Liste der momentanen Konfiguration angezeigt."
msgid "Also displays unset and hidden config variables." msgid "Also displays unset and hidden config variables."
msgstr "" msgstr "Zeigt auch nicht gesetzte und versteckte Konfigurationsvariablen an."
"Zeigt auch nicht angegebene oder versteckte Konfigurationsvariablen an."
#, javascript-format #, javascript-format
msgid "%s = %s (%s)" msgid "%s = %s (%s)"
@ -119,12 +118,12 @@ msgid ""
"Duplicates the notes matching <note> to [notebook]. If no notebook is " "Duplicates the notes matching <note> to [notebook]. If no notebook is "
"specified the note is duplicated in the current notebook." "specified the note is duplicated in the current notebook."
msgstr "" msgstr ""
"Dupliziert die Notizen die mit <note> übereinstimmen zu [Notizbuch]. Wenn " "Dupliziert die Notizen die mit <note> übereinstimmen in [Notizbuch]. Wenn "
"kein Notizbuch angegeben ist, wird die Notiz in das momentane Notizbuch " "kein Notizbuch angegeben ist, wird die Notiz in das aktuelle Notizbuch "
"kopiert." "kopiert."
msgid "Marks a to-do as done." msgid "Marks a to-do as done."
msgstr "Markiert ein To-Do als abgeschlossen." msgstr "Markiert ein To-Do als erledigt."
#, javascript-format #, javascript-format
msgid "Note is not a to-do: \"%s\"" msgid "Note is not a to-do: \"%s\""
@ -134,7 +133,7 @@ msgid ""
"Manages E2EE configuration. Commands are `enable`, `disable`, `decrypt`, " "Manages E2EE configuration. Commands are `enable`, `disable`, `decrypt`, "
"`status` and `target-status`." "`status` and `target-status`."
msgstr "" msgstr ""
"Verwaltet die E2EE-Konfiguration. Die Befehle sind `enable`, `disable`, " "Verwaltet die E2EE-Konfiguration. Die Befehle lauten `enable`, `disable`, "
"`decrypt`, `status` und `target-status`." "`decrypt`, `status` und `target-status`."
msgid "Enter master password:" msgid "Enter master password:"
@ -147,8 +146,8 @@ msgid ""
"Starting decryption... Please wait as it may take several minutes depending " "Starting decryption... Please wait as it may take several minutes depending "
"on how much there is to decrypt." "on how much there is to decrypt."
msgstr "" msgstr ""
"Entschlüsselung starten.... Warte bitte, da es einige Minuten dauern kann, " "Starte Entschlüsselung.... Bitte warten, da dies je nach Anzahl der "
"je nachdem, wie viel es zu entschlüsseln gibt." "betreffenden Objekte einige Minuten dauern kann."
msgid "Completed decryption." msgid "Completed decryption."
msgstr "Entschlüsselung abgeschlossen." msgstr "Entschlüsselung abgeschlossen."
@ -161,7 +160,7 @@ msgstr "Deaktiviert"
#, javascript-format #, javascript-format
msgid "Encryption is: %s" msgid "Encryption is: %s"
msgstr "Die Verschlüsselung ist: %s" msgstr "Verschlüsselung ist: %s"
msgid "Edit note." msgid "Edit note."
msgstr "Notiz bearbeiten." msgstr "Notiz bearbeiten."
@ -169,8 +168,8 @@ msgstr "Notiz bearbeiten."
msgid "" msgid ""
"No text editor is defined. Please set it using `config editor <editor-path>`" "No text editor is defined. Please set it using `config editor <editor-path>`"
msgstr "" msgstr ""
"Kein Textverarbeitungsprogramm angegeben. Bitte lege eines mit `config " "Kein Texteditor definiert. Bitte lege einen mit `config editor <Pfad-Zum-"
"editor <Pfad-Zum-Textverarbeitungsprogramm>` fest" "Texteditor>` fest"
msgid "No active notebook." msgid "No active notebook."
msgstr "Kein aktives Notizbuch." msgstr "Kein aktives Notizbuch."
@ -192,20 +191,19 @@ msgid "Note has been saved."
msgstr "Die Notiz wurde gespeichert." msgstr "Die Notiz wurde gespeichert."
msgid "Exits the application." msgid "Exits the application."
msgstr "Schließt das Programm." msgstr "Beendet das Programm."
#, fuzzy
msgid "" msgid ""
"Exports Joplin data to the given path. By default, it will export the " "Exports Joplin data to the given path. By default, it will export the "
"complete database including notebooks, notes, tags and resources." "complete database including notebooks, notes, tags and resources."
msgstr "" msgstr ""
"Exportiert Joplins Dateien zu dem angegebenen Pfad. Standardmäßig wird die " "Exportiert Joplin Dateien in den angegebenen Pfad. Standardmäßig wird die "
"komplette Datenbank inklusive Notizbüchern, Notizen, Markierungen und " "komplette Datenbank inklusive Notizbüchern, Notizen, Markierungen und "
"Anhängen exportiert." "Anhängen exportiert."
#, fuzzy, javascript-format #, javascript-format
msgid "Destination format: %s" msgid "Destination format: %s"
msgstr "Datumsformat" msgstr "Zielformat: %s"
msgid "Exports only the given note." msgid "Exports only the given note."
msgstr "Exportiert nur die angegebene Notiz." msgstr "Exportiert nur die angegebene Notiz."
@ -222,6 +220,8 @@ msgstr "Zeigt die Nutzungsstatistik an."
#, javascript-format #, javascript-format
msgid "For information on how to customise the shortcuts please visit %s" msgid "For information on how to customise the shortcuts please visit %s"
msgstr "" msgstr ""
"Für weitere Informationen über die Anpassung von Tastenkürzel besuche bitte "
"%s"
msgid "Shortcuts are not available in CLI mode." msgid "Shortcuts are not available in CLI mode."
msgstr "Tastenkürzel sind im CLI Modus nicht verfügbar." msgstr "Tastenkürzel sind im CLI Modus nicht verfügbar."
@ -230,11 +230,12 @@ msgid ""
"Type `help [command]` for more information about a command; or type `help " "Type `help [command]` for more information about a command; or type `help "
"all` for the complete usage information." "all` for the complete usage information."
msgstr "" msgstr ""
"Tippe `help [Befehl]` für weitere Informationen über einen Befehl; oder " "Tippe `help [Befehl]` um weitere Informationen über einen Befehl zu erhalten "
"tippe `help all` für die vollständigen Informationen zur Befehlsverwendung." "oder tippe `help all` für die vollständigen Informationen zur "
"Befehlsverwendung."
msgid "The possible commands are:" msgid "The possible commands are:"
msgstr "Mögliche Befehle sind:" msgstr "Mögliche Befehle lauten:"
msgid "" msgid ""
"In any command, a note or notebook can be refered to by title or ID, or " "In any command, a note or notebook can be refered to by title or ID, or "
@ -243,13 +244,13 @@ msgid ""
msgstr "" msgstr ""
"In jedem Befehl können Notizen oder Notizbücher durch ihren Titel oder ihre " "In jedem Befehl können Notizen oder Notizbücher durch ihren Titel oder ihre "
"ID spezifiziert werden, oder durch die Abkürzung `$n` oder `$b` um entweder " "ID spezifiziert werden, oder durch die Abkürzung `$n` oder `$b` um entweder "
"das momentan ausgewählte Notizbuch oder die momentan ausgewählte Notiz zu " "das momentan ausgewählte Notizbuch oder die momentan ausgewählte Notiz "
"wählen. `$c` kann benutzt werden, um auf die momentane Auswahl zu verweisen." "auszuwählen. `$c` kann benutzt werden, um auf die momentane Auswahl zu "
"verweisen."
msgid "To move from one pane to another, press Tab or Shift+Tab." msgid "To move from one pane to another, press Tab or Shift+Tab."
msgstr "" msgstr ""
"Um ein von einem Fenster zu einem anderen zu wechseln, drücke Tab oder Shift" "Um von einem Fenster zu einem anderen zu wechseln, drücke Tab oder Shift+Tab."
"+Tab."
msgid "" msgid ""
"Use the arrows and page up/down to scroll the lists and text areas " "Use the arrows and page up/down to scroll the lists and text areas "
@ -267,19 +268,18 @@ msgstr "Um den Kommandozeilen Modus aufzurufen, drücke \":\""
msgid "To exit command line mode, press ESCAPE" msgid "To exit command line mode, press ESCAPE"
msgstr "Um den Kommandozeilen Modus zu beenden, drücke ESCAPE" msgstr "Um den Kommandozeilen Modus zu beenden, drücke ESCAPE"
#, fuzzy
msgid "" msgid ""
"For the list of keyboard shortcuts and config options, type `help keymap`" "For the list of keyboard shortcuts and config options, type `help keymap`"
msgstr "" msgstr ""
"Um die komplette Liste von verfügbaren Tastenkürzeln anzuzeigen, tippe `help " "Um die komplette Liste aller verfügbaren Tastenkürzeln anzuzeigen, tippe "
"shortcuts` ein" "`help keymap` ein"
msgid "Imports data into Joplin." msgid "Imports data into Joplin."
msgstr "" msgstr "Importiert Daten in Joplin."
#, fuzzy, javascript-format #, javascript-format
msgid "Source format: %s" msgid "Source format: %s"
msgstr "Ungültiger Befehl: %s" msgstr "Quellformat: %s"
msgid "Do not ask for confirmation." msgid "Do not ask for confirmation."
msgstr "Nicht nach einer Bestätigung fragen." msgstr "Nicht nach einer Bestätigung fragen."
@ -458,7 +458,7 @@ msgid "Starting synchronisation..."
msgstr "Starte Synchronisation..." msgstr "Starte Synchronisation..."
msgid "Cancelling... Please wait." msgid "Cancelling... Please wait."
msgstr "Abbrechen... Bitte warten." msgstr "Abbrechen Bitte warten."
msgid "" msgid ""
"<tag-command> can be \"add\", \"remove\" or \"list\" to assign or remove " "<tag-command> can be \"add\", \"remove\" or \"list\" to assign or remove "
@ -520,6 +520,10 @@ msgstr "Standard: %s"
msgid "Possible keys/values:" msgid "Possible keys/values:"
msgstr "Mögliche Werte:" msgstr "Mögliche Werte:"
#, fuzzy
msgid "Type `joplin help` for usage information."
msgstr "Zeigt die Nutzungsstatistik an."
msgid "Fatal error:" msgid "Fatal error:"
msgstr "Schwerwiegender Fehler:" msgstr "Schwerwiegender Fehler:"
@ -576,17 +580,17 @@ msgstr ""
#, javascript-format #, javascript-format
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..." msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr "Exportiere „%s“ ins „%s“ Format. Bitte warten..."
msgid "File"
msgstr "Datei"
msgid "Directory"
msgstr ""
#, javascript-format #, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..." msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr "Importiere „%s“ ins „%s“ Format. Bitte warten…"
msgid "PDF File"
msgstr "PDF-Datei"
msgid "File"
msgstr "Datei"
msgid "New note" msgid "New note"
msgstr "Neue Notiz" msgstr "Neue Notiz"
@ -600,13 +604,15 @@ msgstr "Neues Notizbuch"
msgid "Import" msgid "Import"
msgstr "Importieren" msgstr "Importieren"
#, fuzzy
msgid "Export" msgid "Export"
msgstr "Importieren" msgstr "Exportieren"
msgid "Print"
msgstr "Drucken"
#, javascript-format #, javascript-format
msgid "Hide %s" msgid "Hide %s"
msgstr "" msgstr "%s ausblenden"
msgid "Quit" msgid "Quit"
msgstr "Verlassen" msgstr "Verlassen"
@ -627,10 +633,10 @@ msgid "Search in all the notes"
msgstr "Alle Notizen durchsuchen" msgstr "Alle Notizen durchsuchen"
msgid "View" msgid "View"
msgstr "" msgstr "Ansicht"
msgid "Toggle editor layout" msgid "Toggle editor layout"
msgstr "" msgstr "Editor Layout umschalten"
msgid "Tools" msgid "Tools"
msgstr "Werkzeuge" msgstr "Werkzeuge"
@ -639,7 +645,7 @@ msgid "Synchronisation status"
msgstr "Status der Synchronisation" msgstr "Status der Synchronisation"
msgid "Encryption options" msgid "Encryption options"
msgstr "Verschlüsselungsoptionen" msgstr "Verschlüsselung"
msgid "General Options" msgid "General Options"
msgstr "Allgemeine Einstellungen" msgstr "Allgemeine Einstellungen"
@ -650,8 +656,11 @@ msgstr "Hilfe"
msgid "Website and documentation" msgid "Website and documentation"
msgstr "Webseite und Dokumentation" msgstr "Webseite und Dokumentation"
msgid "Make a donation"
msgstr "Spenden"
msgid "Check for updates..." msgid "Check for updates..."
msgstr "" msgstr "Überprüfe auf Updates..."
msgid "About Joplin" msgid "About Joplin"
msgstr "Über Joplin" msgstr "Über Joplin"
@ -660,12 +669,12 @@ msgstr "Über Joplin"
msgid "%s %s (%s, %s)" msgid "%s %s (%s, %s)"
msgstr "%s %s (%s, %s)" msgstr "%s %s (%s, %s)"
#, fuzzy, javascript-format #, javascript-format
msgid "Open %s" msgid "Open %s"
msgstr "Auf %s: %s" msgstr "Öffne %s"
msgid "Exit" msgid "Exit"
msgstr "" msgstr "Verlassen"
msgid "OK" msgid "OK"
msgstr "OK" msgstr "OK"
@ -673,33 +682,24 @@ msgstr "OK"
msgid "Cancel" msgid "Cancel"
msgstr "Abbrechen" msgstr "Abbrechen"
#, fuzzy, javascript-format msgid "Current version is up-to-date."
msgid "" msgstr "Die aktuelle Version ist up-to-date."
"Release notes:\n"
"\n"
"%s"
msgstr "Notizen löschen?"
msgid "An update is available, do you want to download it now?" msgid "An update is available, do you want to download it now?"
msgstr "" msgstr "Es ist ein Update verfügbar! Soll dies jetzt heruntergeladen werden?"
msgid "Yes" msgid "Yes"
msgstr "" msgstr "Ja"
#, fuzzy
msgid "No" msgid "No"
msgstr "N" msgstr "Nein"
msgid "Current version is up-to-date."
msgstr ""
#, fuzzy
msgid "Check synchronisation configuration" msgid "Check synchronisation configuration"
msgstr "Synchronisation abbrechen" msgstr "Überprüfen der Synchronisationseinstellungen"
#, javascript-format #, javascript-format
msgid "Notes and settings are stored in: %s" msgid "Notes and settings are stored in: %s"
msgstr "Notizen und Einstellungen gespeichert in: %s" msgstr "Notizen und Einstellungen werden gespeichert in: %s"
msgid "Save" msgid "Save"
msgstr "Speichern" msgstr "Speichern"
@ -765,15 +765,14 @@ msgstr ""
"verwendet werden, abhängig davon, wie die jeweiligen Notizen oder " "verwendet werden, abhängig davon, wie die jeweiligen Notizen oder "
"Notizbücher ursprünglich verschlüsselt wurden." "Notizbücher ursprünglich verschlüsselt wurden."
#, fuzzy
msgid "Missing Master Keys" msgid "Missing Master Keys"
msgstr "Hauptschlüssel" msgstr "Fehlender Master-Key"
msgid "" msgid ""
"The master keys with these IDs are used to encrypt some of your items, " "The master keys with these IDs are used to encrypt some of your items, "
"however the application does not currently have access to them. It is likely " "however the application does not currently have access to them. It is likely "
"they will eventually be downloaded via synchronisation." "they will eventually be downloaded via synchronisation."
msgstr "" msgstr "Die Master-Keas dieser IDs werden für die Verschlüsselung einiger ..."
msgid "Status" msgid "Status"
msgstr "Status" msgstr "Status"
@ -788,7 +787,7 @@ msgstr "Zurück"
msgid "" msgid ""
"New notebook \"%s\" will be created and file \"%s\" will be imported into it" "New notebook \"%s\" will be created and file \"%s\" will be imported into it"
msgstr "" msgstr ""
"Neues Notizbuch \"%s\" wird erstellt und die Datei \"%s\" wird hinein " "Neues Notizbuch \"%s\" wird erstellt und die Datei \"%s\" wird dort hinein "
"importiert" "importiert"
msgid "Please create a notebook first." msgid "Please create a notebook first."
@ -807,7 +806,7 @@ msgid "Separate each tag by a comma."
msgstr "Trenne jede Markierung mit einem Komma." msgstr "Trenne jede Markierung mit einem Komma."
msgid "Rename notebook:" msgid "Rename notebook:"
msgstr "Benne Notizbuch um:" msgstr "Notizbuch umbenennen:"
msgid "Set alarm:" msgid "Set alarm:"
msgstr "Alarm erstellen:" msgstr "Alarm erstellen:"
@ -854,11 +853,10 @@ msgstr ""
"(+) Knopf drückst." "(+) Knopf drückst."
msgid "Open..." msgid "Open..."
msgstr "" msgstr "Öffne..."
#, fuzzy
msgid "Save as..." msgid "Save as..."
msgstr "Änderungen speichern" msgstr "Sichern unter..."
#, javascript-format #, javascript-format
msgid "Unsupported link or message: %s" msgid "Unsupported link or message: %s"
@ -878,18 +876,18 @@ msgid ""
"This note has no content. Click on \"%s\" to toggle the editor and edit the " "This note has no content. Click on \"%s\" to toggle the editor and edit the "
"note." "note."
msgstr "" msgstr ""
"Diese Notiz hat keinen Inhalt. Klicke auf „%s“ um den Editor zu aktivieren "
"und die Notiz zu bearbeiten."
#, fuzzy
msgid "to-do" msgid "to-do"
msgstr "Neues To-Do" msgstr "To-Do"
#, fuzzy
msgid "note" msgid "note"
msgstr "Neue Notiz" msgstr "Notiz"
#, fuzzy, javascript-format #, javascript-format
msgid "Creating new %s..." msgid "Creating new %s..."
msgstr "Importiere Notizen..." msgstr "Erstelle neue %s..."
msgid "Refresh" msgid "Refresh"
msgstr "Aktualisieren" msgstr "Aktualisieren"
@ -942,9 +940,8 @@ msgstr "Unbekanntes Argument: %s"
msgid "File system" msgid "File system"
msgstr "Dateisystem" msgstr "Dateisystem"
#, fuzzy
msgid "Nextcloud" msgid "Nextcloud"
msgstr "Nextcloud (Beta)" msgstr "Nextcloud"
msgid "OneDrive" msgid "OneDrive"
msgstr "OneDrive" msgstr "OneDrive"
@ -952,9 +949,8 @@ msgstr "OneDrive"
msgid "OneDrive Dev (For testing only)" msgid "OneDrive Dev (For testing only)"
msgstr "OneDrive Dev (Nur für Tests)" msgstr "OneDrive Dev (Nur für Tests)"
#, fuzzy
msgid "WebDAV" msgid "WebDAV"
msgstr "Nextcloud WebDAV URL" msgstr "WebDAV"
#, javascript-format #, javascript-format
msgid "Unknown log level: %s" msgid "Unknown log level: %s"
@ -1018,8 +1014,8 @@ msgstr "Remote Objekte gelöscht: %d."
msgid "Fetched items: %d/%d." msgid "Fetched items: %d/%d."
msgstr "Geladene Objekte: %d/%d." msgstr "Geladene Objekte: %d/%d."
#, javascript-format #, fuzzy, javascript-format
msgid "State: \"%s\"." msgid "State: %s."
msgstr "Status: \"%s\"." msgstr "Status: \"%s\"."
msgid "Cancelling..." msgid "Cancelling..."
@ -1029,6 +1025,16 @@ msgstr "Abbrechen..."
msgid "Completed: %s" msgid "Completed: %s"
msgstr "Abgeschlossen: %s" msgstr "Abgeschlossen: %s"
#, javascript-format
msgid "Last error: %s"
msgstr "Letzte Fehlermeldung: %s"
msgid "Idle"
msgstr "wartend"
msgid "In progress"
msgstr "In Bearbeitung"
#, javascript-format #, javascript-format
msgid "Synchronisation is already in progress. State: %s" msgid "Synchronisation is already in progress. State: %s"
msgstr "Synchronisation ist bereits im Gange. Status: %s" msgstr "Synchronisation ist bereits im Gange. Status: %s"
@ -1037,7 +1043,7 @@ msgid "Encrypted"
msgstr "Verschlüsselt" msgstr "Verschlüsselt"
msgid "Encrypted items cannot be modified" msgid "Encrypted items cannot be modified"
msgstr "Verschlüsselte Objekte können nicht verändert werden." msgstr "Verschlüsselte Objekte können nicht verändert werden"
msgid "Conflicts" msgid "Conflicts"
msgstr "Konflikte" msgstr "Konflikte"
@ -1094,49 +1100,45 @@ msgstr "Hell"
msgid "Dark" msgid "Dark"
msgstr "Dunkel" msgstr "Dunkel"
#, fuzzy
msgid "Uncompleted to-dos on top" msgid "Uncompleted to-dos on top"
msgstr "Zeige unvollständige To-Dos oben in der Liste" msgstr "Zeige unvollständige To-Dos an oberster Stelle"
msgid "Sort notes by" msgid "Sort notes by"
msgstr "" msgstr "Sortiere Notizen nach"
#, fuzzy
msgid "Reverse sort order" msgid "Reverse sort order"
msgstr "Dreht die Sortierreihenfolge um." msgstr "Sortierreihenfolge umdrehen"
msgid "Save geo-location with notes" msgid "Save geo-location with notes"
msgstr "Momentanen Standort zusammen mit Notizen speichern" msgstr "Momentanen Standort zusammen mit Notizen speichern"
#, fuzzy
msgid "When creating a new to-do:" msgid "When creating a new to-do:"
msgstr "Erstellt ein neues To-Do." msgstr "Wenn eine neue To-Do erstellt wird:"
#, fuzzy
msgid "Focus title" msgid "Focus title"
msgstr "Notiz Titel:" msgstr "Fokussiere Titel"
msgid "Focus body" msgid "Focus body"
msgstr "" msgstr "Fokussiere Inhalt"
#, fuzzy
msgid "When creating a new note:" msgid "When creating a new note:"
msgstr "Erstellt eine neue Notiz." msgstr "Wenn eine neue Notiz erstellt wird:"
msgid "Show tray icon" msgid "Show tray icon"
msgstr "" msgstr "Zeige Tray Icon"
#, fuzzy
msgid "Global zoom percentage" msgid "Global zoom percentage"
msgstr "Einstellen des Anwendungszooms" msgstr "Zoomstufe der Benutzeroberfläche"
msgid "Editor font family" msgid "Editor font family"
msgstr "" msgstr "Editor Schriftenfamilie"
msgid "" msgid ""
"The font name will not be checked. If incorrect or empty, it will default to " "The font name will not be checked. If incorrect or empty, it will default to "
"a generic monospace font." "a generic monospace font."
msgstr "" msgstr ""
"Der Name der Schrift wird nicht überprüft. Ist dieser inkorrekt oder leer "
"wird eine generische Monospace Schrift verwendet."
msgid "Automatically update the application" msgid "Automatically update the application"
msgstr "Die Applikation automatisch aktualisieren" msgstr "Die Applikation automatisch aktualisieren"
@ -1189,57 +1191,56 @@ msgstr "Nextcloud Benutzername"
msgid "Nextcloud password" msgid "Nextcloud password"
msgstr "Nextcloud Passwort" msgstr "Nextcloud Passwort"
#, fuzzy
msgid "WebDAV URL" msgid "WebDAV URL"
msgstr "Nextcloud WebDAV URL" msgstr "WebDAV URL"
#, fuzzy
msgid "WebDAV username" msgid "WebDAV username"
msgstr "Nextcloud Benutzername" msgstr "WebDAV Benutzername"
#, fuzzy
msgid "WebDAV password" msgid "WebDAV password"
msgstr "Setze ein Passwort" msgstr "WebDAV Passwort"
#, javascript-format #, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s." msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Ungültiger Optionswert: \"%s\". Mögliche Werte sind: %s." msgstr "Ungültiger Optionswert: \"%s\". Mögliche Werte sind: %s."
#, fuzzy
msgid "Joplin Export File" msgid "Joplin Export File"
msgstr "Evernote Export Dateien" msgstr "Joplin Export Datei"
msgid "Markdown" msgid "Markdown"
msgstr "" msgstr "Markdown"
msgid "Joplin Export Directory" msgid "Joplin Export Directory"
msgstr "" msgstr "Joplin Export Verzeichnis"
#, fuzzy
msgid "Evernote Export File" msgid "Evernote Export File"
msgstr "Evernote Export Dateien" msgstr "Evernote Export Datei"
msgid "Directory"
msgstr "Verzeichnis"
#, javascript-format #, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\"" msgid "Cannot load \"%s\" module for format \"%s\""
msgstr "" msgstr "Das Modul „%s“ für das Format „%s“ kann nicht geladen werden"
#, javascript-format #, javascript-format
msgid "Please specify import format for %s" msgid "Please specify import format for %s"
msgstr "" msgstr "Bitte das Exportformat für %s angeben"
#, javascript-format #, javascript-format
msgid "" msgid ""
"This item is currently encrypted: %s \"%s\". Please wait for all items to be " "This item is currently encrypted: %s \"%s\". Please wait for all items to be "
"decrypted and try again." "decrypted and try again."
msgstr "" msgstr ""
"Dieses Objekt ist zur Zeit verschlüsselt: %s „%s“. Bitte warten bis alle "
"Objekte entschlüsselt wurden und versuche es dann erneut."
msgid "There is no data to export." msgid "There is no data to export."
msgstr "" msgstr "Keine Daten für den Export vorhanden."
#, fuzzy
msgid "Please specify the notebook where the notes should be imported to." msgid "Please specify the notebook where the notes should be imported to."
msgstr "" msgstr ""
"Bitte wähle aus, wohin der Synchronisations Status exportiert werden soll" "Bitte wähle aus, wohin der Synchronisations-Status exportiert werden soll."
msgid "Items that cannot be synchronised" msgid "Items that cannot be synchronised"
msgstr "Objekte können nicht synchronisiert werden" msgstr "Objekte können nicht synchronisiert werden"
@ -1329,6 +1330,15 @@ msgstr "Bestätigen"
msgid "Cancel synchronisation" msgid "Cancel synchronisation"
msgstr "Synchronisation abbrechen" msgstr "Synchronisation abbrechen"
msgid "New tags:"
msgstr ""
msgid "Type new tags or select from list"
msgstr ""
msgid "Joplin website"
msgstr "Website von Joplin"
#, javascript-format #, javascript-format
msgid "Master Key %s" msgid "Master Key %s"
msgstr "Hauptschlüssel %s" msgstr "Hauptschlüssel %s"
@ -1354,10 +1364,10 @@ msgid "Edit notebook"
msgstr "Notizbuch bearbeiten" msgstr "Notizbuch bearbeiten"
msgid "Show all" msgid "Show all"
msgstr "" msgstr "Zeige Alles"
msgid "Errors only" msgid "Errors only"
msgstr "" msgstr "Nur Fehler"
msgid "This note has been modified:" msgid "This note has been modified:"
msgstr "Diese Notiz wurde verändert:" msgstr "Diese Notiz wurde verändert:"
@ -1415,6 +1425,15 @@ msgstr ""
msgid "Welcome" msgid "Welcome"
msgstr "Willkommen" msgstr "Willkommen"
#~ msgid ""
#~ "Release notes:\n"
#~ "\n"
#~ "%s"
#~ msgstr ""
#~ "Versionshinweise:\n"
#~ "\n"
#~ "%s"
#~ msgid "Imports an Evernote notebook file (.enex file)." #~ msgid "Imports an Evernote notebook file (.enex file)."
#~ msgstr "Importiert eine Evernote Notizbuch-Datei (.enex Datei)." #~ msgstr "Importiert eine Evernote Notizbuch-Datei (.enex Datei)."

View File

@ -449,6 +449,9 @@ msgstr ""
msgid "Possible keys/values:" msgid "Possible keys/values:"
msgstr "" msgstr ""
msgid "Type `joplin help` for usage information."
msgstr ""
msgid "Fatal error:" msgid "Fatal error:"
msgstr "" msgstr ""
@ -490,16 +493,16 @@ msgstr ""
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..." msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr ""
msgid "File"
msgstr ""
msgid "Directory"
msgstr ""
#, javascript-format #, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..." msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr ""
msgid "PDF File"
msgstr ""
msgid "File"
msgstr ""
msgid "New note" msgid "New note"
msgstr "" msgstr ""
@ -515,6 +518,9 @@ msgstr ""
msgid "Export" msgid "Export"
msgstr "" msgstr ""
msgid "Print"
msgstr ""
#, javascript-format #, javascript-format
msgid "Hide %s" msgid "Hide %s"
msgstr "" msgstr ""
@ -561,6 +567,9 @@ msgstr ""
msgid "Website and documentation" msgid "Website and documentation"
msgstr "" msgstr ""
msgid "Make a donation"
msgstr ""
msgid "Check for updates..." msgid "Check for updates..."
msgstr "" msgstr ""
@ -584,11 +593,7 @@ msgstr ""
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr ""
#, javascript-format msgid "Current version is up-to-date."
msgid ""
"Release notes:\n"
"\n"
"%s"
msgstr "" msgstr ""
msgid "An update is available, do you want to download it now?" msgid "An update is available, do you want to download it now?"
@ -600,9 +605,6 @@ msgstr ""
msgid "No" msgid "No"
msgstr "" msgstr ""
msgid "Current version is up-to-date."
msgstr ""
msgid "Check synchronisation configuration" msgid "Check synchronisation configuration"
msgstr "" msgstr ""
@ -895,7 +897,7 @@ msgid "Fetched items: %d/%d."
msgstr "" msgstr ""
#, javascript-format #, javascript-format
msgid "State: \"%s\"." msgid "State: %s."
msgstr "" msgstr ""
msgid "Cancelling..." msgid "Cancelling..."
@ -905,6 +907,16 @@ msgstr ""
msgid "Completed: %s" msgid "Completed: %s"
msgstr "" msgstr ""
#, javascript-format
msgid "Last error: %s"
msgstr ""
msgid "Idle"
msgstr ""
msgid "In progress"
msgstr ""
#, javascript-format #, javascript-format
msgid "Synchronisation is already in progress. State: %s" msgid "Synchronisation is already in progress. State: %s"
msgstr "" msgstr ""
@ -1075,6 +1087,9 @@ msgstr ""
msgid "Evernote Export File" msgid "Evernote Export File"
msgstr "" msgstr ""
msgid "Directory"
msgstr ""
#, javascript-format #, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\"" msgid "Cannot load \"%s\" module for format \"%s\""
msgstr "" msgstr ""
@ -1178,6 +1193,15 @@ msgstr ""
msgid "Cancel synchronisation" msgid "Cancel synchronisation"
msgstr "" msgstr ""
msgid "New tags:"
msgstr ""
msgid "Type new tags or select from list"
msgstr ""
msgid "Joplin website"
msgstr ""
#, javascript-format #, javascript-format
msgid "Master Key %s" msgid "Master Key %s"
msgstr "" msgstr ""

View File

@ -188,7 +188,6 @@ msgstr "La nota ha sido guardada."
msgid "Exits the application." msgid "Exits the application."
msgstr "Sale de la aplicación." msgstr "Sale de la aplicación."
#, fuzzy
msgid "" msgid ""
"Exports Joplin data to the given path. By default, it will export the " "Exports Joplin data to the given path. By default, it will export the "
"complete database including notebooks, notes, tags and resources." "complete database including notebooks, notes, tags and resources."
@ -196,9 +195,9 @@ msgstr ""
"Exporta datos de Joplin al directorio indicado. Por defecto, se exportará la " "Exporta datos de Joplin al directorio indicado. Por defecto, se exportará la "
"base de datos completa incluyendo libretas, notas, etiquetas y recursos." "base de datos completa incluyendo libretas, notas, etiquetas y recursos."
#, fuzzy, javascript-format #, javascript-format
msgid "Destination format: %s" msgid "Destination format: %s"
msgstr "Formato de fecha" msgstr "Formato de destino: %s"
msgid "Exports only the given note." msgid "Exports only the given note."
msgstr "Exporta únicamente la nota indicada." msgstr "Exporta únicamente la nota indicada."
@ -266,11 +265,11 @@ msgstr ""
"Para una lista de los atajos de teclado disponibles, escriba `help keymap`" "Para una lista de los atajos de teclado disponibles, escriba `help keymap`"
msgid "Imports data into Joplin." msgid "Imports data into Joplin."
msgstr "" msgstr "Importa los datos en Joplin."
#, fuzzy, javascript-format #, javascript-format
msgid "Source format: %s" msgid "Source format: %s"
msgstr "El comando no existe: %s" msgstr "Formato de origen: %s"
msgid "Do not ask for confirmation." msgid "Do not ask for confirmation."
msgstr "No requiere confirmación." msgstr "No requiere confirmación."
@ -506,6 +505,9 @@ msgstr "Por defecto: %s"
msgid "Possible keys/values:" msgid "Possible keys/values:"
msgstr "Claves/valores posbiles:" msgstr "Claves/valores posbiles:"
msgid "Type `joplin help` for usage information."
msgstr "Escriba `joplin help` para mostrar información de uso."
msgid "Fatal error:" msgid "Fatal error:"
msgstr "Error fatal:" msgstr "Error fatal:"
@ -563,17 +565,17 @@ msgstr ""
#, javascript-format #, javascript-format
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..." msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr "Exportando el formato de \"%s\" a \"%s\". Por favor espere..."
msgid "File"
msgstr "Archivo"
msgid "Directory"
msgstr ""
#, javascript-format #, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..." msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr "Importando el formato de \"%s\" a \"%s\". Por favor espere..."
msgid "PDF File"
msgstr "Archivo PDF"
msgid "File"
msgstr "Archivo"
msgid "New note" msgid "New note"
msgstr "Nueva nota" msgstr "Nueva nota"
@ -587,9 +589,11 @@ msgstr "Nueva libreta"
msgid "Import" msgid "Import"
msgstr "Importar" msgstr "Importar"
#, fuzzy
msgid "Export" msgid "Export"
msgstr "Importar" msgstr "Exportar"
msgid "Print"
msgstr "Imprimir"
#, javascript-format #, javascript-format
msgid "Hide %s" msgid "Hide %s"
@ -637,6 +641,9 @@ msgstr "Ayuda"
msgid "Website and documentation" msgid "Website and documentation"
msgstr "Sitio web y documentación" msgstr "Sitio web y documentación"
msgid "Make a donation"
msgstr "Hacer una donación"
msgid "Check for updates..." msgid "Check for updates..."
msgstr "Comprobar actualizaciones..." msgstr "Comprobar actualizaciones..."
@ -660,15 +667,8 @@ msgstr "OK"
msgid "Cancel" msgid "Cancel"
msgstr "Cancelar" msgstr "Cancelar"
#, javascript-format msgid "Current version is up-to-date."
msgid "" msgstr "La versión actual está actualizada."
"Release notes:\n"
"\n"
"%s"
msgstr ""
"Notas de la versión:\n"
"\n"
"%s"
msgid "An update is available, do you want to download it now?" msgid "An update is available, do you want to download it now?"
msgstr "Hay disponible una actualización. ¿Quiere descargarla ahora?" msgstr "Hay disponible una actualización. ¿Quiere descargarla ahora?"
@ -679,9 +679,6 @@ msgstr "Sí"
msgid "No" msgid "No"
msgstr "No" msgstr "No"
msgid "Current version is up-to-date."
msgstr "La versión actual está actualizada."
msgid "Check synchronisation configuration" msgid "Check synchronisation configuration"
msgstr "Comprobar sincronización" msgstr "Comprobar sincronización"
@ -859,6 +856,8 @@ msgid ""
"This note has no content. Click on \"%s\" to toggle the editor and edit the " "This note has no content. Click on \"%s\" to toggle the editor and edit the "
"note." "note."
msgstr "" msgstr ""
"Esta nota no tiene contenido. Pulse en \"%s\" para cambiar al editor y "
"editar la nota."
msgid "to-do" msgid "to-do"
msgstr "lista de tareas" msgstr "lista de tareas"
@ -994,8 +993,8 @@ msgstr "Elementos remotos borrados: %d."
msgid "Fetched items: %d/%d." msgid "Fetched items: %d/%d."
msgstr "Elementos obtenidos: %d/%d." msgstr "Elementos obtenidos: %d/%d."
#, javascript-format #, fuzzy, javascript-format
msgid "State: \"%s\"." msgid "State: %s."
msgstr "Estado: «%s»." msgstr "Estado: «%s»."
msgid "Cancelling..." msgid "Cancelling..."
@ -1005,6 +1004,16 @@ msgstr "Cancelando..."
msgid "Completed: %s" msgid "Completed: %s"
msgstr "Completado: %s" msgstr "Completado: %s"
#, javascript-format
msgid "Last error: %s"
msgstr "Último error: %s"
msgid "Idle"
msgstr "Espera"
msgid "In progress"
msgstr "En progreso"
#, javascript-format #, javascript-format
msgid "Synchronisation is already in progress. State: %s" msgid "Synchronisation is already in progress. State: %s"
msgstr "La sincronización ya está en progreso. Estado: %s" msgstr "La sincronización ya está en progreso. Estado: %s"
@ -1069,16 +1078,14 @@ msgstr "Claro"
msgid "Dark" msgid "Dark"
msgstr "Oscuro" msgstr "Oscuro"
#, fuzzy
msgid "Uncompleted to-dos on top" msgid "Uncompleted to-dos on top"
msgstr "Mostrar tareas incompletas al inicio de las listas" msgstr "Mostrar tareas incompletas al inicio de las listas"
msgid "Sort notes by" msgid "Sort notes by"
msgstr "" msgstr "Ordenar notas por"
#, fuzzy
msgid "Reverse sort order" msgid "Reverse sort order"
msgstr "Invierte el orden." msgstr "Invierte el orden"
msgid "Save geo-location with notes" msgid "Save geo-location with notes"
msgstr "Guardar geolocalización en las notas" msgstr "Guardar geolocalización en las notas"
@ -1098,17 +1105,18 @@ msgstr "Cuando se crear una nota nueva:"
msgid "Show tray icon" msgid "Show tray icon"
msgstr "Mostrar icono en la bandeja" msgstr "Mostrar icono en la bandeja"
#, fuzzy
msgid "Global zoom percentage" msgid "Global zoom percentage"
msgstr "Establecer el porcentaje de aumento de la aplicación" msgstr "Establecer el porcentaje de aumento de la aplicación"
msgid "Editor font family" msgid "Editor font family"
msgstr "" msgstr "Fuente del editor"
msgid "" msgid ""
"The font name will not be checked. If incorrect or empty, it will default to " "The font name will not be checked. If incorrect or empty, it will default to "
"a generic monospace font." "a generic monospace font."
msgstr "" msgstr ""
"El nombre de la fuente no se comprobado. Si es incorrecto o está vacío, se "
"utilizará una fuente genérica monoespaciada."
msgid "Automatically update the application" msgid "Automatically update the application"
msgstr "Actualizar la aplicación automáticamente" msgstr "Actualizar la aplicación automáticamente"
@ -1174,40 +1182,42 @@ msgstr "Contraseña de WebDAV"
msgid "Invalid option value: \"%s\". Possible values are: %s." msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Opción inválida: «%s». Los valores posibles son: %s." msgstr "Opción inválida: «%s». Los valores posibles son: %s."
#, fuzzy
msgid "Joplin Export File" msgid "Joplin Export File"
msgstr "Archivos exportados de Evernote" msgstr "Archivo de exportación de Joplin"
msgid "Markdown" msgid "Markdown"
msgstr "" msgstr "Markdown"
msgid "Joplin Export Directory" msgid "Joplin Export Directory"
msgstr "" msgstr "Directorio para exportar de Joplin"
#, fuzzy
msgid "Evernote Export File" msgid "Evernote Export File"
msgstr "Archivos exportados de Evernote" msgstr "Archivo exportado de Evernote"
msgid "Directory"
msgstr "Directorio"
#, javascript-format #, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\"" msgid "Cannot load \"%s\" module for format \"%s\""
msgstr "" msgstr "No se puede cargar el módulo \"%s\" para el formato \"%s\""
#, javascript-format #, javascript-format
msgid "Please specify import format for %s" msgid "Please specify import format for %s"
msgstr "" msgstr "Por favor especifique el formato para importar de %s"
#, javascript-format #, javascript-format
msgid "" msgid ""
"This item is currently encrypted: %s \"%s\". Please wait for all items to be " "This item is currently encrypted: %s \"%s\". Please wait for all items to be "
"decrypted and try again." "decrypted and try again."
msgstr "" msgstr ""
"El elemento se encuentra cifrado: %s \"%s\". Por favor espere a que todos "
"los elementos estén descifrados y pruebe de nuevo."
msgid "There is no data to export." msgid "There is no data to export."
msgstr "" msgstr "No hay datos para exportar."
#, fuzzy
msgid "Please specify the notebook where the notes should be imported to." msgid "Please specify the notebook where the notes should be imported to."
msgstr "Seleccione a dónde se debería exportar el estado de sincronización" msgstr "Por favor especifique la libreta donde las notas deben ser importadas."
msgid "Items that cannot be synchronised" msgid "Items that cannot be synchronised"
msgstr "Elementos que no se pueden sincronizar" msgstr "Elementos que no se pueden sincronizar"
@ -1295,6 +1305,15 @@ msgstr "Confirmar"
msgid "Cancel synchronisation" msgid "Cancel synchronisation"
msgstr "Cancelar sincronización" msgstr "Cancelar sincronización"
msgid "New tags:"
msgstr ""
msgid "Type new tags or select from list"
msgstr ""
msgid "Joplin website"
msgstr "Sitio web de Joplin"
#, javascript-format #, javascript-format
msgid "Master Key %s" msgid "Master Key %s"
msgstr "Clave maestra %s" msgstr "Clave maestra %s"
@ -1379,6 +1398,15 @@ msgstr ""
msgid "Welcome" msgid "Welcome"
msgstr "Bienvenido" msgstr "Bienvenido"
#~ msgid ""
#~ "Release notes:\n"
#~ "\n"
#~ "%s"
#~ msgstr ""
#~ "Notas de la versión:\n"
#~ "\n"
#~ "%s"
#~ msgid "Imports an Evernote notebook file (.enex file)." #~ msgid "Imports an Evernote notebook file (.enex file)."
#~ msgstr "Importar una libreta de Evernote (archivo .enex)." #~ msgstr "Importar una libreta de Evernote (archivo .enex)."

View File

@ -503,6 +503,10 @@ msgstr "Lehenetsia: %s"
msgid "Possible keys/values:" msgid "Possible keys/values:"
msgstr "Litezkeen balioak:" msgstr "Litezkeen balioak:"
#, fuzzy
msgid "Type `joplin help` for usage information."
msgstr "Erakutsi erabilera datuak."
msgid "Fatal error:" msgid "Fatal error:"
msgstr "Aio! Agur! :_( " msgstr "Aio! Agur! :_( "
@ -560,16 +564,17 @@ msgstr ""
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..." msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr ""
msgid "File"
msgstr "Fitxategia"
msgid "Directory"
msgstr ""
#, javascript-format #, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..." msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr ""
#, fuzzy
msgid "PDF File"
msgstr "Fitxategia"
msgid "File"
msgstr "Fitxategia"
msgid "New note" msgid "New note"
msgstr "Ohar berria" msgstr "Ohar berria"
@ -586,6 +591,9 @@ msgstr "Inportatu"
msgid "Export" msgid "Export"
msgstr "Inportatu" msgstr "Inportatu"
msgid "Print"
msgstr ""
#, javascript-format #, javascript-format
msgid "Hide %s" msgid "Hide %s"
msgstr "" msgstr ""
@ -632,6 +640,10 @@ msgstr "Laguntza"
msgid "Website and documentation" msgid "Website and documentation"
msgstr "Web orria eta dokumentazioa (en)" msgstr "Web orria eta dokumentazioa (en)"
#, fuzzy
msgid "Make a donation"
msgstr "Web orria eta dokumentazioa (en)"
msgid "Check for updates..." msgid "Check for updates..."
msgstr "" msgstr ""
@ -655,12 +667,8 @@ msgstr "OK"
msgid "Cancel" msgid "Cancel"
msgstr "Utzi" msgstr "Utzi"
#, fuzzy, javascript-format msgid "Current version is up-to-date."
msgid "" msgstr ""
"Release notes:\n"
"\n"
"%s"
msgstr "Oharrak ezabatu?"
msgid "An update is available, do you want to download it now?" msgid "An update is available, do you want to download it now?"
msgstr "" msgstr ""
@ -672,9 +680,6 @@ msgstr ""
msgid "No" msgid "No"
msgstr "E" msgstr "E"
msgid "Current version is up-to-date."
msgstr ""
#, fuzzy #, fuzzy
msgid "Check synchronisation configuration" msgid "Check synchronisation configuration"
msgstr "Sinkronizazioa utzi" msgstr "Sinkronizazioa utzi"
@ -995,8 +1000,8 @@ msgstr "Urruneko itemak ezabatuta: %d."
msgid "Fetched items: %d/%d." msgid "Fetched items: %d/%d."
msgstr "Itemak eskuratuta: %d%d." msgstr "Itemak eskuratuta: %d%d."
#, javascript-format #, fuzzy, javascript-format
msgid "State: \"%s\"." msgid "State: %s."
msgstr "Egoera: \"%s\"." msgstr "Egoera: \"%s\"."
msgid "Cancelling..." msgid "Cancelling..."
@ -1006,6 +1011,16 @@ msgstr "Bertan behera uzten..."
msgid "Completed: %s" msgid "Completed: %s"
msgstr "Osatuta: %s" msgstr "Osatuta: %s"
#, fuzzy, javascript-format
msgid "Last error: %s"
msgstr "Aio! Agur! :_( "
msgid "Idle"
msgstr ""
msgid "In progress"
msgstr ""
#, javascript-format #, javascript-format
msgid "Synchronisation is already in progress. State: %s" msgid "Synchronisation is already in progress. State: %s"
msgstr "Sinkronizazioa hasita dago. Egoera: %s" msgstr "Sinkronizazioa hasita dago. Egoera: %s"
@ -1193,6 +1208,9 @@ msgstr ""
msgid "Evernote Export File" msgid "Evernote Export File"
msgstr "Evernotetik esportatutako fitxategiak" msgstr "Evernotetik esportatutako fitxategiak"
msgid "Directory"
msgstr ""
#, javascript-format #, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\"" msgid "Cannot load \"%s\" module for format \"%s\""
msgstr "" msgstr ""
@ -1299,6 +1317,15 @@ msgstr "Baieztatu"
msgid "Cancel synchronisation" msgid "Cancel synchronisation"
msgstr "Sinkronizazioa utzi" msgstr "Sinkronizazioa utzi"
msgid "New tags:"
msgstr ""
msgid "Type new tags or select from list"
msgstr ""
msgid "Joplin website"
msgstr ""
#, javascript-format #, javascript-format
msgid "Master Key %s" msgid "Master Key %s"
msgstr "Pasahitz Nagusia %s" msgstr "Pasahitz Nagusia %s"
@ -1382,6 +1409,13 @@ msgstr "Oraindik ez duzu koadernorik. Sortu bat (+) botoian sakatuta."
msgid "Welcome" msgid "Welcome"
msgstr "Ongi etorri!" msgstr "Ongi etorri!"
#, fuzzy
#~ msgid ""
#~ "Release notes:\n"
#~ "\n"
#~ "%s"
#~ msgstr "Oharrak ezabatu?"
#~ msgid "Imports an Evernote notebook file (.enex file)." #~ msgid "Imports an Evernote notebook file (.enex file)."
#~ msgstr "Inportatu Evernote koaderno fitxategia (.enex fitxategia)." #~ msgstr "Inportatu Evernote koaderno fitxategia (.enex fitxategia)."

View File

@ -504,6 +504,9 @@ msgstr "Défaut : %s"
msgid "Possible keys/values:" msgid "Possible keys/values:"
msgstr "Clefs/Valeurs possibles :" msgstr "Clefs/Valeurs possibles :"
msgid "Type `joplin help` for usage information."
msgstr "Tapez `Joplin help` pour afficher l'aide."
msgid "Fatal error:" msgid "Fatal error:"
msgstr "Erreur fatale :" msgstr "Erreur fatale :"
@ -561,16 +564,16 @@ msgstr ""
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..." msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr "Exporter vers \"%s\" au format \"%s\". Veuillez patienter..." msgstr "Exporter vers \"%s\" au format \"%s\". Veuillez patienter..."
msgid "File"
msgstr "Fichier"
msgid "Directory"
msgstr "Dossier"
#, javascript-format #, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..." msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr "Importer depuis \"%s\" au format \"%s\". Veuillez patienter..." msgstr "Importer depuis \"%s\" au format \"%s\". Veuillez patienter..."
msgid "PDF File"
msgstr "Fichier PDF"
msgid "File"
msgstr "Fichier"
msgid "New note" msgid "New note"
msgstr "Nouvelle note" msgstr "Nouvelle note"
@ -586,6 +589,9 @@ msgstr "Importer"
msgid "Export" msgid "Export"
msgstr "Exporter" msgstr "Exporter"
msgid "Print"
msgstr "Imprimer"
#, javascript-format #, javascript-format
msgid "Hide %s" msgid "Hide %s"
msgstr "Cacher %s" msgstr "Cacher %s"
@ -632,6 +638,9 @@ msgstr "Aide"
msgid "Website and documentation" msgid "Website and documentation"
msgstr "Documentation en ligne" msgstr "Documentation en ligne"
msgid "Make a donation"
msgstr "Faire un don"
msgid "Check for updates..." msgid "Check for updates..."
msgstr "Vérifier les mises à jour..." msgstr "Vérifier les mises à jour..."
@ -655,15 +664,8 @@ msgstr "OK"
msgid "Cancel" msgid "Cancel"
msgstr "Annuler" msgstr "Annuler"
#, javascript-format msgid "Current version is up-to-date."
msgid "" msgstr "La version actuelle est à jour."
"Release notes:\n"
"\n"
"%s"
msgstr ""
"Notes de version :\n"
"\n"
"%s"
msgid "An update is available, do you want to download it now?" msgid "An update is available, do you want to download it now?"
msgstr "" msgstr ""
@ -675,9 +677,6 @@ msgstr "Oui"
msgid "No" msgid "No"
msgstr "Non" msgstr "Non"
msgid "Current version is up-to-date."
msgstr "La version actuelle est à jour."
msgid "Check synchronisation configuration" msgid "Check synchronisation configuration"
msgstr "Vérifier config synchronisation" msgstr "Vérifier config synchronisation"
@ -1001,8 +1000,8 @@ msgid "Fetched items: %d/%d."
msgstr "Téléchargés : %d/%d." msgstr "Téléchargés : %d/%d."
#, javascript-format #, javascript-format
msgid "State: \"%s\"." msgid "State: %s."
msgstr "État : \"%s\"." msgstr "État : %s."
msgid "Cancelling..." msgid "Cancelling..."
msgstr "Annulation..." msgstr "Annulation..."
@ -1011,6 +1010,16 @@ msgstr "Annulation..."
msgid "Completed: %s" msgid "Completed: %s"
msgstr "Terminé : %s" msgstr "Terminé : %s"
#, javascript-format
msgid "Last error: %s"
msgstr "Dernière erreur : %s"
msgid "Idle"
msgstr "Arrêté"
msgid "In progress"
msgstr "En cours"
#, javascript-format #, javascript-format
msgid "Synchronisation is already in progress. State: %s" msgid "Synchronisation is already in progress. State: %s"
msgstr "La synchronisation est déjà en cours. État : %s" msgstr "La synchronisation est déjà en cours. État : %s"
@ -1081,7 +1090,7 @@ msgid "Sort notes by"
msgstr "Trier les notes par" msgstr "Trier les notes par"
msgid "Reverse sort order" msgid "Reverse sort order"
msgstr "Inverser l'ordre." msgstr "Inverser l'ordre"
msgid "Save geo-location with notes" msgid "Save geo-location with notes"
msgstr "Enregistrer l'emplacement avec les notes" msgstr "Enregistrer l'emplacement avec les notes"
@ -1190,6 +1199,9 @@ msgstr "Dossier d'export Joplin"
msgid "Evernote Export File" msgid "Evernote Export File"
msgstr "Fichiers d'export Evernote" msgstr "Fichiers d'export Evernote"
msgid "Directory"
msgstr "Dossier"
#, javascript-format #, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\"" msgid "Cannot load \"%s\" module for format \"%s\""
msgstr "Impossible de charger module \"%s\" pour le format \"%s\"" msgstr "Impossible de charger module \"%s\" pour le format \"%s\""
@ -1300,6 +1312,15 @@ msgstr "Confirmer"
msgid "Cancel synchronisation" msgid "Cancel synchronisation"
msgstr "Annuler synchronisation" msgstr "Annuler synchronisation"
msgid "New tags:"
msgstr "Nouvelles étiquettes :"
msgid "Type new tags or select from list"
msgstr "Entrez de nouvelles étiquettes ou sélectionnez de la liste"
msgid "Joplin website"
msgstr "Site web de Joplin"
#, javascript-format #, javascript-format
msgid "Master Key %s" msgid "Master Key %s"
msgstr "Clef maître %s" msgstr "Clef maître %s"
@ -1385,6 +1406,15 @@ msgstr ""
msgid "Welcome" msgid "Welcome"
msgstr "Bienvenue" msgstr "Bienvenue"
#~ msgid ""
#~ "Release notes:\n"
#~ "\n"
#~ "%s"
#~ msgstr ""
#~ "Notes de version :\n"
#~ "\n"
#~ "%s"
#~ msgid "Imports an Evernote notebook file (.enex file)." #~ msgid "Imports an Evernote notebook file (.enex file)."
#~ msgstr "Importer un carnet Evernote (fichier .enex)." #~ msgstr "Importer un carnet Evernote (fichier .enex)."

View File

@ -510,6 +510,10 @@ msgstr "Default: %s"
msgid "Possible keys/values:" msgid "Possible keys/values:"
msgstr "Mogući ključevi/vrijednosti:" msgstr "Mogući ključevi/vrijednosti:"
#, fuzzy
msgid "Type `joplin help` for usage information."
msgstr "Prikazuje informacije o korištenju."
msgid "Fatal error:" msgid "Fatal error:"
msgstr "Fatalna greška:" msgstr "Fatalna greška:"
@ -562,16 +566,17 @@ msgstr ""
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..." msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr ""
msgid "File"
msgstr "Datoteka"
msgid "Directory"
msgstr ""
#, javascript-format #, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..." msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr ""
#, fuzzy
msgid "PDF File"
msgstr "Datoteka"
msgid "File"
msgstr "Datoteka"
msgid "New note" msgid "New note"
msgstr "Nova bilješka" msgstr "Nova bilješka"
@ -588,6 +593,9 @@ msgstr "Uvoz"
msgid "Export" msgid "Export"
msgstr "Uvoz" msgstr "Uvoz"
msgid "Print"
msgstr ""
#, javascript-format #, javascript-format
msgid "Hide %s" msgid "Hide %s"
msgstr "" msgstr ""
@ -635,6 +643,10 @@ msgstr "Pomoć"
msgid "Website and documentation" msgid "Website and documentation"
msgstr "Website i dokumentacija" msgstr "Website i dokumentacija"
#, fuzzy
msgid "Make a donation"
msgstr "Website i dokumentacija"
msgid "Check for updates..." msgid "Check for updates..."
msgstr "" msgstr ""
@ -658,12 +670,8 @@ msgstr "U redu"
msgid "Cancel" msgid "Cancel"
msgstr "Odustani" msgstr "Odustani"
#, fuzzy, javascript-format msgid "Current version is up-to-date."
msgid "" msgstr ""
"Release notes:\n"
"\n"
"%s"
msgstr "Obriši bilješke?"
msgid "An update is available, do you want to download it now?" msgid "An update is available, do you want to download it now?"
msgstr "" msgstr ""
@ -675,9 +683,6 @@ msgstr ""
msgid "No" msgid "No"
msgstr "N" msgstr "N"
msgid "Current version is up-to-date."
msgstr ""
#, fuzzy #, fuzzy
msgid "Check synchronisation configuration" msgid "Check synchronisation configuration"
msgstr "Prekini sinkronizaciju" msgstr "Prekini sinkronizaciju"
@ -982,8 +987,8 @@ msgstr "Obrisane udaljene stavke: %d."
msgid "Fetched items: %d/%d." msgid "Fetched items: %d/%d."
msgstr "Stvorene lokalne stavke: %d." msgstr "Stvorene lokalne stavke: %d."
#, javascript-format #, fuzzy, javascript-format
msgid "State: \"%s\"." msgid "State: %s."
msgstr "Stanje: \"%s\"." msgstr "Stanje: \"%s\"."
msgid "Cancelling..." msgid "Cancelling..."
@ -993,6 +998,16 @@ msgstr "Prekidam..."
msgid "Completed: %s" msgid "Completed: %s"
msgstr "Dovršeno: %s" msgstr "Dovršeno: %s"
#, fuzzy, javascript-format
msgid "Last error: %s"
msgstr "Fatalna greška:"
msgid "Idle"
msgstr ""
msgid "In progress"
msgstr ""
#, javascript-format #, javascript-format
msgid "Synchronisation is already in progress. State: %s" msgid "Synchronisation is already in progress. State: %s"
msgstr "Sinkronizacija je već u toku. Stanje: %s" msgstr "Sinkronizacija je već u toku. Stanje: %s"
@ -1175,6 +1190,9 @@ msgstr ""
msgid "Evernote Export File" msgid "Evernote Export File"
msgstr "Evernote izvozne datoteke" msgstr "Evernote izvozne datoteke"
msgid "Directory"
msgstr ""
#, javascript-format #, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\"" msgid "Cannot load \"%s\" module for format \"%s\""
msgstr "" msgstr ""
@ -1279,6 +1297,15 @@ msgstr "Potvrdi"
msgid "Cancel synchronisation" msgid "Cancel synchronisation"
msgstr "Prekini sinkronizaciju" msgstr "Prekini sinkronizaciju"
msgid "New tags:"
msgstr ""
msgid "Type new tags or select from list"
msgstr ""
msgid "Joplin website"
msgstr ""
#, javascript-format #, javascript-format
msgid "Master Key %s" msgid "Master Key %s"
msgstr "" msgstr ""
@ -1363,6 +1390,13 @@ msgstr "Trenutno nemaš nijednu bilježnicu. Stvori novu klikom na (+) gumb."
msgid "Welcome" msgid "Welcome"
msgstr "Dobro došli" msgstr "Dobro došli"
#, fuzzy
#~ msgid ""
#~ "Release notes:\n"
#~ "\n"
#~ "%s"
#~ msgstr "Obriši bilješke?"
#~ msgid "Imports an Evernote notebook file (.enex file)." #~ msgid "Imports an Evernote notebook file (.enex file)."
#~ msgstr "Uvozi Evernote bilježnicu (.enex datoteku)." #~ msgstr "Uvozi Evernote bilježnicu (.enex datoteku)."

View File

@ -496,6 +496,10 @@ msgstr "Predefinito: %s"
msgid "Possible keys/values:" msgid "Possible keys/values:"
msgstr "Chiave/valore possibili:" msgstr "Chiave/valore possibili:"
#, fuzzy
msgid "Type `joplin help` for usage information."
msgstr "Mostra le informazioni di utilizzo."
msgid "Fatal error:" msgid "Fatal error:"
msgstr "Errore fatale:" msgstr "Errore fatale:"
@ -544,16 +548,17 @@ msgstr ""
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..." msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr ""
msgid "File"
msgstr "File"
msgid "Directory"
msgstr ""
#, javascript-format #, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..." msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr ""
#, fuzzy
msgid "PDF File"
msgstr "File"
msgid "File"
msgstr "File"
msgid "New note" msgid "New note"
msgstr "Nuova nota" msgstr "Nuova nota"
@ -570,6 +575,9 @@ msgstr "Importa"
msgid "Export" msgid "Export"
msgstr "Importa" msgstr "Importa"
msgid "Print"
msgstr ""
#, javascript-format #, javascript-format
msgid "Hide %s" msgid "Hide %s"
msgstr "" msgstr ""
@ -617,6 +625,10 @@ msgstr "Aiuto"
msgid "Website and documentation" msgid "Website and documentation"
msgstr "Sito web e documentazione" msgstr "Sito web e documentazione"
#, fuzzy
msgid "Make a donation"
msgstr "Sito web e documentazione"
msgid "Check for updates..." msgid "Check for updates..."
msgstr "" msgstr ""
@ -640,12 +652,8 @@ msgstr "OK"
msgid "Cancel" msgid "Cancel"
msgstr "Cancella" msgstr "Cancella"
#, fuzzy, javascript-format msgid "Current version is up-to-date."
msgid "" msgstr ""
"Release notes:\n"
"\n"
"%s"
msgstr "Eliminare le note?"
msgid "An update is available, do you want to download it now?" msgid "An update is available, do you want to download it now?"
msgstr "" msgstr ""
@ -657,9 +665,6 @@ msgstr ""
msgid "No" msgid "No"
msgstr "N" msgstr "N"
msgid "Current version is up-to-date."
msgstr ""
#, fuzzy #, fuzzy
msgid "Check synchronisation configuration" msgid "Check synchronisation configuration"
msgstr "Cancella la sincronizzazione" msgstr "Cancella la sincronizzazione"
@ -968,8 +973,8 @@ msgstr "Elementi remoti eliminati: %d."
msgid "Fetched items: %d/%d." msgid "Fetched items: %d/%d."
msgstr "Elementi locali creati: %d." msgstr "Elementi locali creati: %d."
#, javascript-format #, fuzzy, javascript-format
msgid "State: \"%s\"." msgid "State: %s."
msgstr "Stato: \"%s\"." msgstr "Stato: \"%s\"."
msgid "Cancelling..." msgid "Cancelling..."
@ -979,6 +984,16 @@ msgstr "Cancellazione..."
msgid "Completed: %s" msgid "Completed: %s"
msgstr "Completata: %s" msgstr "Completata: %s"
#, fuzzy, javascript-format
msgid "Last error: %s"
msgstr "Errore fatale:"
msgid "Idle"
msgstr ""
msgid "In progress"
msgstr ""
#, javascript-format #, javascript-format
msgid "Synchronisation is already in progress. State: %s" msgid "Synchronisation is already in progress. State: %s"
msgstr "La sincronizzazione è già in corso. Stato: %s" msgstr "La sincronizzazione è già in corso. Stato: %s"
@ -1161,6 +1176,9 @@ msgstr ""
msgid "Evernote Export File" msgid "Evernote Export File"
msgstr "Esposta i files di Evernote" msgstr "Esposta i files di Evernote"
msgid "Directory"
msgstr ""
#, javascript-format #, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\"" msgid "Cannot load \"%s\" module for format \"%s\""
msgstr "" msgstr ""
@ -1265,6 +1283,15 @@ msgstr "Conferma"
msgid "Cancel synchronisation" msgid "Cancel synchronisation"
msgstr "Cancella la sincronizzazione" msgstr "Cancella la sincronizzazione"
msgid "New tags:"
msgstr ""
msgid "Type new tags or select from list"
msgstr ""
msgid "Joplin website"
msgstr ""
#, javascript-format #, javascript-format
msgid "Master Key %s" msgid "Master Key %s"
msgstr "" msgstr ""
@ -1351,6 +1378,13 @@ msgstr ""
msgid "Welcome" msgid "Welcome"
msgstr "Benvenuto" msgstr "Benvenuto"
#, fuzzy
#~ msgid ""
#~ "Release notes:\n"
#~ "\n"
#~ "%s"
#~ msgstr "Eliminare le note?"
#~ msgid "Imports an Evernote notebook file (.enex file)." #~ msgid "Imports an Evernote notebook file (.enex file)."
#~ msgstr "Importa un file blocco note di Evernote (.enex file)." #~ msgstr "Importa un file blocco note di Evernote (.enex file)."

View File

@ -492,6 +492,10 @@ msgstr "規定値: %s"
msgid "Possible keys/values:" msgid "Possible keys/values:"
msgstr "取り得るキーバリュー: " msgstr "取り得るキーバリュー: "
#, fuzzy
msgid "Type `joplin help` for usage information."
msgstr "使い方を表示する。"
msgid "Fatal error:" msgid "Fatal error:"
msgstr "致命的なエラー: " msgstr "致命的なエラー: "
@ -545,16 +549,17 @@ msgstr ""
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..." msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr ""
msgid "File"
msgstr "ファイル"
msgid "Directory"
msgstr ""
#, javascript-format #, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..." msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr ""
#, fuzzy
msgid "PDF File"
msgstr "ファイル"
msgid "File"
msgstr "ファイル"
msgid "New note" msgid "New note"
msgstr "新しいノート" msgstr "新しいノート"
@ -571,6 +576,9 @@ msgstr "インポート"
msgid "Export" msgid "Export"
msgstr "インポート" msgstr "インポート"
msgid "Print"
msgstr ""
#, javascript-format #, javascript-format
msgid "Hide %s" msgid "Hide %s"
msgstr "" msgstr ""
@ -618,6 +626,10 @@ msgstr "ヘルプ"
msgid "Website and documentation" msgid "Website and documentation"
msgstr "Webサイトとドキュメント" msgstr "Webサイトとドキュメント"
#, fuzzy
msgid "Make a donation"
msgstr "Webサイトとドキュメント"
msgid "Check for updates..." msgid "Check for updates..."
msgstr "" msgstr ""
@ -641,12 +653,8 @@ msgstr ""
msgid "Cancel" msgid "Cancel"
msgstr "キャンセル" msgstr "キャンセル"
#, fuzzy, javascript-format msgid "Current version is up-to-date."
msgid "" msgstr ""
"Release notes:\n"
"\n"
"%s"
msgstr "ノートを削除しますか?"
msgid "An update is available, do you want to download it now?" msgid "An update is available, do you want to download it now?"
msgstr "" msgstr ""
@ -657,9 +665,6 @@ msgstr ""
msgid "No" msgid "No"
msgstr "" msgstr ""
msgid "Current version is up-to-date."
msgstr ""
#, fuzzy #, fuzzy
msgid "Check synchronisation configuration" msgid "Check synchronisation configuration"
msgstr "同期の中止" msgstr "同期の中止"
@ -970,8 +975,8 @@ msgstr "リモートアイテムの削除: %d."
msgid "Fetched items: %d/%d." msgid "Fetched items: %d/%d."
msgstr "ローカルアイテムの作成: %d." msgstr "ローカルアイテムの作成: %d."
#, javascript-format #, fuzzy, javascript-format
msgid "State: \"%s\"." msgid "State: %s."
msgstr "状態: \"%s\"。" msgstr "状態: \"%s\"。"
msgid "Cancelling..." msgid "Cancelling..."
@ -981,6 +986,16 @@ msgstr "中止中..."
msgid "Completed: %s" msgid "Completed: %s"
msgstr "完了: %s" msgstr "完了: %s"
#, fuzzy, javascript-format
msgid "Last error: %s"
msgstr "致命的なエラー: "
msgid "Idle"
msgstr ""
msgid "In progress"
msgstr ""
#, javascript-format #, javascript-format
msgid "Synchronisation is already in progress. State: %s" msgid "Synchronisation is already in progress. State: %s"
msgstr "同期作業はすでに実行中です。状態: %s" msgstr "同期作業はすでに実行中です。状態: %s"
@ -1165,6 +1180,9 @@ msgstr ""
msgid "Evernote Export File" msgid "Evernote Export File"
msgstr "Evernote Exportファイル" msgstr "Evernote Exportファイル"
msgid "Directory"
msgstr ""
#, javascript-format #, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\"" msgid "Cannot load \"%s\" module for format \"%s\""
msgstr "" msgstr ""
@ -1269,6 +1287,15 @@ msgstr "確認"
msgid "Cancel synchronisation" msgid "Cancel synchronisation"
msgstr "同期の中止" msgstr "同期の中止"
msgid "New tags:"
msgstr ""
msgid "Type new tags or select from list"
msgstr ""
msgid "Joplin website"
msgstr ""
#, javascript-format #, javascript-format
msgid "Master Key %s" msgid "Master Key %s"
msgstr "" msgstr ""
@ -1355,6 +1382,13 @@ msgstr ""
msgid "Welcome" msgid "Welcome"
msgstr "ようこそ" msgstr "ようこそ"
#, fuzzy
#~ msgid ""
#~ "Release notes:\n"
#~ "\n"
#~ "%s"
#~ msgstr "ノートを削除しますか?"
#~ msgid "Imports an Evernote notebook file (.enex file)." #~ msgid "Imports an Evernote notebook file (.enex file)."
#~ msgstr "Evernoteノートブックファイル(.enex)のインポート" #~ msgstr "Evernoteノートブックファイル(.enex)のインポート"

View File

@ -449,6 +449,9 @@ msgstr ""
msgid "Possible keys/values:" msgid "Possible keys/values:"
msgstr "" msgstr ""
msgid "Type `joplin help` for usage information."
msgstr ""
msgid "Fatal error:" msgid "Fatal error:"
msgstr "" msgstr ""
@ -490,16 +493,16 @@ msgstr ""
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..." msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr ""
msgid "File"
msgstr ""
msgid "Directory"
msgstr ""
#, javascript-format #, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..." msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr ""
msgid "PDF File"
msgstr ""
msgid "File"
msgstr ""
msgid "New note" msgid "New note"
msgstr "" msgstr ""
@ -515,6 +518,9 @@ msgstr ""
msgid "Export" msgid "Export"
msgstr "" msgstr ""
msgid "Print"
msgstr ""
#, javascript-format #, javascript-format
msgid "Hide %s" msgid "Hide %s"
msgstr "" msgstr ""
@ -561,6 +567,9 @@ msgstr ""
msgid "Website and documentation" msgid "Website and documentation"
msgstr "" msgstr ""
msgid "Make a donation"
msgstr ""
msgid "Check for updates..." msgid "Check for updates..."
msgstr "" msgstr ""
@ -584,11 +593,7 @@ msgstr ""
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr ""
#, javascript-format msgid "Current version is up-to-date."
msgid ""
"Release notes:\n"
"\n"
"%s"
msgstr "" msgstr ""
msgid "An update is available, do you want to download it now?" msgid "An update is available, do you want to download it now?"
@ -600,9 +605,6 @@ msgstr ""
msgid "No" msgid "No"
msgstr "" msgstr ""
msgid "Current version is up-to-date."
msgstr ""
msgid "Check synchronisation configuration" msgid "Check synchronisation configuration"
msgstr "" msgstr ""
@ -895,7 +897,7 @@ msgid "Fetched items: %d/%d."
msgstr "" msgstr ""
#, javascript-format #, javascript-format
msgid "State: \"%s\"." msgid "State: %s."
msgstr "" msgstr ""
msgid "Cancelling..." msgid "Cancelling..."
@ -905,6 +907,16 @@ msgstr ""
msgid "Completed: %s" msgid "Completed: %s"
msgstr "" msgstr ""
#, javascript-format
msgid "Last error: %s"
msgstr ""
msgid "Idle"
msgstr ""
msgid "In progress"
msgstr ""
#, javascript-format #, javascript-format
msgid "Synchronisation is already in progress. State: %s" msgid "Synchronisation is already in progress. State: %s"
msgstr "" msgstr ""
@ -1075,6 +1087,9 @@ msgstr ""
msgid "Evernote Export File" msgid "Evernote Export File"
msgstr "" msgstr ""
msgid "Directory"
msgstr ""
#, javascript-format #, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\"" msgid "Cannot load \"%s\" module for format \"%s\""
msgstr "" msgstr ""
@ -1178,6 +1193,15 @@ msgstr ""
msgid "Cancel synchronisation" msgid "Cancel synchronisation"
msgstr "" msgstr ""
msgid "New tags:"
msgstr ""
msgid "Type new tags or select from list"
msgstr ""
msgid "Joplin website"
msgstr ""
#, javascript-format #, javascript-format
msgid "Master Key %s" msgid "Master Key %s"
msgstr "" msgstr ""

View File

@ -505,6 +505,10 @@ msgstr "Standaard: %s"
msgid "Possible keys/values:" msgid "Possible keys/values:"
msgstr "Mogelijke sleutels/waarden:" msgstr "Mogelijke sleutels/waarden:"
#, fuzzy
msgid "Type `joplin help` for usage information."
msgstr "Toont gebruiksinformatie."
msgid "Fatal error:" msgid "Fatal error:"
msgstr "Fatale fout:" msgstr "Fatale fout:"
@ -562,16 +566,17 @@ msgstr ""
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..." msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr ""
msgid "File"
msgstr "Bestand"
msgid "Directory"
msgstr ""
#, javascript-format #, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..." msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr ""
#, fuzzy
msgid "PDF File"
msgstr "Bestand"
msgid "File"
msgstr "Bestand"
msgid "New note" msgid "New note"
msgstr "Nieuwe notitie" msgstr "Nieuwe notitie"
@ -588,6 +593,9 @@ msgstr "Importeer"
msgid "Export" msgid "Export"
msgstr "Importeer" msgstr "Importeer"
msgid "Print"
msgstr ""
#, javascript-format #, javascript-format
msgid "Hide %s" msgid "Hide %s"
msgstr "" msgstr ""
@ -634,6 +642,10 @@ msgstr "Help"
msgid "Website and documentation" msgid "Website and documentation"
msgstr "Website en documentatie" msgstr "Website en documentatie"
#, fuzzy
msgid "Make a donation"
msgstr "Website en documentatie"
msgid "Check for updates..." msgid "Check for updates..."
msgstr "" msgstr ""
@ -657,12 +669,8 @@ msgstr "OK"
msgid "Cancel" msgid "Cancel"
msgstr "Annuleer" msgstr "Annuleer"
#, fuzzy, javascript-format msgid "Current version is up-to-date."
msgid "" msgstr ""
"Release notes:\n"
"\n"
"%s"
msgstr "Notities verwijderen?"
msgid "An update is available, do you want to download it now?" msgid "An update is available, do you want to download it now?"
msgstr "" msgstr ""
@ -674,9 +682,6 @@ msgstr ""
msgid "No" msgid "No"
msgstr "N" msgstr "N"
msgid "Current version is up-to-date."
msgstr ""
#, fuzzy #, fuzzy
msgid "Check synchronisation configuration" msgid "Check synchronisation configuration"
msgstr "Annuleer synchronisatie" msgstr "Annuleer synchronisatie"
@ -997,8 +1002,8 @@ msgstr "Verwijderde remote items: %d."
msgid "Fetched items: %d/%d." msgid "Fetched items: %d/%d."
msgstr "Opgehaalde items: %d/%d." msgstr "Opgehaalde items: %d/%d."
#, javascript-format #, fuzzy, javascript-format
msgid "State: \"%s\"." msgid "State: %s."
msgstr "Status: \"%s\"" msgstr "Status: \"%s\""
msgid "Cancelling..." msgid "Cancelling..."
@ -1008,6 +1013,16 @@ msgstr "Annuleren..."
msgid "Completed: %s" msgid "Completed: %s"
msgstr "Voltooid: %s" msgstr "Voltooid: %s"
#, fuzzy, javascript-format
msgid "Last error: %s"
msgstr "Fatale fout:"
msgid "Idle"
msgstr ""
msgid "In progress"
msgstr ""
#, javascript-format #, javascript-format
msgid "Synchronisation is already in progress. State: %s" msgid "Synchronisation is already in progress. State: %s"
msgstr "Synchronisatie is reeds bezig. Status: %s" msgstr "Synchronisatie is reeds bezig. Status: %s"
@ -1193,6 +1208,9 @@ msgstr ""
msgid "Evernote Export File" msgid "Evernote Export File"
msgstr "Exporteer Evernote bestanden" msgstr "Exporteer Evernote bestanden"
msgid "Directory"
msgstr ""
#, javascript-format #, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\"" msgid "Cannot load \"%s\" module for format \"%s\""
msgstr "" msgstr ""
@ -1301,6 +1319,15 @@ msgstr "Bevestig"
msgid "Cancel synchronisation" msgid "Cancel synchronisation"
msgstr "Annuleer synchronisatie" msgstr "Annuleer synchronisatie"
msgid "New tags:"
msgstr ""
msgid "Type new tags or select from list"
msgstr ""
msgid "Joplin website"
msgstr ""
#, javascript-format #, javascript-format
msgid "Master Key %s" msgid "Master Key %s"
msgstr "Hoofdsleutel: %s" msgstr "Hoofdsleutel: %s"
@ -1386,6 +1413,13 @@ msgstr ""
msgid "Welcome" msgid "Welcome"
msgstr "Welkom" msgstr "Welkom"
#, fuzzy
#~ msgid ""
#~ "Release notes:\n"
#~ "\n"
#~ "%s"
#~ msgstr "Notities verwijderen?"
#~ msgid "Imports an Evernote notebook file (.enex file)." #~ msgid "Imports an Evernote notebook file (.enex file)."
#~ msgstr "Importeer een Evernote notitieboek (.enex bestand)." #~ msgstr "Importeer een Evernote notitieboek (.enex bestand)."

View File

@ -7,13 +7,13 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Joplin-CLI 1.0.0\n" "Project-Id-Version: Joplin-CLI 1.0.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"Last-Translator: \n" "Last-Translator: Renato Nunes Bastos <rnbastos@gmail.com>\n"
"Language-Team: \n" "Language-Team: \n"
"Language: pt_BR\n" "Language: pt_BR\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.0.5\n" "X-Generator: Poedit 2.0.6\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n"
msgid "To delete a tag, untag the associated notes." msgid "To delete a tag, untag the associated notes."
@ -50,16 +50,16 @@ msgstr "s"
msgid "Cancelling background synchronisation... Please wait." msgid "Cancelling background synchronisation... Please wait."
msgstr "Cancelando sincronização em segundo plano... Por favor, aguarde." msgstr "Cancelando sincronização em segundo plano... Por favor, aguarde."
#, fuzzy, javascript-format #, javascript-format
msgid "No such command: %s" msgid "No such command: %s"
msgstr "Comando inválido: \"%s\"" msgstr "Não existe o comando: \"%s\""
#, javascript-format #, javascript-format
msgid "The command \"%s\" is only available in GUI mode" msgid "The command \"%s\" is only available in GUI mode"
msgstr "O comando \"%s\" está disponível somente em modo gráfico" msgstr "O comando \"%s\" está disponível somente em modo gráfico"
msgid "Cannot change encrypted item" msgid "Cannot change encrypted item"
msgstr "" msgstr "Não pode mudar um item encriptado"
#, javascript-format #, javascript-format
msgid "Missing required argument: %s" msgid "Missing required argument: %s"
@ -127,22 +127,25 @@ msgid ""
"Manages E2EE configuration. Commands are `enable`, `disable`, `decrypt`, " "Manages E2EE configuration. Commands are `enable`, `disable`, `decrypt`, "
"`status` and `target-status`." "`status` and `target-status`."
msgstr "" msgstr ""
"Gerencia a configuração E2EE. Os comandos são `enable`, `disable`, "
"`decrypt`, `status` e `target-status`."
msgid "Enter master password:" msgid "Enter master password:"
msgstr "" msgstr "Entre a senha master:"
msgid "Operation cancelled" msgid "Operation cancelled"
msgstr "" msgstr "Operação cancelada"
msgid "" msgid ""
"Starting decryption... Please wait as it may take several minutes depending " "Starting decryption... Please wait as it may take several minutes depending "
"on how much there is to decrypt." "on how much there is to decrypt."
msgstr "" msgstr ""
"Iniciando decriptação... Por favor aguarde, pois isso pode demorar vários "
"minutos, dependendo de quanto há para decriptar."
msgid "Completed decryption." msgid "Completed decryption."
msgstr "" msgstr "Decriptação completada."
#, fuzzy
msgid "Enabled" msgid "Enabled"
msgstr "Desabilitado" msgstr "Desabilitado"
@ -151,7 +154,7 @@ msgstr "Desabilitado"
#, javascript-format #, javascript-format
msgid "Encryption is: %s" msgid "Encryption is: %s"
msgstr "" msgstr "Encriptação está: %s"
msgid "Edit note." msgid "Edit note."
msgstr "Editar nota." msgstr "Editar nota."
@ -174,7 +177,7 @@ msgstr "Começando a editar a nota. Feche o editor para voltar ao prompt."
#, javascript-format #, javascript-format
msgid "Error opening note in editor: %s" msgid "Error opening note in editor: %s"
msgstr "" msgstr "Erro ao abir a nota no editor: %s"
msgid "Note has been saved." msgid "Note has been saved."
msgstr "Nota gravada." msgstr "Nota gravada."
@ -182,7 +185,6 @@ msgstr "Nota gravada."
msgid "Exits the application." msgid "Exits the application."
msgstr "Sai da aplicação." msgstr "Sai da aplicação."
#, fuzzy
msgid "" msgid ""
"Exports Joplin data to the given path. By default, it will export the " "Exports Joplin data to the given path. By default, it will export the "
"complete database including notebooks, notes, tags and resources." "complete database including notebooks, notes, tags and resources."
@ -191,9 +193,9 @@ msgstr ""
"exportará o banco de dados completo, incluindo cadernos, notas, tags e " "exportará o banco de dados completo, incluindo cadernos, notas, tags e "
"recursos." "recursos."
#, fuzzy, javascript-format #, javascript-format
msgid "Destination format: %s" msgid "Destination format: %s"
msgstr "Formato de data" msgstr "Formato do destino: %s"
msgid "Exports only the given note." msgid "Exports only the given note."
msgstr "Exporta apenas a nota fornecida." msgstr "Exporta apenas a nota fornecida."
@ -209,16 +211,17 @@ msgstr "Exibe informações de uso."
#, javascript-format #, javascript-format
msgid "For information on how to customise the shortcuts please visit %s" msgid "For information on how to customise the shortcuts please visit %s"
msgstr "" msgstr "Para informações sobre como customizar os atalhos, por favor visite %s"
msgid "Shortcuts are not available in CLI mode." msgid "Shortcuts are not available in CLI mode."
msgstr "Os atalhos não estão disponíveis no modo CLI." msgstr "Os atalhos não estão disponíveis no modo CLI."
#, fuzzy
msgid "" msgid ""
"Type `help [command]` for more information about a command; or type `help " "Type `help [command]` for more information about a command; or type `help "
"all` for the complete usage information." "all` for the complete usage information."
msgstr "Digite `help [comando]` para obter mais informações sobre um comando." msgstr ""
"Digite `help [comando]` para obter mais informações sobre um comando; ou "
"digite `help all` para informações completas de uso."
msgid "The possible commands are:" msgid "The possible commands are:"
msgstr "Os comandos possíveis são:" msgstr "Os comandos possíveis são:"
@ -252,19 +255,17 @@ msgstr "Para entrar no modo de linha de comando, pressione \":\""
msgid "To exit command line mode, press ESCAPE" msgid "To exit command line mode, press ESCAPE"
msgstr "Para sair do modo de linha de comando, pressione o ESC" msgstr "Para sair do modo de linha de comando, pressione o ESC"
#, fuzzy
msgid "" msgid ""
"For the list of keyboard shortcuts and config options, type `help keymap`" "For the list of keyboard shortcuts and config options, type `help keymap`"
msgstr "" msgstr ""
"Para a lista completa de atalhos de teclado disponíveis, digite `help " "Para a lista completa de atalhos de teclado disponíveis, digite `help keymap`"
"shortcuts`"
msgid "Imports data into Joplin." msgid "Imports data into Joplin."
msgstr "" msgstr "Importa dados para o Joplin."
#, fuzzy, javascript-format #, javascript-format
msgid "Source format: %s" msgid "Source format: %s"
msgstr "Comando inválido: \"%s\"" msgstr "Formato da origem: \"%s\""
msgid "Do not ask for confirmation." msgid "Do not ask for confirmation."
msgstr "Não pedir confirmação." msgstr "Não pedir confirmação."
@ -363,6 +364,8 @@ msgstr "Exclui o caderno sem pedir confirmação."
msgid "Delete notebook? All notes within this notebook will also be deleted." msgid "Delete notebook? All notes within this notebook will also be deleted."
msgstr "" msgstr ""
"Excluir o caderno? Todas as notas deste caderno notebook também serão "
"excluídas."
msgid "Deletes the notes matching <note-pattern>." msgid "Deletes the notes matching <note-pattern>."
msgstr "Exclui as notas correspondentes ao padrão <note-pattern>." msgstr "Exclui as notas correspondentes ao padrão <note-pattern>."
@ -380,13 +383,17 @@ msgstr "Apagar nota?"
msgid "Searches for the given <pattern> in all the notes." msgid "Searches for the given <pattern> in all the notes."
msgstr "Procura o padrão <pattern>em todas as notas." msgstr "Procura o padrão <pattern>em todas as notas."
#, fuzzy, javascript-format #, javascript-format
msgid "" msgid ""
"Sets the property <name> of the given <note> to the given [value]. Possible " "Sets the property <name> of the given <note> to the given [value]. Possible "
"properties are:\n" "properties are:\n"
"\n" "\n"
"%s" "%s"
msgstr "Define a propriedade <name> da <note> para o dado [valor]." msgstr ""
"Define a propriedade <name> da <note> para o valor [value]. As propriedades "
"possíveis são:\n"
"\n"
"%s"
msgid "Displays summary about the notes and notebooks." msgid "Displays summary about the notes and notebooks."
msgstr "Exibe sumário sobre as notas e cadernos." msgstr "Exibe sumário sobre as notas e cadernos."
@ -407,6 +414,8 @@ msgstr ""
#, javascript-format #, javascript-format
msgid "Not authentified with %s. Please provide any missing credentials." msgid "Not authentified with %s. Please provide any missing credentials."
msgstr "" msgstr ""
"Não autenticado com %s. Por favor, complete as credenciais que estiverem "
"faltando."
msgid "Synchronisation is already in progress." msgid "Synchronisation is already in progress."
msgstr "A sincronização já está em andamento." msgstr "A sincronização já está em andamento."
@ -493,6 +502,9 @@ msgstr "Padrão: %s"
msgid "Possible keys/values:" msgid "Possible keys/values:"
msgstr "Possíveis chaves/valores:" msgstr "Possíveis chaves/valores:"
msgid "Type `joplin help` for usage information."
msgstr "Digite 'joplin help' para informações de uso."
msgid "Fatal error:" msgid "Fatal error:"
msgstr "Erro fatal:" msgstr "Erro fatal:"
@ -528,6 +540,13 @@ msgid ""
"\n" "\n"
"For example, to create a notebook press `mb`; to create a note press `mn`." "For example, to create a notebook press `mb`; to create a note press `mn`."
msgstr "" msgstr ""
"Bem-vindo ao Joplin!\n"
"\n"
"Digite `:help shortcuts` para obter a lista de atalhos de teclado, ou apenas "
"`:help` para informações de utilização.\n"
"\n"
"Por exemplo, para criar um caderno digite `mb`; para criar uma nota, digite "
"`mn`."
msgid "" msgid ""
"One or more items are currently encrypted and you may need to supply a " "One or more items are currently encrypted and you may need to supply a "
@ -535,20 +554,25 @@ msgid ""
"supplied the password, the encrypted items are being decrypted in the " "supplied the password, the encrypted items are being decrypted in the "
"background and will be available soon." "background and will be available soon."
msgstr "" msgstr ""
"Um ou mais itens estão criptografados, e você pode precisar de informar uma "
"senha master. Para fazer isso, por favor digite `e2ee decrypt`. Se você já "
"forneceu a senha, os itens criptografados estão sendo decriptados em "
"background e logo estarão disponíveis."
#, javascript-format #, javascript-format
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..." msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr "Exportando para \"%s\" com o formato \"%s\". Por favor, aguarde..."
msgid "File"
msgstr "Arquivo"
msgid "Directory"
msgstr ""
#, javascript-format #, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..." msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr "Importando de \"%s\" com o formato \"%s\". Por favor, aguarde..."
#, fuzzy
msgid "PDF File"
msgstr "Arquivo"
msgid "File"
msgstr "Arquivo"
msgid "New note" msgid "New note"
msgstr "Nova nota" msgstr "Nova nota"
@ -562,13 +586,15 @@ msgstr "Novo caderno"
msgid "Import" msgid "Import"
msgstr "Importar" msgstr "Importar"
#, fuzzy
msgid "Export" msgid "Export"
msgstr "Importar" msgstr "Exportar"
msgid "Print"
msgstr ""
#, javascript-format #, javascript-format
msgid "Hide %s" msgid "Hide %s"
msgstr "" msgstr "Ocultar %s"
msgid "Quit" msgid "Quit"
msgstr "Sair" msgstr "Sair"
@ -589,24 +615,22 @@ msgid "Search in all the notes"
msgstr "Pesquisar em todas as notas" msgstr "Pesquisar em todas as notas"
msgid "View" msgid "View"
msgstr "" msgstr "Visualizar"
msgid "Toggle editor layout" msgid "Toggle editor layout"
msgstr "" msgstr "Alternar layout do editor"
msgid "Tools" msgid "Tools"
msgstr "Ferramentas" msgstr "Ferramentas"
#, fuzzy
msgid "Synchronisation status" msgid "Synchronisation status"
msgstr "Alvo de sincronização" msgstr "Status de sincronização"
msgid "Encryption options" msgid "Encryption options"
msgstr "" msgstr "Opções de Encriptação"
#, fuzzy
msgid "General Options" msgid "General Options"
msgstr "Opções" msgstr "Opções Gerais"
msgid "Help" msgid "Help"
msgstr "Ajuda" msgstr "Ajuda"
@ -614,8 +638,11 @@ msgstr "Ajuda"
msgid "Website and documentation" msgid "Website and documentation"
msgstr "Website e documentação" msgstr "Website e documentação"
msgid "Make a donation"
msgstr "Fazer uma doação"
msgid "Check for updates..." msgid "Check for updates..."
msgstr "" msgstr "Verificar atualizações..."
msgid "About Joplin" msgid "About Joplin"
msgstr "Sobre o Joplin" msgstr "Sobre o Joplin"
@ -624,12 +651,12 @@ msgstr "Sobre o Joplin"
msgid "%s %s (%s, %s)" msgid "%s %s (%s, %s)"
msgstr "%s %s (%s, %s)" msgstr "%s %s (%s, %s)"
#, fuzzy, javascript-format #, javascript-format
msgid "Open %s" msgid "Open %s"
msgstr "Em %s: %s" msgstr "Abrir %s"
msgid "Exit" msgid "Exit"
msgstr "" msgstr "Sair"
msgid "OK" msgid "OK"
msgstr "OK" msgstr "OK"
@ -637,42 +664,35 @@ msgstr "OK"
msgid "Cancel" msgid "Cancel"
msgstr "Cancelar" msgstr "Cancelar"
#, fuzzy, javascript-format msgid "Current version is up-to-date."
msgid "" msgstr "A versão atual está atualizada."
"Release notes:\n"
"\n"
"%s"
msgstr "Excluir notas?"
msgid "An update is available, do you want to download it now?" msgid "An update is available, do you want to download it now?"
msgstr "" msgstr "Uma atualização está disponível, você quer baixar agora?"
msgid "Yes" msgid "Yes"
msgstr "" msgstr "Sim"
#, fuzzy
msgid "No" msgid "No"
msgstr "N" msgstr "Não"
msgid "Current version is up-to-date."
msgstr ""
#, fuzzy
msgid "Check synchronisation configuration" msgid "Check synchronisation configuration"
msgstr "Cancelar sincronização" msgstr "Verificar a configuração da sincronização"
#, javascript-format #, javascript-format
msgid "Notes and settings are stored in: %s" msgid "Notes and settings are stored in: %s"
msgstr "" msgstr "Notas e configurações estão armazenadas em: %s"
msgid "Save" msgid "Save"
msgstr "" msgstr "Salvar"
msgid "" msgid ""
"Disabling encryption means *all* your notes and attachments are going to be " "Disabling encryption means *all* your notes and attachments are going to be "
"re-synchronised and sent unencrypted to the sync target. Do you wish to " "re-synchronised and sent unencrypted to the sync target. Do you wish to "
"continue?" "continue?"
msgstr "" msgstr ""
"Desabilitar a encriptação significa que *todas* as suas notas e anexos serão "
"re-sincronizados e enviados sem encriptação. Você quer continuar?"
msgid "" msgid ""
"Enabling encryption means *all* your notes and attachments are going to be " "Enabling encryption means *all* your notes and attachments are going to be "
@ -680,59 +700,68 @@ msgid ""
"password as, for security purposes, this will be the *only* way to decrypt " "password as, for security purposes, this will be the *only* way to decrypt "
"the data! To enable encryption, please enter your password below." "the data! To enable encryption, please enter your password below."
msgstr "" msgstr ""
"Habilitar a encriptação significa que *todas* as suas notas e anexos serão "
"re-sincronizados e re-enviados com encriptação. Não perca sua senha, pois, "
"por medidas de segurança, esse será o *único* modo de decriptar seus dados! "
"Para habilitar a encriptação, por favor entre sua senha abaixo."
msgid "Disable encryption" msgid "Disable encryption"
msgstr "" msgstr "Desabilitar encriptação"
msgid "Enable encryption" msgid "Enable encryption"
msgstr "" msgstr "Habilitar encriptação"
msgid "Master Keys" msgid "Master Keys"
msgstr "" msgstr "Chaves mestras"
msgid "Active" msgid "Active"
msgstr "" msgstr "Ativar"
msgid "ID" msgid "ID"
msgstr "" msgstr "ID"
msgid "Source" msgid "Source"
msgstr "" msgstr "Origem"
#, fuzzy
msgid "Created" msgid "Created"
msgstr "Criado: %d." msgstr "Criado"
#, fuzzy
msgid "Updated" msgid "Updated"
msgstr "Atualizado: %d." msgstr "Atualizado"
msgid "Password" msgid "Password"
msgstr "" msgstr "Senha"
msgid "Password OK" msgid "Password OK"
msgstr "" msgstr "Senha OK"
msgid "" msgid ""
"Note: Only one master key is going to be used for encryption (the one marked " "Note: Only one master key is going to be used for encryption (the one marked "
"as \"active\"). Any of the keys might be used for decryption, depending on " "as \"active\"). Any of the keys might be used for decryption, depending on "
"how the notes or notebooks were originally encrypted." "how the notes or notebooks were originally encrypted."
msgstr "" msgstr ""
"Noto: Apenas uma chave máster será usada para encriptação (a que estiver "
"marcada como \"ativa\"). Qualquer uma das chaves pode ser usada para "
"decriptação, dependendo de como as notas ou os cadernos foram encriptados "
"originalmente."
msgid "Missing Master Keys" msgid "Missing Master Keys"
msgstr "" msgstr "Chaves Master faltando"
msgid "" msgid ""
"The master keys with these IDs are used to encrypt some of your items, " "The master keys with these IDs are used to encrypt some of your items, "
"however the application does not currently have access to them. It is likely " "however the application does not currently have access to them. It is likely "
"they will eventually be downloaded via synchronisation." "they will eventually be downloaded via synchronisation."
msgstr "" msgstr ""
"As chaves master com essas IDs são usadas para encriptar alguns de seus "
"itens, contudo a aplicação atualmente não tem acesso a elas. Provavelmente, "
"elas serão baixadas via sincronização."
msgid "Status" msgid "Status"
msgstr "Status" msgstr "Status"
msgid "Encryption is:" msgid "Encryption is:"
msgstr "" msgstr "Encriptação está:"
msgid "Back" msgid "Back"
msgstr "Voltar" msgstr "Voltar"
@ -770,19 +799,17 @@ msgstr "Procurar"
msgid "Layout" msgid "Layout"
msgstr "Layout" msgstr "Layout"
#, fuzzy
msgid "Some items cannot be synchronised." msgid "Some items cannot be synchronised."
msgstr "Não é possível inicializar o sincronizador." msgstr "Alguns itens não podem ser sincronizados."
msgid "View them now" msgid "View them now"
msgstr "" msgstr "Visualizar agora"
#, fuzzy
msgid "Some items cannot be decrypted." msgid "Some items cannot be decrypted."
msgstr "Não é possível inicializar o sincronizador." msgstr "Alguns itens não podem ser decriptados."
msgid "Set the password" msgid "Set the password"
msgstr "" msgstr "Configurar a senha"
msgid "Add or remove tags" msgid "Add or remove tags"
msgstr "Adicionar ou remover tags" msgstr "Adicionar ou remover tags"
@ -799,17 +826,15 @@ msgstr "Excluir notas?"
msgid "No notes in here. Create one by clicking on \"New note\"." msgid "No notes in here. Create one by clicking on \"New note\"."
msgstr "Não há notas aqui. Crie uma, clicando em \"Nova nota\"." msgstr "Não há notas aqui. Crie uma, clicando em \"Nova nota\"."
#, fuzzy
msgid "" msgid ""
"There is currently no notebook. Create one by clicking on \"New notebook\"." "There is currently no notebook. Create one by clicking on \"New notebook\"."
msgstr "Atualmente, não há notas. Crie uma, clicando no botão (+)." msgstr "Atualmente, não há cadernos. Crie um, clicando em \"Novo caderno\"."
msgid "Open..." msgid "Open..."
msgstr "" msgstr "Abrir..."
#, fuzzy
msgid "Save as..." msgid "Save as..."
msgstr "Gravar alterações" msgstr "Salvar como..."
#, javascript-format #, javascript-format
msgid "Unsupported link or message: %s" msgid "Unsupported link or message: %s"
@ -829,18 +854,18 @@ msgid ""
"This note has no content. Click on \"%s\" to toggle the editor and edit the " "This note has no content. Click on \"%s\" to toggle the editor and edit the "
"note." "note."
msgstr "" msgstr ""
"Esta nota não possui conteúdo. Clique em \"%s\" para alternar para o editor, "
"e edite a nota."
#, fuzzy
msgid "to-do" msgid "to-do"
msgstr "Nova tarefa" msgstr "tarefa"
#, fuzzy
msgid "note" msgid "note"
msgstr "Nova nota" msgstr "nota"
#, fuzzy, javascript-format #, javascript-format
msgid "Creating new %s..." msgid "Creating new %s..."
msgstr "Importando notas ..." msgstr "Criando novo %s..."
msgid "Refresh" msgid "Refresh"
msgstr "Atualizar" msgstr "Atualizar"
@ -854,12 +879,11 @@ msgstr "Login no OneDrive"
msgid "Options" msgid "Options"
msgstr "Opções" msgstr "Opções"
#, fuzzy
msgid "Synchronisation Status" msgid "Synchronisation Status"
msgstr "Alvo de sincronização" msgstr "Status de sincronização"
msgid "Encryption Options" msgid "Encryption Options"
msgstr "" msgstr "Opções de Encriptação"
msgid "Remove this tag from all the notes?" msgid "Remove this tag from all the notes?"
msgstr "Remover esta tag de todas as notas?" msgstr "Remover esta tag de todas as notas?"
@ -879,9 +903,10 @@ msgstr "Cadernos"
msgid "Searches" msgid "Searches"
msgstr "Pesquisas" msgstr "Pesquisas"
#, fuzzy
msgid "Please select where the sync status should be exported to" msgid "Please select where the sync status should be exported to"
msgstr "Por favor, primeiro, selecione a nota ou caderno a excluir." msgstr ""
"Favor selecionar o local para onde o status de sincronia deveria ser "
"exportado"
#, javascript-format #, javascript-format
msgid "Usage: %s" msgid "Usage: %s"
@ -895,7 +920,7 @@ msgid "File system"
msgstr "Sistema de arquivos" msgstr "Sistema de arquivos"
msgid "Nextcloud" msgid "Nextcloud"
msgstr "" msgstr "Nextcloud"
msgid "OneDrive" msgid "OneDrive"
msgstr "OneDrive" msgstr "OneDrive"
@ -904,7 +929,7 @@ msgid "OneDrive Dev (For testing only)"
msgstr "OneDrive Dev (apenas para testes)" msgstr "OneDrive Dev (apenas para testes)"
msgid "WebDAV" msgid "WebDAV"
msgstr "" msgstr "WebDAV"
#, javascript-format #, javascript-format
msgid "Unknown log level: %s" msgid "Unknown log level: %s"
@ -964,12 +989,12 @@ msgstr "Itens locais excluídos: %d."
msgid "Deleted remote items: %d." msgid "Deleted remote items: %d."
msgstr "Itens remotos excluídos: %d." msgstr "Itens remotos excluídos: %d."
#, fuzzy, javascript-format
msgid "Fetched items: %d/%d."
msgstr "Itens locais criados: %d."
#, javascript-format #, javascript-format
msgid "State: \"%s\"." msgid "Fetched items: %d/%d."
msgstr "Itens pesquisados: %d/%d."
#, fuzzy, javascript-format
msgid "State: %s."
msgstr "Estado: \"%s\"." msgstr "Estado: \"%s\"."
msgid "Cancelling..." msgid "Cancelling..."
@ -979,16 +1004,25 @@ msgstr "Cancelando..."
msgid "Completed: %s" msgid "Completed: %s"
msgstr "Completado: %s" msgstr "Completado: %s"
#, fuzzy, javascript-format
msgid "Last error: %s"
msgstr "Erro fatal:"
msgid "Idle"
msgstr ""
msgid "In progress"
msgstr ""
#, javascript-format #, javascript-format
msgid "Synchronisation is already in progress. State: %s" msgid "Synchronisation is already in progress. State: %s"
msgstr "Sincronização já em andamento. Estado: %s" msgstr "Sincronização já em andamento. Estado: %s"
msgid "Encrypted" msgid "Encrypted"
msgstr "" msgstr "Encriptado"
#, fuzzy
msgid "Encrypted items cannot be modified" msgid "Encrypted items cannot be modified"
msgstr "Não é possível inicializar o sincronizador." msgstr "Itens encriptados não podem ser modificados"
msgid "Conflicts" msgid "Conflicts"
msgstr "Conflitos" msgstr "Conflitos"
@ -1010,7 +1044,7 @@ msgstr "Esta nota não possui informações de geolocalização."
#, javascript-format #, javascript-format
msgid "Cannot copy note to \"%s\" notebook" msgid "Cannot copy note to \"%s\" notebook"
msgstr "Não é possível copiar a nota para o caderno \"%s\" " msgstr "Não é possível copiar a nota para o caderno \"%s\""
#, javascript-format #, javascript-format
msgid "Cannot move note to \"%s\" notebook" msgid "Cannot move note to \"%s\" notebook"
@ -1044,48 +1078,45 @@ msgstr "Light"
msgid "Dark" msgid "Dark"
msgstr "Dark" msgstr "Dark"
#, fuzzy
msgid "Uncompleted to-dos on top" msgid "Uncompleted to-dos on top"
msgstr "Mostrar tarefas incompletas no topo das listas" msgstr "Mostrar tarefas incompletas no topo"
msgid "Sort notes by" msgid "Sort notes by"
msgstr "" msgstr "Ordenar notas por"
#, fuzzy
msgid "Reverse sort order" msgid "Reverse sort order"
msgstr "Inverte a ordem de classificação." msgstr "Inverter ordem de classificação."
msgid "Save geo-location with notes" msgid "Save geo-location with notes"
msgstr "Salvar geolocalização com notas" msgstr "Salvar geolocalização com notas"
#, fuzzy
msgid "When creating a new to-do:" msgid "When creating a new to-do:"
msgstr "Cria uma nova tarefa." msgstr "Quando criar uma nova tarefa:"
#, fuzzy
msgid "Focus title" msgid "Focus title"
msgstr "Título da nota:" msgstr "Foco no título"
msgid "Focus body" msgid "Focus body"
msgstr "" msgstr "Focar no corpo"
#, fuzzy
msgid "When creating a new note:" msgid "When creating a new note:"
msgstr "Cria uma nova nota." msgstr "Quando criar uma nota nova:"
msgid "Show tray icon" msgid "Show tray icon"
msgstr "" msgstr "Exibir tray icon"
msgid "Global zoom percentage" msgid "Global zoom percentage"
msgstr "" msgstr "Porcentagem global do zoom"
msgid "Editor font family" msgid "Editor font family"
msgstr "" msgstr "Família de fontes do editor"
msgid "" msgid ""
"The font name will not be checked. If incorrect or empty, it will default to " "The font name will not be checked. If incorrect or empty, it will default to "
"a generic monospace font." "a generic monospace font."
msgstr "" msgstr ""
"O nomes da fonte não será verificado. Se estiver incorreto ou vazio, será "
"usado por default uma fonte genérica monospace."
msgid "Automatically update the application" msgid "Automatically update the application"
msgstr "Atualizar automaticamente o aplicativo" msgstr "Atualizar automaticamente o aplicativo"
@ -1115,9 +1146,11 @@ msgid ""
"The target to synchonise to. Each sync target may have additional parameters " "The target to synchonise to. Each sync target may have additional parameters "
"which are named as `sync.NUM.NAME` (all documented below)." "which are named as `sync.NUM.NAME` (all documented below)."
msgstr "" msgstr ""
"O alvo para onde sincronizar. Cada alvo pode ter parâmetros adicionais que "
"são nomeados como `sync.NUM.NAME` (todos documentados abaixo)."
msgid "Directory to synchronise with (absolute path)" msgid "Directory to synchronise with (absolute path)"
msgstr "" msgstr "Diretório para sincronizar (caminho absoluto)"
msgid "" msgid ""
"The path to synchronise with when file system synchronisation is enabled. " "The path to synchronise with when file system synchronisation is enabled. "
@ -1127,74 +1160,80 @@ msgstr ""
"está habilitada. Veja `sync.target`." "está habilitada. Veja `sync.target`."
msgid "Nextcloud WebDAV URL" msgid "Nextcloud WebDAV URL"
msgstr "" msgstr "Nextcloud WebDAV URL"
msgid "Nextcloud username" msgid "Nextcloud username"
msgstr "" msgstr "Usuário da Nextcloud"
msgid "Nextcloud password" msgid "Nextcloud password"
msgstr "" msgstr "Senha da Nextcloud"
msgid "WebDAV URL" msgid "WebDAV URL"
msgstr "" msgstr "WebDAV URL"
msgid "WebDAV username" msgid "WebDAV username"
msgstr "" msgstr "Usuário do WebDAV"
msgid "WebDAV password" msgid "WebDAV password"
msgstr "" msgstr "Senha do WebDAV"
#, javascript-format #, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s." msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Valor da opção inválida: \"%s\". Os valores possíveis são: %s." msgstr "Valor da opção inválida: \"%s\". Os valores possíveis são: %s."
#, fuzzy
msgid "Joplin Export File" msgid "Joplin Export File"
msgstr "Arquivos de Exportação do Evernote" msgstr "Arquivo de Exportação do Joplin"
msgid "Markdown" msgid "Markdown"
msgstr "" msgstr "Markdown"
msgid "Joplin Export Directory" msgid "Joplin Export Directory"
msgstr "" msgstr "Diretório de Exportação do Joplin"
#, fuzzy
msgid "Evernote Export File" msgid "Evernote Export File"
msgstr "Arquivos de Exportação do Evernote" msgstr "Arquivo de Exportação do Evernote"
msgid "Directory"
msgstr "DIretório"
#, javascript-format #, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\"" msgid "Cannot load \"%s\" module for format \"%s\""
msgstr "" msgstr "Não é possível carregar o módulo \"%s\" para o formato \"%s\""
#, javascript-format #, javascript-format
msgid "Please specify import format for %s" msgid "Please specify import format for %s"
msgstr "" msgstr "Favor especificar o formato de importação para %s"
#, javascript-format #, javascript-format
msgid "" msgid ""
"This item is currently encrypted: %s \"%s\". Please wait for all items to be " "This item is currently encrypted: %s \"%s\". Please wait for all items to be "
"decrypted and try again." "decrypted and try again."
msgstr "" msgstr ""
"Este item atualmente está encriptado: %s \"%s\". Favor aguardar que todos os "
"itens sejam decriptados e tente novamente."
msgid "There is no data to export." msgid "There is no data to export."
msgstr "" msgstr "Não há dados a exportar."
#, fuzzy
msgid "Please specify the notebook where the notes should be imported to." msgid "Please specify the notebook where the notes should be imported to."
msgstr "Por favor, primeiro, selecione a nota ou caderno a excluir." msgstr ""
"Por favor, especifique o caderno para onde as notas deveriam ser importadas."
msgid "Items that cannot be synchronised" msgid "Items that cannot be synchronised"
msgstr "" msgstr "Os itens não podem ser sincronizados"
#, fuzzy, javascript-format #, javascript-format
msgid "%s (%s): %s" msgid "%s (%s): %s"
msgstr "%s %s (%s)" msgstr "%s (%s): %s"
msgid "" msgid ""
"These items will remain on the device but will not be uploaded to the sync " "These items will remain on the device but will not be uploaded to the sync "
"target. In order to find these items, either search for the title or the ID " "target. In order to find these items, either search for the title or the ID "
"(which is displayed in brackets above)." "(which is displayed in brackets above)."
msgstr "" msgstr ""
"Estes itens continuarão no dispositivo mas não serão enviados ao alvo de "
"sincronização. Para encontrar esses itens, ou pesquise pelo título ou pelo "
"ID (que é exibido nos colchetes acima)"
msgid "Sync status (synced items / total items)" msgid "Sync status (synced items / total items)"
msgstr "Status de sincronização (sincronizados / totais)" msgstr "Status de sincronização (sincronizados / totais)"
@ -1242,7 +1281,7 @@ msgid "Export Debug Report"
msgstr "Exportar Relatório de Debug" msgstr "Exportar Relatório de Debug"
msgid "Encryption Config" msgid "Encryption Config"
msgstr "" msgstr "Configuração de Encriptação"
msgid "Configuration" msgid "Configuration"
msgstr "Configuração" msgstr "Configuração"
@ -1255,7 +1294,7 @@ msgid "Move %d notes to notebook \"%s\"?"
msgstr "Mover %d notas para o caderno \"%s\"?" msgstr "Mover %d notas para o caderno \"%s\"?"
msgid "Press to set the decryption password." msgid "Press to set the decryption password."
msgstr "" msgstr "Pressione para configurar a senha de decriptação."
msgid "Select date" msgid "Select date"
msgstr "Selecionar data" msgstr "Selecionar data"
@ -1266,23 +1305,31 @@ msgstr "Confirmar"
msgid "Cancel synchronisation" msgid "Cancel synchronisation"
msgstr "Cancelar sincronização" msgstr "Cancelar sincronização"
msgid "New tags:"
msgstr ""
msgid "Type new tags or select from list"
msgstr ""
msgid "Joplin website"
msgstr "Site do Joplin"
#, javascript-format #, javascript-format
msgid "Master Key %s" msgid "Master Key %s"
msgstr "" msgstr "Chave Master %s"
#, fuzzy, javascript-format #, javascript-format
msgid "Created: %s" msgid "Created: %s"
msgstr "Criado: %d." msgstr "Criado: %s"
msgid "Password:" msgid "Password:"
msgstr "" msgstr "Senha:"
msgid "Password cannot be empty" msgid "Password cannot be empty"
msgstr "" msgstr "Senha não pode ser vazia"
#, fuzzy
msgid "Enable" msgid "Enable"
msgstr "Desabilitado" msgstr "Habilitar"
#, javascript-format #, javascript-format
msgid "The notebook could not be saved: %s" msgid "The notebook could not be saved: %s"
@ -1292,10 +1339,10 @@ msgid "Edit notebook"
msgstr "Editar caderno" msgstr "Editar caderno"
msgid "Show all" msgid "Show all"
msgstr "" msgstr "Exibir tudo"
msgid "Errors only" msgid "Errors only"
msgstr "" msgstr "Somente erros"
msgid "This note has been modified:" msgid "This note has been modified:"
msgstr "Esta nota foi modificada:" msgstr "Esta nota foi modificada:"
@ -1350,6 +1397,15 @@ msgstr "Você não possui cadernos. Crie um clicando no botão (+)."
msgid "Welcome" msgid "Welcome"
msgstr "Bem-vindo" msgstr "Bem-vindo"
#~ msgid ""
#~ "Release notes:\n"
#~ "\n"
#~ "%s"
#~ msgstr ""
#~ "Notas da versão:\n"
#~ "\n"
#~ "%s"
#~ msgid "Imports an Evernote notebook file (.enex file)." #~ msgid "Imports an Evernote notebook file (.enex file)."
#~ msgstr "Importa um arquivo de caderno do Evernote (arquivo .enex)." #~ msgstr "Importa um arquivo de caderno do Evernote (arquivo .enex)."

View File

@ -189,17 +189,16 @@ msgstr "Заметка сохранена."
msgid "Exits the application." msgid "Exits the application."
msgstr "Выход из приложения." msgstr "Выход из приложения."
#, fuzzy
msgid "" msgid ""
"Exports Joplin data to the given path. By default, it will export the " "Exports Joplin data to the given path. By default, it will export the "
"complete database including notebooks, notes, tags and resources." "complete database including notebooks, notes, tags and resources."
msgstr "" msgstr ""
"Экспортирует данные Joplin в заданный каталог. По умолчанию экспортируется " "Экспортирует данные Joplin по заданному пути. По умолчанию экспортируется "
"полная база данных, включая блокноты, заметки, теги и ресурсы." "полная база данных, включая блокноты, заметки, теги и ресурсы."
#, fuzzy, javascript-format #, javascript-format
msgid "Destination format: %s" msgid "Destination format: %s"
msgstr "Формат даты" msgstr "Целевой формат: %s"
msgid "Exports only the given note." msgid "Exports only the given note."
msgstr "Экспортирует только заданную заметку." msgstr "Экспортирует только заданную заметку."
@ -215,7 +214,7 @@ msgstr "Выводит информацию об использовании."
#, javascript-format #, javascript-format
msgid "For information on how to customise the shortcuts please visit %s" msgid "For information on how to customise the shortcuts please visit %s"
msgstr "" msgstr "Информацию по настройке сочетаний можно получить, посетив %s"
msgid "Shortcuts are not available in CLI mode." msgid "Shortcuts are not available in CLI mode."
msgstr "Ярлыки недоступны в режиме командной строки." msgstr "Ярлыки недоступны в режиме командной строки."
@ -259,19 +258,18 @@ msgstr "Чтобы войти в режим командной строки, н
msgid "To exit command line mode, press ESCAPE" msgid "To exit command line mode, press ESCAPE"
msgstr "Чтобы выйти из режима командной строки, нажмите ESCAPE" msgstr "Чтобы выйти из режима командной строки, нажмите ESCAPE"
#, fuzzy
msgid "" msgid ""
"For the list of keyboard shortcuts and config options, type `help keymap`" "For the list of keyboard shortcuts and config options, type `help keymap`"
msgstr "" msgstr ""
"Для просмотра списка доступных клавиатурных сочетаний введите `help " "Для просмотра списка клавиатурных сочетаний и настроек конфигурации введите "
"shortcuts`" "`help keymap`"
msgid "Imports data into Joplin." msgid "Imports data into Joplin."
msgstr "" msgstr "Импортирует данные в Joplin."
#, fuzzy, javascript-format #, javascript-format
msgid "Source format: %s" msgid "Source format: %s"
msgstr "Нет такой команды: %s" msgstr "Исходный формат: %s"
msgid "Do not ask for confirmation." msgid "Do not ask for confirmation."
msgstr "Не запрашивать подтверждение." msgstr "Не запрашивать подтверждение."
@ -507,6 +505,9 @@ msgstr "По умолчанию: %s"
msgid "Possible keys/values:" msgid "Possible keys/values:"
msgstr "Возможные ключи/значения:" msgstr "Возможные ключи/значения:"
msgid "Type `joplin help` for usage information."
msgstr "Введите `joplin help` для получения информации об использовании."
msgid "Fatal error:" msgid "Fatal error:"
msgstr "Фатальная ошибка:" msgstr "Фатальная ошибка:"
@ -562,17 +563,17 @@ msgstr ""
#, javascript-format #, javascript-format
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..." msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr "Экспорт в «%s» в формате «%s». Пожалуйста, ожидайте..."
msgid "File"
msgstr "Файл"
msgid "Directory"
msgstr ""
#, javascript-format #, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..." msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr "Импорт из «%s» в формате «%s». Пожалуйста, ожидайте..."
msgid "PDF File"
msgstr "PDF-файл"
msgid "File"
msgstr "Файл"
msgid "New note" msgid "New note"
msgstr "Новая заметка" msgstr "Новая заметка"
@ -586,13 +587,15 @@ msgstr "Новый блокнот"
msgid "Import" msgid "Import"
msgstr "Импорт" msgstr "Импорт"
#, fuzzy
msgid "Export" msgid "Export"
msgstr "Импорт" msgstr "Экспорт"
msgid "Print"
msgstr "Печать"
#, javascript-format #, javascript-format
msgid "Hide %s" msgid "Hide %s"
msgstr "" msgstr "Скрыть %s"
msgid "Quit" msgid "Quit"
msgstr "Выход" msgstr "Выход"
@ -613,10 +616,10 @@ msgid "Search in all the notes"
msgstr "Поиск во всех заметках" msgstr "Поиск во всех заметках"
msgid "View" msgid "View"
msgstr "" msgstr "Вид"
msgid "Toggle editor layout" msgid "Toggle editor layout"
msgstr "" msgstr "Переключить вид редактора"
msgid "Tools" msgid "Tools"
msgstr "Инструменты" msgstr "Инструменты"
@ -636,6 +639,9 @@ msgstr "Помощь"
msgid "Website and documentation" msgid "Website and documentation"
msgstr "Сайт и документация" msgstr "Сайт и документация"
msgid "Make a donation"
msgstr "Сделать пожертвование"
msgid "Check for updates..." msgid "Check for updates..."
msgstr "Проверить обновления..." msgstr "Проверить обновления..."
@ -646,12 +652,12 @@ msgstr "О Joplin"
msgid "%s %s (%s, %s)" msgid "%s %s (%s, %s)"
msgstr "%s %s (%s, %s)" msgstr "%s %s (%s, %s)"
#, fuzzy, javascript-format #, javascript-format
msgid "Open %s" msgid "Open %s"
msgstr "В %s: %s" msgstr "Открыть %s"
msgid "Exit" msgid "Exit"
msgstr "" msgstr "Выход"
msgid "OK" msgid "OK"
msgstr "OK" msgstr "OK"
@ -659,30 +665,20 @@ msgstr "OK"
msgid "Cancel" msgid "Cancel"
msgstr "Отмена" msgstr "Отмена"
#, fuzzy, javascript-format
msgid ""
"Release notes:\n"
"\n"
"%s"
msgstr "Удалить заметки?"
#, fuzzy
msgid "An update is available, do you want to download it now?"
msgstr "Доступно обновление. Обновить сейчас?"
msgid "Yes"
msgstr ""
#, fuzzy
msgid "No"
msgstr "N"
msgid "Current version is up-to-date." msgid "Current version is up-to-date."
msgstr "Вы используете самую свежую версию." msgstr "Вы используете самую свежую версию."
#, fuzzy msgid "An update is available, do you want to download it now?"
msgstr "Доступно обновление. Желаете скачать его сейчас?"
msgid "Yes"
msgstr "Да"
msgid "No"
msgstr "Нет"
msgid "Check synchronisation configuration" msgid "Check synchronisation configuration"
msgstr "Отменить синхронизацию" msgstr "Проверить настройки синхронизации"
#, javascript-format #, javascript-format
msgid "Notes and settings are stored in: %s" msgid "Notes and settings are stored in: %s"
@ -752,15 +748,17 @@ msgstr ""
"ключей, в зависимости от того, как изначально были зашифрованы заметки или " "ключей, в зависимости от того, как изначально были зашифрованы заметки или "
"блокноты." "блокноты."
#, fuzzy
msgid "Missing Master Keys" msgid "Missing Master Keys"
msgstr "Мастер-ключи" msgstr "Недостающие мастер-ключи"
msgid "" msgid ""
"The master keys with these IDs are used to encrypt some of your items, " "The master keys with these IDs are used to encrypt some of your items, "
"however the application does not currently have access to them. It is likely " "however the application does not currently have access to them. It is likely "
"they will eventually be downloaded via synchronisation." "they will eventually be downloaded via synchronisation."
msgstr "" msgstr ""
"Мастер-ключи с такими ID используются для шифрования некоторых из ваших "
"элементов, однако у приложения сейчас нет к ним доступа. Скорее всего, они "
"загрузятся при синхронизации."
msgid "Status" msgid "Status"
msgstr "Статус" msgstr "Статус"
@ -835,11 +833,10 @@ msgid ""
msgstr "Сейчас здесь нет блокнотов. Создайте новый нажав «Новый блокнот»." msgstr "Сейчас здесь нет блокнотов. Создайте новый нажав «Новый блокнот»."
msgid "Open..." msgid "Open..."
msgstr "" msgstr "Открыть..."
#, fuzzy
msgid "Save as..." msgid "Save as..."
msgstr "Сохранить изменения" msgstr "Сохранить как..."
#, javascript-format #, javascript-format
msgid "Unsupported link or message: %s" msgid "Unsupported link or message: %s"
@ -859,18 +856,18 @@ msgid ""
"This note has no content. Click on \"%s\" to toggle the editor and edit the " "This note has no content. Click on \"%s\" to toggle the editor and edit the "
"note." "note."
msgstr "" msgstr ""
"Заметка пуста. Нажмите на «%s», чтобы переключиться в редактор и "
"отредактировать её."
#, fuzzy
msgid "to-do" msgid "to-do"
msgstr "Новая задача" msgstr "задача"
#, fuzzy
msgid "note" msgid "note"
msgstr "Новая заметка" msgstr "заметка"
#, fuzzy, javascript-format #, javascript-format
msgid "Creating new %s..." msgid "Creating new %s..."
msgstr "Импорт заметок..." msgstr "Создание новой %s..."
msgid "Refresh" msgid "Refresh"
msgstr "Обновить" msgstr "Обновить"
@ -922,9 +919,8 @@ msgstr "Неизвестный флаг: %s"
msgid "File system" msgid "File system"
msgstr "Файловая система" msgstr "Файловая система"
#, fuzzy
msgid "Nextcloud" msgid "Nextcloud"
msgstr "Nextcloud (Beta)" msgstr "Nextcloud"
msgid "OneDrive" msgid "OneDrive"
msgstr "OneDrive" msgstr "OneDrive"
@ -932,9 +928,8 @@ msgstr "OneDrive"
msgid "OneDrive Dev (For testing only)" msgid "OneDrive Dev (For testing only)"
msgstr "OneDrive Dev (только для тестирования)" msgstr "OneDrive Dev (только для тестирования)"
#, fuzzy
msgid "WebDAV" msgid "WebDAV"
msgstr "Nextcloud WebDAV URL" msgstr "WebDAV"
#, javascript-format #, javascript-format
msgid "Unknown log level: %s" msgid "Unknown log level: %s"
@ -998,8 +993,8 @@ msgstr "Удалено удалённых элементов: %d."
msgid "Fetched items: %d/%d." msgid "Fetched items: %d/%d."
msgstr "Получено элементов: %d/%d." msgstr "Получено элементов: %d/%d."
#, javascript-format #, fuzzy, javascript-format
msgid "State: \"%s\"." msgid "State: %s."
msgstr "Статус: «%s»." msgstr "Статус: «%s»."
msgid "Cancelling..." msgid "Cancelling..."
@ -1009,6 +1004,16 @@ msgstr "Отмена..."
msgid "Completed: %s" msgid "Completed: %s"
msgstr "Завершено: %s" msgstr "Завершено: %s"
#, javascript-format
msgid "Last error: %s"
msgstr "Последняя ошибка: %s"
msgid "Idle"
msgstr "Простой"
msgid "In progress"
msgstr "Выполнение"
#, javascript-format #, javascript-format
msgid "Synchronisation is already in progress. State: %s" msgid "Synchronisation is already in progress. State: %s"
msgstr "Синхронизация уже выполняется. Статус: %s" msgstr "Синхронизация уже выполняется. Статус: %s"
@ -1072,16 +1077,14 @@ msgstr "Светлая"
msgid "Dark" msgid "Dark"
msgstr "Тёмная" msgstr "Тёмная"
#, fuzzy
msgid "Uncompleted to-dos on top" msgid "Uncompleted to-dos on top"
msgstr "Показывать незавершённые задачи вверху списков" msgstr "Незавершённые задачи сверху"
msgid "Sort notes by" msgid "Sort notes by"
msgstr "" msgstr "Сортировать заметки по"
#, fuzzy
msgid "Reverse sort order" msgid "Reverse sort order"
msgstr "Обращает порядок сортировки." msgstr "Обратный порядок сортировки"
msgid "Save geo-location with notes" msgid "Save geo-location with notes"
msgstr "Сохранять информацию о геолокации в заметках" msgstr "Сохранять информацию о геолокации в заметках"
@ -1099,19 +1102,20 @@ msgid "When creating a new note:"
msgstr "При создании новой заметки:" msgstr "При создании новой заметки:"
msgid "Show tray icon" msgid "Show tray icon"
msgstr "" msgstr "Показывать иконку в панели задач"
#, fuzzy
msgid "Global zoom percentage" msgid "Global zoom percentage"
msgstr "Масштаб приложения в процентах" msgstr "Глобальный масштаб в процентах"
msgid "Editor font family" msgid "Editor font family"
msgstr "" msgstr "Семейство шрифтов редактора"
msgid "" msgid ""
"The font name will not be checked. If incorrect or empty, it will default to " "The font name will not be checked. If incorrect or empty, it will default to "
"a generic monospace font." "a generic monospace font."
msgstr "" msgstr ""
"Название шрифта не проверяется. Если оно указано некорректно или не задано, "
"будет использоваться стандартный моноширинный шрифт."
msgid "Automatically update the application" msgid "Automatically update the application"
msgstr "Автоматически обновлять приложение" msgstr "Автоматически обновлять приложение"
@ -1163,56 +1167,56 @@ msgstr "Имя пользователя Nextcloud"
msgid "Nextcloud password" msgid "Nextcloud password"
msgstr "Пароль Nextcloud" msgstr "Пароль Nextcloud"
#, fuzzy
msgid "WebDAV URL" msgid "WebDAV URL"
msgstr "Nextcloud WebDAV URL" msgstr "URL WebDAV"
#, fuzzy
msgid "WebDAV username" msgid "WebDAV username"
msgstr "Имя пользователя Nextcloud" msgstr "Имя пользователя WebDAV"
#, fuzzy
msgid "WebDAV password" msgid "WebDAV password"
msgstr "Установить пароль" msgstr "Пароль WebDAV"
#, javascript-format #, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s." msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Неверное значение параметра: «%s». Доступные значения: %s." msgstr "Неверное значение параметра: «%s». Доступные значения: %s."
#, fuzzy
msgid "Joplin Export File" msgid "Joplin Export File"
msgstr "Файлы экспорта Evernote" msgstr "Файл экспорта Joplin"
msgid "Markdown" msgid "Markdown"
msgstr "" msgstr "Markdown"
msgid "Joplin Export Directory" msgid "Joplin Export Directory"
msgstr "" msgstr "Папка экспорта Joplin"
#, fuzzy
msgid "Evernote Export File" msgid "Evernote Export File"
msgstr "Файлы экспорта Evernote" msgstr "Файл экспорта Evernote"
msgid "Directory"
msgstr "Директория"
#, javascript-format #, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\"" msgid "Cannot load \"%s\" module for format \"%s\""
msgstr "" msgstr "Не удалось загрузить модуль «%s» для формата «%s»"
#, javascript-format #, javascript-format
msgid "Please specify import format for %s" msgid "Please specify import format for %s"
msgstr "" msgstr "Пожалуйста, укажите формат импорта для %s"
#, javascript-format #, javascript-format
msgid "" msgid ""
"This item is currently encrypted: %s \"%s\". Please wait for all items to be " "This item is currently encrypted: %s \"%s\". Please wait for all items to be "
"decrypted and try again." "decrypted and try again."
msgstr "" msgstr ""
"Этот элемент сейчас зашифрован: %s «%s». Пожалуйста, дождитесь расшифровки "
"всех элементов и попробуйте снова."
msgid "There is no data to export." msgid "There is no data to export."
msgstr "" msgstr "Нет данных для экспорта."
#, fuzzy
msgid "Please specify the notebook where the notes should be imported to." msgid "Please specify the notebook where the notes should be imported to."
msgstr "Выберите, куда должен быть экспортирован статус синхронизации" msgstr ""
"Пожалуйста, укажите блокнот, в который должны быть импортированы заметки."
msgid "Items that cannot be synchronised" msgid "Items that cannot be synchronised"
msgstr "Элементы, которые не могут быть синхронизированы" msgstr "Элементы, которые не могут быть синхронизированы"
@ -1300,6 +1304,15 @@ msgstr "Подтвердить"
msgid "Cancel synchronisation" msgid "Cancel synchronisation"
msgstr "Отменить синхронизацию" msgstr "Отменить синхронизацию"
msgid "New tags:"
msgstr ""
msgid "Type new tags or select from list"
msgstr ""
msgid "Joplin website"
msgstr "Сайт Joplin"
#, javascript-format #, javascript-format
msgid "Master Key %s" msgid "Master Key %s"
msgstr "Мастер-ключ %s" msgstr "Мастер-ключ %s"
@ -1325,11 +1338,10 @@ msgid "Edit notebook"
msgstr "Редактировать блокнот" msgstr "Редактировать блокнот"
msgid "Show all" msgid "Show all"
msgstr "" msgstr "Показать всё"
#, fuzzy
msgid "Errors only" msgid "Errors only"
msgstr "Ошибка" msgstr "Только ошибки"
msgid "This note has been modified:" msgid "This note has been modified:"
msgstr "Эта заметка была изменена:" msgstr "Эта заметка была изменена:"
@ -1384,6 +1396,13 @@ msgstr "У вас сейчас нет блокнота. Создайте его
msgid "Welcome" msgid "Welcome"
msgstr "Добро пожаловать" msgstr "Добро пожаловать"
#, fuzzy
#~ msgid ""
#~ "Release notes:\n"
#~ "\n"
#~ "%s"
#~ msgstr "Удалить заметки?"
#~ msgid "Imports an Evernote notebook file (.enex file)." #~ msgid "Imports an Evernote notebook file (.enex file)."
#~ msgstr "Импортирует файл блокнотов Evernote (.enex-файл)." #~ msgstr "Импортирует файл блокнотов Evernote (.enex-файл)."

View File

@ -471,6 +471,10 @@ msgstr "默认值: %s"
msgid "Possible keys/values:" msgid "Possible keys/values:"
msgstr "可用键/值:" msgstr "可用键/值:"
#, fuzzy
msgid "Type `joplin help` for usage information."
msgstr "显示使用信息。"
msgid "Fatal error:" msgid "Fatal error:"
msgstr "严重错误:" msgstr "严重错误:"
@ -515,16 +519,17 @@ msgstr ""
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..." msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr ""
msgid "File"
msgstr "文件"
msgid "Directory"
msgstr ""
#, javascript-format #, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..." msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr "" msgstr ""
#, fuzzy
msgid "PDF File"
msgstr "文件"
msgid "File"
msgstr "文件"
msgid "New note" msgid "New note"
msgstr "新笔记" msgstr "新笔记"
@ -541,6 +546,9 @@ msgstr "导入"
msgid "Export" msgid "Export"
msgstr "导入" msgstr "导入"
msgid "Print"
msgstr ""
#, javascript-format #, javascript-format
msgid "Hide %s" msgid "Hide %s"
msgstr "" msgstr ""
@ -588,6 +596,10 @@ msgstr "帮助"
msgid "Website and documentation" msgid "Website and documentation"
msgstr "网站与文档" msgstr "网站与文档"
#, fuzzy
msgid "Make a donation"
msgstr "网站与文档"
msgid "Check for updates..." msgid "Check for updates..."
msgstr "" msgstr ""
@ -611,12 +623,8 @@ msgstr "确认"
msgid "Cancel" msgid "Cancel"
msgstr "取消" msgstr "取消"
#, fuzzy, javascript-format msgid "Current version is up-to-date."
msgid "" msgstr ""
"Release notes:\n"
"\n"
"%s"
msgstr "是否删除笔记?"
msgid "An update is available, do you want to download it now?" msgid "An update is available, do you want to download it now?"
msgstr "" msgstr ""
@ -628,9 +636,6 @@ msgstr ""
msgid "No" msgid "No"
msgstr "否" msgstr "否"
msgid "Current version is up-to-date."
msgstr ""
#, fuzzy #, fuzzy
msgid "Check synchronisation configuration" msgid "Check synchronisation configuration"
msgstr "取消同步" msgstr "取消同步"
@ -936,8 +941,8 @@ msgstr "已删除远程项目: %d。"
msgid "Fetched items: %d/%d." msgid "Fetched items: %d/%d."
msgstr "已新建本地项目: %d。" msgstr "已新建本地项目: %d。"
#, javascript-format #, fuzzy, javascript-format
msgid "State: \"%s\"." msgid "State: %s."
msgstr "状态:\"%s\"。" msgstr "状态:\"%s\"。"
msgid "Cancelling..." msgid "Cancelling..."
@ -947,6 +952,16 @@ msgstr "正在取消..."
msgid "Completed: %s" msgid "Completed: %s"
msgstr "已完成:\"%s\"" msgstr "已完成:\"%s\""
#, fuzzy, javascript-format
msgid "Last error: %s"
msgstr "严重错误:"
msgid "Idle"
msgstr ""
msgid "In progress"
msgstr ""
#, javascript-format #, javascript-format
msgid "Synchronisation is already in progress. State: %s" msgid "Synchronisation is already in progress. State: %s"
msgstr "同步正在进行中。状态:%s" msgstr "同步正在进行中。状态:%s"
@ -1125,6 +1140,9 @@ msgstr ""
msgid "Evernote Export File" msgid "Evernote Export File"
msgstr "Evernote导出文件" msgstr "Evernote导出文件"
msgid "Directory"
msgstr ""
#, javascript-format #, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\"" msgid "Cannot load \"%s\" module for format \"%s\""
msgstr "" msgstr ""
@ -1229,6 +1247,15 @@ msgstr "确认"
msgid "Cancel synchronisation" msgid "Cancel synchronisation"
msgstr "取消同步" msgstr "取消同步"
msgid "New tags:"
msgstr ""
msgid "Type new tags or select from list"
msgstr ""
msgid "Joplin website"
msgstr ""
#, javascript-format #, javascript-format
msgid "Master Key %s" msgid "Master Key %s"
msgstr "" msgstr ""
@ -1311,6 +1338,13 @@ msgstr "您当前没有任何笔记本。点击(+)按钮创建新笔记本。"
msgid "Welcome" msgid "Welcome"
msgstr "欢迎" msgstr "欢迎"
#, fuzzy
#~ msgid ""
#~ "Release notes:\n"
#~ "\n"
#~ "%s"
#~ msgstr "是否删除笔记?"
#~ msgid "Imports an Evernote notebook file (.enex file)." #~ msgid "Imports an Evernote notebook file (.enex file)."
#~ msgstr "导入Evernote笔记本文件(.enex文件)。" #~ msgstr "导入Evernote笔记本文件(.enex文件)。"

View File

@ -1,6 +1,6 @@
{ {
"name": "joplin", "name": "joplin",
"version": "1.0.100", "version": "1.0.104",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -19,7 +19,7 @@
], ],
"owner": "Laurent Cozic" "owner": "Laurent Cozic"
}, },
"version": "1.0.100", "version": "1.0.104",
"bin": { "bin": {
"joplin": "./main.js" "joplin": "./main.js"
}, },

View File

@ -0,0 +1,99 @@
require('app-module-path').addPath(__dirname);
const { time } = require('lib/time-utils.js');
const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
const InteropService = require('lib/services/InteropService.js');
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const Tag = require('lib/models/Tag.js');
const NoteTag = require('lib/models/NoteTag.js');
const Resource = require('lib/models/Resource.js');
const NoteResource = require('lib/models/NoteResource.js');
const ResourceService = require('lib/services/ResourceService.js');
const fs = require('fs-extra');
const ArrayUtils = require('lib/ArrayUtils');
const ObjectUtils = require('lib/ObjectUtils');
const { shim } = require('lib/shim.js');
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
function exportDir() {
return __dirname + '/export';
}
function fieldsEqual(model1, model2, fieldNames) {
for (let i = 0; i < fieldNames.length; i++) {
const f = fieldNames[i];
expect(model1[f]).toBe(model2[f], 'For key ' + f);
}
}
describe('services_ResourceService', function() {
beforeEach(async (done) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
});
it('should delete orphaned resources', asyncTest(async () => {
const service = new ResourceService();
let folder1 = await Folder.save({ title: "folder1" });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
note1 = await shim.attachFileToNote(note1, __dirname + '/../tests/support/photo.jpg');
let resource1 = (await Resource.all())[0];
const resourcePath = Resource.fullPath(resource1);
await service.indexNoteResources();
await service.deleteOrphanResources(0);
expect(!!(await Resource.load(resource1.id))).toBe(true);
await Note.delete(note1.id);
await service.deleteOrphanResources(0);
expect(!!(await Resource.load(resource1.id))).toBe(true);
await service.indexNoteResources();
await service.deleteOrphanResources(1000 * 60);
expect(!!(await Resource.load(resource1.id))).toBe(true);
await service.deleteOrphanResources(0);
expect(!!(await Resource.load(resource1.id))).toBe(false);
expect(await shim.fsDriver().exists(resourcePath)).toBe(false);
expect(!(await NoteResource.all()).length).toBe(true);
}));
it('should not delete resource if still associated with at least one note', asyncTest(async () => {
const service = new ResourceService();
let folder1 = await Folder.save({ title: "folder1" });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
let note2 = await Note.save({ title: 'ma deuxième note', parent_id: folder1.id });
note1 = await shim.attachFileToNote(note1, __dirname + '/../tests/support/photo.jpg');
let resource1 = (await Resource.all())[0];
const resourcePath = Resource.fullPath(resource1);
await service.indexNoteResources();
await Note.delete(note1.id);
await service.indexNoteResources();
await Note.save({ id: note2.id, body: Resource.markdownTag(resource1) });
await service.indexNoteResources();
await service.deleteOrphanResources(0);
expect(!!(await Resource.load(resource1.id))).toBe(true);
}));
});

View File

@ -119,7 +119,7 @@ describe('Synchronizer', function() {
await localItemsSameAsRemote(all, expect); await localItemsSameAsRemote(all, expect);
})); }));
it('should update remote item', asyncTest(async () => { it('should update remote items', asyncTest(async () => {
let folder = await Folder.save({ title: "folder1" }); let folder = await Folder.save({ title: "folder1" });
let note = await Note.save({ title: "un", parent_id: folder.id }); let note = await Note.save({ title: "un", parent_id: folder.id });
await synchronizer().start(); await synchronizer().start();
@ -883,6 +883,37 @@ describe('Synchronizer', function() {
expect(fileContentEqual(resourcePath1, resourcePath1_2)).toBe(true); expect(fileContentEqual(resourcePath1, resourcePath1_2)).toBe(true);
})); }));
it('should delete resources', asyncTest(async () => {
while (insideBeforeEach) await time.msleep(500);
let folder1 = await Folder.save({ title: "folder1" });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
await shim.attachFileToNote(note1, __dirname + '/../tests/support/photo.jpg');
let resource1 = (await Resource.all())[0];
let resourcePath1 = Resource.fullPath(resource1);
await synchronizer().start();
await switchClient(2);
await synchronizer().start();
let allResources = await Resource.all();
expect(allResources.length).toBe(1);
let all = await fileApi().list();
expect(all.items.length).toBe(3);
await Resource.delete(resource1.id);
await synchronizer().start();
all = await fileApi().list();
expect(all.items.length).toBe(2);
await switchClient(1);
expect(await shim.fsDriver().exists(resourcePath1)).toBe(true);
await synchronizer().start();
allResources = await Resource.all();
expect(allResources.length).toBe(0);
expect(await shim.fsDriver().exists(resourcePath1)).toBe(false);
}));
it('should encryt resources', asyncTest(async () => { it('should encryt resources', asyncTest(async () => {
Setting.setValue('encryption.enabled', true); Setting.setValue('encryption.enabled', true);
const masterKey = await loadEncryptionMasterKey(); const masterKey = await loadEncryptionMasterKey();
@ -1006,4 +1037,29 @@ describe('Synchronizer', function() {
await localItemsSameAsRemote(all, expect); await localItemsSameAsRemote(all, expect);
})); }));
}); it("should update remote items but not pull remote changes", asyncTest(async () => {
let folder = await Folder.save({ title: "folder1" });
let note = await Note.save({ title: "un", parent_id: folder.id });
await synchronizer().start();
await switchClient(2);
await synchronizer().start();
await Note.save({ title: "deux", parent_id: folder.id });
await synchronizer().start();
await switchClient(1);
await Note.save({ title: "un UPDATE", id: note.id });
await synchronizer().start({ syncSteps: ["update_remote"] });
let all = await allItems();
expect(all.length).toBe(2);
await switchClient(2);
await synchronizer().start();
let note2 = await Note.load(note.id);
expect(note2.title).toBe("un UPDATE");
}));
});

View File

@ -16,6 +16,7 @@ const { FileApi } = require('lib/file-api.js');
const { FileApiDriverMemory } = require('lib/file-api-driver-memory.js'); const { FileApiDriverMemory } = require('lib/file-api-driver-memory.js');
const { FileApiDriverLocal } = require('lib/file-api-driver-local.js'); const { FileApiDriverLocal } = require('lib/file-api-driver-local.js');
const { FileApiDriverWebDav } = require('lib/file-api-driver-webdav.js'); const { FileApiDriverWebDav } = require('lib/file-api-driver-webdav.js');
const BaseService = require('lib/services/BaseService.js');
const { FsDriverNode } = require('lib/fs-driver-node.js'); const { FsDriverNode } = require('lib/fs-driver-node.js');
const { time } = require('lib/time-utils.js'); const { time } = require('lib/time-utils.js');
const { shimInit } = require('lib/shim-init-node.js'); const { shimInit } = require('lib/shim-init-node.js');
@ -51,8 +52,8 @@ SyncTargetRegistry.addClass(SyncTargetFilesystem);
SyncTargetRegistry.addClass(SyncTargetOneDrive); SyncTargetRegistry.addClass(SyncTargetOneDrive);
SyncTargetRegistry.addClass(SyncTargetNextcloud); SyncTargetRegistry.addClass(SyncTargetNextcloud);
const syncTargetId_ = SyncTargetRegistry.nameToId('nextcloud'); // const syncTargetId_ = SyncTargetRegistry.nameToId("nextcloud");
//const syncTargetId_ = SyncTargetRegistry.nameToId('memory'); const syncTargetId_ = SyncTargetRegistry.nameToId("memory");
//const syncTargetId_ = SyncTargetRegistry.nameToId('filesystem'); //const syncTargetId_ = SyncTargetRegistry.nameToId('filesystem');
const syncDir = __dirname + '/../tests/sync'; const syncDir = __dirname + '/../tests/sync';
@ -63,7 +64,7 @@ console.info('Testing with sync target: ' + SyncTargetRegistry.idToName(syncTarg
const logger = new Logger(); const logger = new Logger();
logger.addTarget('console'); logger.addTarget('console');
logger.addTarget('file', { path: logDir + '/log.txt' }); logger.addTarget('file', { path: logDir + '/log.txt' });
logger.setLevel(Logger.LEVEL_WARN); // Set to INFO to display sync process in console logger.setLevel(Logger.LEVEL_WARN); // Set to DEBUG to display sync process in console
BaseItem.loadClass('Note', Note); BaseItem.loadClass('Note', Note);
BaseItem.loadClass('Folder', Folder); BaseItem.loadClass('Folder', Folder);
@ -75,6 +76,8 @@ BaseItem.loadClass('MasterKey', MasterKey);
Setting.setConstant('appId', 'net.cozic.joplin-cli'); Setting.setConstant('appId', 'net.cozic.joplin-cli');
Setting.setConstant('appType', 'cli'); Setting.setConstant('appType', 'cli');
BaseService.logger_ = logger;
Setting.autoSaveEnabled = false; Setting.autoSaveEnabled = false;
function syncTargetId() { function syncTargetId() {
@ -118,8 +121,9 @@ async function clearDatabase(id = null) {
'DELETE FROM tags', 'DELETE FROM tags',
'DELETE FROM note_tags', 'DELETE FROM note_tags',
'DELETE FROM master_keys', 'DELETE FROM master_keys',
'DELETE FROM settings', 'DELETE FROM item_changes',
'DELETE FROM note_resources',
'DELETE FROM settings',
'DELETE FROM deleted_items', 'DELETE FROM deleted_items',
'DELETE FROM sync_items', 'DELETE FROM sync_items',
]; ];

View File

@ -110,10 +110,14 @@ class ElectronAppWrapper {
}); });
} }
async exit() { async quit() {
this.electronApp_.quit(); this.electronApp_.quit();
} }
exit(errorCode = 0) {
this.electronApp_.exit(errorCode);
}
trayShown() { trayShown() {
return !!this.tray_; return !!this.tray_;
} }

View File

@ -22,6 +22,7 @@ const AlarmServiceDriverNode = require('lib/services/AlarmServiceDriverNode');
const DecryptionWorker = require('lib/services/DecryptionWorker'); const DecryptionWorker = require('lib/services/DecryptionWorker');
const InteropService = require('lib/services/InteropService'); const InteropService = require('lib/services/InteropService');
const InteropServiceHelper = require('./InteropServiceHelper.js'); const InteropServiceHelper = require('./InteropServiceHelper.js');
const ResourceService = require('lib/services/ResourceService');
const { bridge } = require('electron').remote.require('./bridge'); const { bridge } = require('electron').remote.require('./bridge');
const Menu = bridge().Menu; const Menu = bridge().Menu;
@ -150,8 +151,8 @@ class Application extends BaseApplication {
this.updateEditorFont(); this.updateEditorFont();
} }
if (['NOTE_UPDATE_ONE', 'NOTE_DELETE', 'FOLDER_UPDATE_ONE', 'FOLDER_DELETE'].indexOf(action.type) >= 0) { if (["NOTE_UPDATE_ONE", "NOTE_DELETE", "FOLDER_UPDATE_ONE", "FOLDER_DELETE"].indexOf(action.type) >= 0) {
if (!await reg.syncTarget().syncStarted()) reg.scheduleSync(); if (!await reg.syncTarget().syncStarted()) reg.scheduleSync(5, { syncSteps: ["update_remote", "delete_remote"] });
} }
if (['EVENT_NOTE_ALARM_FIELD_CHANGE', 'NOTE_DELETE'].indexOf(action.type) >= 0) { if (['EVENT_NOTE_ALARM_FIELD_CHANGE', 'NOTE_DELETE'].indexOf(action.type) >= 0) {
@ -205,7 +206,7 @@ class Application extends BaseApplication {
const module = ioModules[i]; const module = ioModules[i];
if (module.type === 'exporter') { if (module.type === 'exporter') {
exportItems.push({ exportItems.push({
label: module.format + ' - ' + module.description, label: module.fullLabel(),
screens: ['Main'], screens: ['Main'],
click: async () => { click: async () => {
await InteropServiceHelper.export(this.dispatch.bind(this), module); await InteropServiceHelper.export(this.dispatch.bind(this), module);
@ -214,12 +215,8 @@ class Application extends BaseApplication {
} else { } else {
for (let j = 0; j < module.sources.length; j++) { for (let j = 0; j < module.sources.length; j++) {
const moduleSource = module.sources[j]; const moduleSource = module.sources[j];
let label = [module.format + ' - ' + module.description];
if (module.sources.length > 1) {
label.push('(' + (moduleSource === 'file' ? _('File') : _('Directory')) + ')');
}
importItems.push({ importItems.push({
label: label.join(' '), label: module.fullLabel(moduleSource),
screens: ['Main'], screens: ['Main'],
click: async () => { click: async () => {
let path = null; let path = null;
@ -269,6 +266,17 @@ class Application extends BaseApplication {
} }
} }
exportItems.push({
label: 'PDF - ' + _('PDF File'),
screens: ['Main'],
click: async () => {
this.dispatch({
type: 'WINDOW_COMMAND',
name: 'exportPdf',
});
}
});
const template = [ const template = [
{ {
label: _('File'), label: _('File'),
@ -304,31 +312,24 @@ class Application extends BaseApplication {
} }
}, { }, {
type: 'separator', type: 'separator',
// }, {
// label: _('Import Evernote notes'),
// click: () => {
// const filePaths = bridge().showOpenDialog({
// properties: ['openFile', 'createDirectory'],
// filters: [
// { name: _('Evernote Export Files'), extensions: ['enex'] },
// ]
// });
// if (!filePaths || !filePaths.length) return;
// this.dispatch({
// type: 'NAV_GO',
// routeName: 'Import',
// props: {
// filePath: filePaths[0],
// },
// });
// }
}, { }, {
label: _('Import'), label: _('Import'),
submenu: importItems, submenu: importItems,
}, { }, {
label: _('Export'), label: _('Export'),
submenu: exportItems, submenu: exportItems,
}, {
type: 'separator',
}, {
label: _('Print'),
accelerator: 'CommandOrControl+P',
screens: ['Main'],
click: () => {
this.dispatch({
type: 'WINDOW_COMMAND',
name: 'print',
});
}
}, { }, {
type: 'separator', type: 'separator',
platforms: ['darwin'], platforms: ['darwin'],
@ -342,7 +343,7 @@ class Application extends BaseApplication {
}, { }, {
label: _('Quit'), label: _('Quit'),
accelerator: 'CommandOrControl+Q', accelerator: 'CommandOrControl+Q',
click: () => { bridge().electronApp().exit() } click: () => { bridge().electronApp().quit() }
}] }]
}, { }, {
label: _('Edit'), label: _('Edit'),
@ -367,11 +368,11 @@ class Application extends BaseApplication {
}, { }, {
label: _('Search in all the notes'), label: _('Search in all the notes'),
screens: ['Main'], screens: ['Main'],
accelerator: 'F6', accelerator: 'CommandOrControl+F',
click: () => { click: () => {
this.dispatch({ this.dispatch({
type: 'WINDOW_COMMAND', type: 'WINDOW_COMMAND',
name: 'search', name: 'focus_search',
}); });
}, },
}], }],
@ -448,11 +449,17 @@ class Application extends BaseApplication {
label: _('Website and documentation'), label: _('Website and documentation'),
accelerator: 'F1', accelerator: 'F1',
click () { bridge().openExternal('http://joplin.cozic.net') } click () { bridge().openExternal('http://joplin.cozic.net') }
}, {
label: _('Make a donation'),
click () { bridge().openExternal('http://joplin.cozic.net/donate') }
}, { }, {
label: _('Check for updates...'), label: _('Check for updates...'),
click: () => { click: () => {
bridge().checkForUpdates(false, bridge().window(), this.checkForUpdateLoggerPath()); bridge().checkForUpdates(false, bridge().window(), this.checkForUpdateLoggerPath());
} }
}, {
type: 'separator',
screens: ['Main'],
}, { }, {
label: _('About Joplin'), label: _('About Joplin'),
click: () => { click: () => {
@ -518,7 +525,7 @@ class Application extends BaseApplication {
const contextMenu = Menu.buildFromTemplate([ const contextMenu = Menu.buildFromTemplate([
{ label: _('Open %s', app.electronApp().getName()), click: () => { app.window().show(); } }, { label: _('Open %s', app.electronApp().getName()), click: () => { app.window().show(); } },
{ type: 'separator' }, { type: 'separator' },
{ label: _('Exit'), click: () => { app.exit() } }, { label: _('Exit'), click: () => { app.quit() } },
]) ])
app.createTray(contextMenu); app.createTray(contextMenu);
} }
@ -602,6 +609,8 @@ class Application extends BaseApplication {
AlarmService.garbageCollect(); AlarmService.garbageCollect();
}, 1000 * 60 * 60); }, 1000 * 60 * 60);
ResourceService.runInBackground();
if (Setting.value('env') === 'dev') { if (Setting.value('env') === 'dev') {
AlarmService.updateAllNotifications(); AlarmService.updateAllNotifications();
} else { } else {

View File

@ -1,31 +1,15 @@
const { dialog } = require('electron') const { dialog } = require('electron')
const { autoUpdater } = require('electron-updater')
const { Logger } = require('lib/logger.js'); const { Logger } = require('lib/logger.js');
const { _ } = require('lib/locale.js'); const { _ } = require('lib/locale.js');
const fetch = require('node-fetch');
const packageInfo = require('./packageInfo.js');
const compareVersions = require('compare-versions');
let autoUpdateLogger_ = new Logger(); let autoUpdateLogger_ = new Logger();
let checkInBackground_ = false; let checkInBackground_ = false;
let isCheckingForUpdate_ = false; let isCheckingForUpdate_ = false;
let parentWindow_ = null; let parentWindow_ = null;
// Note: Electron Builder's autoUpdater is incredibly buggy so currently it's only used
// to detect if a new version is present. If it is, the download link is simply opened
// in a new browser window.
autoUpdater.autoDownload = false;
function htmlToText_(html) {
let output = html.replace(/\n/g, '');
output = output.replace(/<li>/g, '- ');
output = output.replace(/<p>/g, '');
output = output.replace(/<\/p>/g, '\n');
output = output.replace(/<\/li>/g, '\n');
output = output.replace(/<ul>/g, '');
output = output.replace(/<\/ul>/g, '');
output = output.replace(/<.*?>/g, '');
output = output.replace(/<\/.*?>/g, '');
return output;
}
function showErrorMessageBox(message) { function showErrorMessageBox(message) {
return dialog.showMessageBox(parentWindow_, { return dialog.showMessageBox(parentWindow_, {
type: 'error', type: 'error',
@ -43,81 +27,45 @@ function onCheckEnded() {
isCheckingForUpdate_ = false; isCheckingForUpdate_ = false;
} }
autoUpdater.on('error', (error) => { async function fetchLatestRelease() {
autoUpdateLogger_.error(error); const response = await fetch('https://api.github.com/repos/laurent22/joplin/releases/latest');
if (checkInBackground_) return onCheckEnded();
let msg = error == null ? "unknown" : (error.stack || error).toString(); if (!response.ok) {
// Error messages can be very long even without stack trace so shorten const responseText = await response.text();
// then so that the dialog box doesn't take the whole screen. throw new Error('Cannot get latest release info: ' + responseText.substr(0,500));
msg = msg.substr(0,512).replace(/\\n/g, '\n'); }
showErrorMessageBox(msg)
const json = await response.json();
onCheckEnded(); const version = json.tag_name.substr(1);
}) let downloadUrl = null;
const platform = process.platform;
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) {
found = true;
} else if (platform === 'darwin' && asset.name.indexOf('.dmg') >= 0) {
found = true;
} else if (platform === 'linux' && asset.name.indexOf('.AppImage') >= 0) {
found = true;
}
function findDownloadFilename_(info) { if (found) {
// { version: '1.0.64', downloadUrl = asset.browser_download_url;
// files: break;
// [ { url: 'Joplin-1.0.64-mac.zip', }
// sha512: 'OlemXqhq/fSifx7EutvMzfoCI/1kGNl10i8nkvACEDfVXwP8hankDBXEC0+GxSArsZuxOh3U1+C+4j72SfIUew==' },
// { url: 'Joplin-1.0.64.dmg',
// sha512: 'jAewQQoJ3nCaOj8hWDgf0sc3LBbAWQtiKqfTflK8Hc3Dh7fAy9jRHfFAZKFUZ9ll95Bun0DVsLq8wLSUrdsMXw==',
// size: 77104485 } ],
// path: 'Joplin-1.0.64-mac.zip',
// sha512: 'OlemXqhq/fSifx7EutvMzfoCI/1kGNl10i8nkvACEDfVXwP8hankDBXEC0+GxSArsZuxOh3U1+C+4j72SfIUew==',
// releaseDate: '2018-02-16T00:13:01.634Z',
// releaseName: 'v1.0.64',
// releaseNotes: '<p>Still more fixes and im...' }
if (!info) return null;
if (!info.files) {
// info.path seems to contain a default, though not a good one,
// so the loop below if preferable to find the right file.
return info.path;
} }
for (let i = 0; i < info.files.length; i++) { if (!downloadUrl) throw new Error('Cannot find download Url: ' + JSON.stringify(json).substr(0,500));
const f = info.files[i].url; // Annoyingly this is called "url" but it's obviously not a url, so hopefully it won't change later on and become one.
if (f.indexOf('.exe') >= 0) return f;
if (f.indexOf('.dmg') >= 0) return f;
}
return info.path; return {
version: version,
downloadUrl: downloadUrl,
notes: json.body,
};
} }
autoUpdater.on('update-available', (info) => {
const filename = findDownloadFilename_(info);
if (!info.version || !filename) {
if (checkInBackground_) return onCheckEnded();
showErrorMessageBox(('Could not get version info: ' + JSON.stringify(info)));
return onCheckEnded();
}
const downloadUrl = 'https://github.com/laurent22/joplin/releases/download/v' + info.version + '/' + filename;
let releaseNotes = info.releaseNotes + '';
if (releaseNotes) releaseNotes = '\n\n' + _('Release notes:\n\n%s', htmlToText_(releaseNotes));
const buttonIndex = dialog.showMessageBox(parentWindow_, {
type: 'info',
message: _('An update is available, do you want to download it now?' + releaseNotes),
buttons: [_('Yes'), _('No')]
});
onCheckEnded();
if (buttonIndex === 0) require('electron').shell.openExternal(downloadUrl);
})
autoUpdater.on('update-not-available', () => {
if (checkInBackground_) return onCheckEnded();
dialog.showMessageBox({ message: _('Current version is up-to-date.') })
onCheckEnded();
})
function checkForUpdates(inBackground, window, logFilePath) { function checkForUpdates(inBackground, window, logFilePath) {
if (isCheckingForUpdate_) { if (isCheckingForUpdate_) {
autoUpdateLogger_.info('checkForUpdates: Skipping check because it is already running'); autoUpdateLogger_.info('checkForUpdates: Skipping check because it is already running');
@ -133,18 +81,30 @@ function checkForUpdates(inBackground, window, logFilePath) {
autoUpdateLogger_.addTarget('file', { path: logFilePath }); autoUpdateLogger_.addTarget('file', { path: logFilePath });
autoUpdateLogger_.setLevel(Logger.LEVEL_DEBUG); autoUpdateLogger_.setLevel(Logger.LEVEL_DEBUG);
autoUpdateLogger_.info('checkForUpdates: Initializing...'); autoUpdateLogger_.info('checkForUpdates: Initializing...');
autoUpdater.logger = autoUpdateLogger_;
} }
checkInBackground_ = inBackground; checkInBackground_ = inBackground;
try { fetchLatestRelease().then(release => {
autoUpdater.checkForUpdates() if (compareVersions(release.version, packageInfo.version) <= 0) {
} catch (error) { if (!checkInBackground_) dialog.showMessageBox({ message: _('Current version is up-to-date.') })
} else {
const releaseNotes = release.notes.trim() ? "\n\n" + release.notes.trim() : '';
const buttonIndex = dialog.showMessageBox(parentWindow_, {
type: 'info',
message: _('An update is available, do you want to download it now?' + releaseNotes),
buttons: [_('Yes'), _('No')]
});
if (buttonIndex === 0) require('electron').shell.openExternal(release.downloadUrl);
}
}).catch(error => {
autoUpdateLogger_.error(error); autoUpdateLogger_.error(error);
if (!checkInBackground_) showErrorMessageBox(error.message); if (!checkInBackground_) showErrorMessageBox(error.message);
}).then(() => {
onCheckEnded(); onCheckEnded();
} });
} }
module.exports.checkForUpdates = checkForUpdates module.exports.checkForUpdates = checkForUpdates

View File

@ -6,6 +6,63 @@ const { _ } = require('lib/locale.js');
class HeaderComponent extends React.Component { class HeaderComponent extends React.Component {
constructor() {
super();
this.state = {
searchQuery: '',
};
this.scheduleSearchChangeEventIid_ = null;
this.searchOnQuery_ = null;
this.searchElement_ = null;
const triggerOnQuery = (query) => {
clearTimeout(this.scheduleSearchChangeEventIid_);
if (this.searchOnQuery_) this.searchOnQuery_(query);
this.scheduleSearchChangeEventIid_ = null;
}
this.search_onChange = (event) => {
this.setState({ searchQuery: event.target.value });
if (this.scheduleSearchChangeEventIid_) clearTimeout(this.scheduleSearchChangeEventIid_);
this.scheduleSearchChangeEventIid_ = setTimeout(() => {
triggerOnQuery(this.state.searchQuery);
}, 500);
};
this.search_onClear = (event) => {
this.setState({ searchQuery: '' });
triggerOnQuery('');
}
}
async componentWillReceiveProps(nextProps) {
if (nextProps.windowCommand) {
this.doCommand(nextProps.windowCommand);
}
}
async doCommand(command) {
if (!command) return;
let commandProcessed = true;
if (command.name === 'focus_search' && this.searchElement_) {
this.searchElement_.focus();
} else {
commandProcessed = false;
}
if (commandProcessed) {
this.props.dispatch({
type: 'WINDOW_COMMAND',
name: null,
});
}
}
back_click() { back_click() {
this.props.dispatch({ type: 'NAV_BACK' }); this.props.dispatch({ type: 'NAV_BACK' });
} }
@ -40,6 +97,59 @@ class HeaderComponent extends React.Component {
</a> </a>
} }
makeSearch(key, style, options, state) {
const inputStyle = {
display: 'flex',
flex: 1,
paddingLeft: 4,
paddingRight: 4,
color: style.color,
fontSize: style.fontSize,
fontFamily: style.fontFamily,
};
const searchButton = {
paddingLeft: 4,
paddingRight: 4,
paddingTop: 2,
paddingBottom: 2,
textDecoration: 'none',
};
const iconStyle = {
display: 'flex',
fontSize: Math.round(style.fontSize) * 1.2,
color: style.color,
};
const containerStyle = {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
};
const iconName = state.searchQuery ? 'fa-times' : 'fa-search';
const icon = <i style={iconStyle} className={"fa " + iconName}></i>
if (options.onQuery) this.searchOnQuery_ = options.onQuery;
return (
<div key={key} style={containerStyle}>
<input
type="text"
style={inputStyle}
placeholder={options.title}
value={state.searchQuery}
onChange={this.search_onChange}
ref={elem => this.searchElement_ = elem}
/>
<a
href="#"
style={searchButton}
onClick={this.search_onClear}
>{icon}</a>
</div>);
}
render() { render() {
const style = Object.assign({}, this.props.style); const style = Object.assign({}, this.props.style);
const theme = themeStyle(this.props.theme); const theme = themeStyle(this.props.theme);
@ -50,9 +160,9 @@ class HeaderComponent extends React.Component {
style.borderBottom = '1px solid ' + theme.dividerColor; style.borderBottom = '1px solid ' + theme.dividerColor;
style.boxSizing = 'border-box'; style.boxSizing = 'border-box';
const buttons = []; const items = [];
const buttonStyle = { const itemStyle = {
height: theme.headerHeight, height: theme.headerHeight,
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
@ -67,19 +177,24 @@ class HeaderComponent extends React.Component {
}; };
if (showBackButton) { if (showBackButton) {
buttons.push(this.makeButton('back', buttonStyle, { title: _('Back'), onClick: () => this.back_click(), iconName: 'fa-chevron-left ' })); items.push(this.makeButton('back', itemStyle, { title: _('Back'), onClick: () => this.back_click(), iconName: 'fa-chevron-left ' }));
} }
if (this.props.buttons) { if (this.props.items) {
for (let i = 0; i < this.props.buttons.length; i++) { for (let i = 0; i < this.props.items.length; i++) {
const o = this.props.buttons[i]; const item = this.props.items[i];
buttons.push(this.makeButton('btn_' + i + '_' + o.title, buttonStyle, o));
if (item.type === 'search') {
items.push(this.makeSearch('item_' + i + '_search', itemStyle, item, this.state));
} else {
items.push(this.makeButton('item_' + i + '_' + item.title, itemStyle, item));
}
} }
} }
return ( return (
<div className="header" style={style}> <div className="header" style={style}>
{ buttons } { items }
</div> </div>
); );
} }
@ -87,7 +202,10 @@ class HeaderComponent extends React.Component {
} }
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
return { theme: state.settings.theme }; return {
theme: state.settings.theme,
windowCommand: state.windowCommand,
};
}; };
const Header = connect(mapStateToProps)(HeaderComponent); const Header = connect(mapStateToProps)(HeaderComponent);

View File

@ -140,33 +140,27 @@ class MainScreenComponent extends React.Component {
}, },
}); });
} else if (command.name === 'search') { } else if (command.name === 'search') {
this.setState({
promptOptions: {
label: _('Search:'),
onClose: async (answer) => {
if (answer !== null) {
const searchId = uuid.create();
this.props.dispatch({ if (!this.searchId_) this.searchId_ = uuid.create();
type: 'SEARCH_ADD',
search: {
id: searchId,
title: answer,
query_pattern: answer,
query_folder_id: null,
type_: BaseModel.TYPE_SEARCH,
},
});
this.props.dispatch({ this.props.dispatch({
type: 'SEARCH_SELECT', type: 'SEARCH_UPDATE',
id: searchId, search: {
}); id: this.searchId_,
} title: command.query,
this.setState({ promptOptions: null }); query_pattern: command.query,
} query_folder_id: null,
type_: BaseModel.TYPE_SEARCH,
}, },
}); });
if (command.query) {
this.props.dispatch({
type: 'SEARCH_SELECT',
id: this.searchId_,
});
}
} else if (command.name === 'toggleVisiblePanes') { } else if (command.name === 'toggleVisiblePanes') {
this.toggleVisiblePanes(); this.toggleVisiblePanes();
} else if (command.name === 'showModalMessage') { } else if (command.name === 'showModalMessage') {
@ -278,7 +272,7 @@ class MainScreenComponent extends React.Component {
position: 'absolute', position: 'absolute',
top: 0, top: 0,
left: 0, left: 0,
backgroundColor: theme.backgroundColorTransparent, backgroundColor: theme.backgroundColor,
width: width - 20, width: width - 20,
height: height - 20, height: height - 20,
padding: 10, padding: 10,
@ -298,41 +292,42 @@ class MainScreenComponent extends React.Component {
const selectedFolderId = this.props.selectedFolderId; const selectedFolderId = this.props.selectedFolderId;
const onConflictFolder = this.props.selectedFolderId === Folder.conflictFolderId(); const onConflictFolder = this.props.selectedFolderId === Folder.conflictFolderId();
const headerButtons = []; const headerItems = [];
headerButtons.push({ headerItems.push({
title: _('New note'), title: _('New note'),
iconName: 'fa-file-o', iconName: 'fa-file-o',
enabled: !!folders.length && !onConflictFolder, enabled: !!folders.length && !onConflictFolder,
onClick: () => { this.doCommand({ name: 'newNote' }) }, onClick: () => { this.doCommand({ name: 'newNote' }) },
}); });
headerButtons.push({ headerItems.push({
title: _('New to-do'), title: _('New to-do'),
iconName: 'fa-check-square-o', iconName: 'fa-check-square-o',
enabled: !!folders.length && !onConflictFolder, enabled: !!folders.length && !onConflictFolder,
onClick: () => { this.doCommand({ name: 'newTodo' }) }, onClick: () => { this.doCommand({ name: 'newTodo' }) },
}); });
headerButtons.push({ headerItems.push({
title: _('New notebook'), title: _('New notebook'),
iconName: 'fa-folder-o', iconName: 'fa-folder-o',
onClick: () => { this.doCommand({ name: 'newNotebook' }) }, onClick: () => { this.doCommand({ name: 'newNotebook' }) },
}); });
headerButtons.push({ headerItems.push({
title: _('Search'),
iconName: 'fa-search',
onClick: () => { this.doCommand({ name: 'search' }) },
});
headerButtons.push({
title: _('Layout'), title: _('Layout'),
iconName: 'fa-columns', iconName: 'fa-columns',
enabled: !!notes.length, enabled: !!notes.length,
onClick: () => { this.doCommand({ name: 'toggleVisiblePanes' }) }, onClick: () => { this.doCommand({ name: 'toggleVisiblePanes' }) },
}); });
headerItems.push({
title: _('Search...'),
iconName: 'fa-search',
onQuery: (query) => { this.doCommand({ name: 'search', query: query }) },
type: 'search',
});
if (!this.promptOnClose_) { if (!this.promptOnClose_) {
this.promptOnClose_ = (answer, buttonType) => { this.promptOnClose_ = (answer, buttonType) => {
return this.state.promptOptions.onClose(answer, buttonType); return this.state.promptOptions.onClose(answer, buttonType);
@ -389,7 +384,7 @@ class MainScreenComponent extends React.Component {
visible={!!this.state.promptOptions} visible={!!this.state.promptOptions}
buttons={promptOptions && ('buttons' in promptOptions) ? promptOptions.buttons : null} buttons={promptOptions && ('buttons' in promptOptions) ? promptOptions.buttons : null}
inputType={promptOptions && ('inputType' in promptOptions) ? promptOptions.inputType : null} /> inputType={promptOptions && ('inputType' in promptOptions) ? promptOptions.inputType : null} />
<Header style={styles.header} showBackButton={false} buttons={headerButtons} /> <Header style={styles.header} showBackButton={false} items={headerItems} />
{messageComp} {messageComp}
<SideBar style={styles.sideBar} /> <SideBar style={styles.sideBar} />
<NoteList style={styles.noteList} /> <NoteList style={styles.noteList} />

View File

@ -11,6 +11,8 @@ const MenuItem = bridge().MenuItem;
const eventManager = require('../eventManager'); const eventManager = require('../eventManager');
const InteropService = require('lib/services/InteropService'); const InteropService = require('lib/services/InteropService');
const InteropServiceHelper = require('../InteropServiceHelper.js'); const InteropServiceHelper = require('../InteropServiceHelper.js');
const Search = require('lib/models/Search');
const Mark = require('mark.js/dist/mark.min.js');
class NoteListComponent extends React.Component { class NoteListComponent extends React.Component {
@ -94,11 +96,22 @@ class NoteListComponent extends React.Component {
} }
}})); }}));
menu.append(new MenuItem({label: _('Export'), click: async () => { const exportMenu = new Menu();
const ioService = new InteropService();
const module = ioService.moduleByFormat_('exporter', 'jex'); const ioService = new InteropService();
await InteropServiceHelper.export(this.props.dispatch.bind(this), module, { sourceNoteIds: noteIds }); const ioModules = ioService.modules();
}})); for (let i = 0; i < ioModules.length; i++) {
const module = ioModules[i];
if (module.type !== 'exporter') continue;
exportMenu.append(new MenuItem({ label: module.fullLabel() , click: async () => {
await InteropServiceHelper.export(this.props.dispatch.bind(this), module, { sourceNoteIds: noteIds });
}}));
}
const exportMenuItem = new MenuItem({label: _('Export'), submenu: exportMenu});
menu.append(exportMenuItem);
} }
menu.append(new MenuItem({label: _('Delete'), click: async () => { menu.append(new MenuItem({label: _('Delete'), click: async () => {
@ -153,6 +166,12 @@ class NoteListComponent extends React.Component {
const hPadding = 10; const hPadding = 10;
let highlightedWords = [];
if (this.props.notesParentType === 'Search') {
const search = BaseModel.byId(this.props.searches, this.props.selectedSearchId);
highlightedWords = search ? Search.keywords(search.query_pattern) : [];
}
let style = Object.assign({ width: width }, this.style().listItem); let style = Object.assign({ width: width }, this.style().listItem);
if (this.props.selectedNoteIds.indexOf(item.id) >= 0) { if (this.props.selectedNoteIds.indexOf(item.id) >= 0) {
@ -171,8 +190,30 @@ class NoteListComponent extends React.Component {
listItemTitleStyle.paddingLeft = !checkbox ? hPadding : 4; listItemTitleStyle.paddingLeft = !checkbox ? hPadding : 4;
if (item.is_todo && !!item.todo_completed) listItemTitleStyle = Object.assign(listItemTitleStyle, this.style().listItemTitleCompleted); if (item.is_todo && !!item.todo_completed) listItemTitleStyle = Object.assign(listItemTitleStyle, this.style().listItemTitleCompleted);
let displayTitle = Note.displayTitle(item);
let titleComp = null;
if (highlightedWords.length) {
const titleElement = document.createElement('span');
titleElement.textContent = displayTitle;
const mark = new Mark(titleElement, {
exclude: ['img'],
acrossElements: true,
});
mark.mark(highlightedWords);
// Note: in this case it is safe to use dangerouslySetInnerHTML because titleElement
// is a span tag that we created and that contains data that's been inserted as plain text
// with `textContent` so it cannot contain any XSS attacks. We use this feature because
// mark.js can only deal with DOM elements.
// https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
titleComp = <span dangerouslySetInnerHTML={{ __html: titleElement.outerHTML }}></span>
} else {
titleComp = <span>{displayTitle}</span>
}
// Need to include "todo_completed" in key so that checkbox is updated when // Need to include "todo_completed" in key so that checkbox is updated when
// item is changed via sync. // item is changed via sync.
return <div key={item.id + '_' + item.todo_completed} style={style}> return <div key={item.id + '_' + item.todo_completed} style={style}>
{checkbox} {checkbox}
<a <a
@ -185,7 +226,7 @@ class NoteListComponent extends React.Component {
onDragStart={(event) => onDragStart(event) } onDragStart={(event) => onDragStart(event) }
data-id={item.id} data-id={item.id}
> >
{Note.displayTitle(item)} {titleComp}
</a> </a>
</div> </div>
} }
@ -228,7 +269,9 @@ const mapStateToProps = (state) => {
folders: state.folders, folders: state.folders,
selectedNoteIds: state.selectedNoteIds, selectedNoteIds: state.selectedNoteIds,
theme: state.settings.theme, theme: state.settings.theme,
// uncompletedTodosOnTop: state.settings.uncompletedTodosOnTop, notesParentType: state.notesParentType,
searches: state.searches,
selectedSearchId: state.selectedSearchId,
}; };
}; };

View File

@ -0,0 +1,47 @@
const React = require('react');
const { connect } = require('react-redux');
const { time } = require('lib/time-utils.js');
const { themeStyle } = require('../theme.js');
const { _ } = require('lib/locale.js');
class NoteStatusBarComponent extends React.Component {
style() {
const theme = themeStyle(this.props.theme);
const itemHeight = 34;
let style = {
root: Object.assign({}, theme.textStyle, {
backgroundColor: theme.backgroundColor,
color: theme.colorFaded,
}),
};
return style;
}
render() {
const theme = themeStyle(this.props.theme);
const style = this.props.style;
const note = this.props.note;
return (
<div style={this.style().root}>{time.formatMsToLocal(note.user_updated_time)}</div>
);
}
}
const mapStateToProps = (state) => {
return {
// notes: state.notes,
// folders: state.folders,
// selectedNoteIds: state.selectedNoteIds,
theme: state.settings.theme,
};
};
const NoteStatusBar = connect(mapStateToProps)(NoteStatusBarComponent);
module.exports = { NoteStatusBar };

View File

@ -1,5 +1,7 @@
const React = require('react'); const React = require('react');
const Note = require('lib/models/Note.js'); const Note = require('lib/models/Note.js');
const BaseModel = require('lib/BaseModel.js');
const Search = require('lib/models/Search.js');
const { time } = require('lib/time-utils.js'); const { time } = require('lib/time-utils.js');
const Setting = require('lib/models/Setting.js'); const Setting = require('lib/models/Setting.js');
const { IconButton } = require('./IconButton.min.js'); const { IconButton } = require('./IconButton.min.js');
@ -195,7 +197,9 @@ class NoteTextComponent extends React.Component {
this.editorSetScrollTop(1); this.editorSetScrollTop(1);
this.restoreScrollTop_ = 0; this.restoreScrollTop_ = 0;
if (note) { // If a search is in progress we don't focus any field otherwise it will
// take the focus out of the search box.
if (note && this.props.notesParentType !== 'Search') {
const focusSettingName = !!note.is_todo ? 'newTodoFocus' : 'newNoteFocus'; const focusSettingName = !!note.is_todo ? 'newTodoFocus' : 'newNoteFocus';
if (Setting.value(focusSettingName) === 'title') { if (Setting.value(focusSettingName) === 'title') {
@ -240,6 +244,10 @@ class NoteTextComponent extends React.Component {
if ('syncStarted' in nextProps && !nextProps.syncStarted && !this.isModified()) { if ('syncStarted' in nextProps && !nextProps.syncStarted && !this.isModified()) {
await this.reloadNote(nextProps, { noReloadIfLocalChanges: true }); await this.reloadNote(nextProps, { noReloadIfLocalChanges: true });
} }
if (nextProps.windowCommand) {
this.doCommand(nextProps.windowCommand);
}
} }
isModified() { isModified() {
@ -292,7 +300,7 @@ class NoteTextComponent extends React.Component {
const menu = new Menu() const menu = new Menu()
if (itemType === 'image') { if (itemType === "image" || itemType === "link") {
const resource = await Resource.load(arg0.resourceId); const resource = await Resource.load(arg0.resourceId);
const resourcePath = Resource.fullPath(resource); const resourcePath = Resource.fullPath(resource);
@ -362,7 +370,7 @@ class NoteTextComponent extends React.Component {
webviewReady: true, webviewReady: true,
}); });
// if (Setting.value('env') === 'dev') this.webview_.openDevTools(); if (Setting.value('env') === 'dev') this.webview_.openDevTools();
} }
webview_ref(element) { webview_ref(element) {
@ -389,6 +397,18 @@ class NoteTextComponent extends React.Component {
if (this.editor_) { if (this.editor_) {
this.editor_.editor.renderer.on('afterRender', this.onAfterEditorRender_); this.editor_.editor.renderer.on('afterRender', this.onAfterEditorRender_);
const cancelledKeys = ['Ctrl+F', 'Ctrl+T', 'Ctrl+P', 'Ctrl+Q', 'Ctrl+L', 'Ctrl+,'];
for (let i = 0; i < cancelledKeys.length; i++) {
const k = cancelledKeys[i];
this.editor_.editor.commands.bindKey(k, () => {
// HACK: Ace doesn't seem to provide a way to override its shortcuts, but throwing
// an exception from this undocumented function seems to cancel it without any
// side effect.
// https://stackoverflow.com/questions/36075846
throw new Error('HACK: Overriding Ace Editor shortcut: ' + k);
});
}
} }
} }
@ -426,6 +446,38 @@ class NoteTextComponent extends React.Component {
this.scheduleSave(); this.scheduleSave();
} }
async doCommand(command) {
if (!command) return;
let commandProcessed = true;
if (command.name === 'exportPdf' && this.webview_) {
const path = bridge().showSaveDialog({
filters: [{ name: _('PDF File'), extensions: ['pdf']}]
});
if (path) {
this.webview_.printToPDF({}, (error, data) => {
if (error) {
bridge().showErrorMessageBox(error.message);
} else {
shim.fsDriver().writeFile(path, data, 'buffer');
}
});
}
} else if (command.name === 'print' && this.webview_) {
this.webview_.print();
} else {
commandProcessed = false;
}
if (commandProcessed) {
this.props.dispatch({
type: 'WINDOW_COMMAND',
name: null,
});
}
}
async commandAttachFile() { async commandAttachFile() {
const filePaths = bridge().showOpenDialog({ const filePaths = bridge().showOpenDialog({
@ -530,6 +582,7 @@ class NoteTextComponent extends React.Component {
marginBottom: 0, marginBottom: 0,
display: 'flex', display: 'flex',
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center',
}; };
const titleEditorStyle = { const titleEditorStyle = {
@ -606,6 +659,10 @@ class NoteTextComponent extends React.Component {
const html = this.mdToHtml().render(bodyToRender, theme, mdOptions); const html = this.mdToHtml().render(bodyToRender, theme, mdOptions);
this.webview_.send('setHtml', html); 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);
} }
const toolbarItems = []; const toolbarItems = [];
@ -649,6 +706,8 @@ class NoteTextComponent extends React.Component {
display: 'flex', display: 'flex',
}} iconName="fa-caret-down" theme={this.props.theme} onClick={() => { this.itemContextMenu() }} /> }} iconName="fa-caret-down" theme={this.props.theme} onClick={() => { this.itemContextMenu() }} />
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} style={viewerStyle}
nodeintegration="1" nodeintegration="1"
@ -656,6 +715,24 @@ class NoteTextComponent extends React.Component {
ref={(elem) => { this.webview_ref(elem); } } ref={(elem) => { this.webview_ref(elem); } }
/> />
// const markers = [{
// startRow: 2,
// startCol: 3,
// endRow: 2,
// endCol: 6,
// type: 'text',
// className: 'test-marker'
// }];
// markers={markers}
// editorProps={{$useWorker: false}}
// #note-editor .test-marker {
// background-color: red;
// color: yellow;
// position: absolute;
// }
const editorRootStyle = Object.assign({}, editorStyle); const editorRootStyle = Object.assign({}, editorStyle);
delete editorRootStyle.width; delete editorRootStyle.width;
delete editorRootStyle.height; delete editorRootStyle.height;
@ -682,14 +759,15 @@ class NoteTextComponent extends React.Component {
editorProps={{$blockScrolling: true}} editorProps={{$blockScrolling: true}}
// This is buggy (gets outside the container) // This is buggy (gets outside the container)
highlightActiveLine={false} highlightActiveLine={false}
/> />
return ( return (
<div style={rootStyle}> <div style={rootStyle}>
<div style={titleBarStyle}> <div style={titleBarStyle}>
{ titleEditor } { titleEditor }
{ titleBarMenuButton } { titleBarDate }
{ false ? titleBarMenuButton : null }
</div> </div>
{ toolbar } { toolbar }
{ editor } { editor }
@ -710,6 +788,10 @@ const mapStateToProps = (state) => {
showAdvancedOptions: state.settings.showAdvancedOptions, showAdvancedOptions: state.settings.showAdvancedOptions,
syncStarted: state.syncStarted, syncStarted: state.syncStarted,
newNote: state.newNote, newNote: state.newNote,
windowCommand: state.windowCommand,
notesParentType: state.notesParentType,
searches: state.searches,
selectedSearchId: state.selectedSearchId,
}; };
}; };

View File

@ -1,20 +1,19 @@
const React = require('react'); const React = require("react");
const { connect } = require('react-redux'); const { connect } = require("react-redux");
const shared = require('lib/components/shared/side-menu-shared.js'); const shared = require("lib/components/shared/side-menu-shared.js");
const { Synchronizer } = require('lib/synchronizer.js'); const { Synchronizer } = require("lib/synchronizer.js");
const BaseModel = require('lib/BaseModel.js'); const BaseModel = require("lib/BaseModel.js");
const Folder = require('lib/models/Folder.js'); const Folder = require("lib/models/Folder.js");
const Note = require('lib/models/Note.js'); const Note = require("lib/models/Note.js");
const Tag = require('lib/models/Tag.js'); const Tag = require("lib/models/Tag.js");
const { _ } = require('lib/locale.js'); const { _ } = require("lib/locale.js");
const { themeStyle } = require('../theme.js'); const { themeStyle } = require("../theme.js");
const { bridge } = require('electron').remote.require('./bridge'); const { bridge } = require("electron").remote.require("./bridge");
const Menu = bridge().Menu; const Menu = bridge().Menu;
const MenuItem = bridge().MenuItem; const MenuItem = bridge().MenuItem;
const InteropServiceHelper = require('../InteropServiceHelper.js'); const InteropServiceHelper = require("../InteropServiceHelper.js");
class SideBarComponent extends React.Component { class SideBarComponent extends React.Component {
style() { style() {
const theme = themeStyle(this.props.theme); const theme = themeStyle(this.props.theme);
@ -28,63 +27,65 @@ class SideBarComponent extends React.Component {
height: itemHeight, height: itemHeight,
fontFamily: theme.fontFamily, fontFamily: theme.fontFamily,
fontSize: theme.fontSize, fontSize: theme.fontSize,
textDecoration: 'none', textDecoration: "none",
boxSizing: 'border-box', boxSizing: "border-box",
color: theme.color2, color: theme.color2,
paddingLeft: 14, paddingLeft: 14,
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
cursor: 'default', cursor: "default",
opacity: 0.8, opacity: 0.8,
whiteSpace: 'nowrap', whiteSpace: "nowrap",
}, },
listItemSelected: { listItemSelected: {
backgroundColor: theme.selectedColor2, backgroundColor: theme.selectedColor2,
}, },
conflictFolder: { conflictFolder: {
color: theme.colorError2, color: theme.colorError2,
fontWeight: 'bold', fontWeight: "bold",
}, },
header: { header: {
height: itemHeight * 1.8, height: itemHeight * 1.8,
fontFamily: theme.fontFamily, fontFamily: theme.fontFamily,
fontSize: theme.fontSize * 1.3, fontSize: theme.fontSize * 1.3,
textDecoration: 'none', textDecoration: "none",
boxSizing: 'border-box', boxSizing: "border-box",
color: theme.color2, color: theme.color2,
paddingLeft: 8, paddingLeft: 8,
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
}, },
button: { button: {
padding: 6, padding: 6,
fontFamily: theme.fontFamily, fontFamily: theme.fontFamily,
fontSize: theme.fontSize, fontSize: theme.fontSize,
textDecoration: 'none', textDecoration: "none",
boxSizing: 'border-box', boxSizing: "border-box",
color: theme.color2, color: theme.color2,
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
justifyContent: 'center', justifyContent: "center",
border: "1px solid rgba(255,255,255,0.2)", border: "1px solid rgba(255,255,255,0.2)",
marginTop: 10, marginTop: 10,
marginLeft: 5, marginLeft: 5,
marginRight: 5, marginRight: 5,
cursor: 'default', cursor: "default",
}, },
syncReport: { syncReport: {
fontFamily: theme.fontFamily, fontFamily: theme.fontFamily,
fontSize: Math.round(theme.fontSize * .9), fontSize: Math.round(theme.fontSize * 0.9),
color: theme.color2, color: theme.color2,
opacity: .5, opacity: 0.5,
display: 'flex', display: "flex",
alignItems: 'left', alignItems: "left",
justifyContent: 'top', justifyContent: "top",
flexDirection: 'column', flexDirection: "column",
marginTop: 10, marginTop: 10,
marginLeft: 5, marginLeft: 5,
marginRight: 5, marginRight: 5,
minHeight: 70, minHeight: 70,
wordWrap: "break-word",
width: "100%",
}, },
}; };
@ -92,19 +93,19 @@ class SideBarComponent extends React.Component {
} }
itemContextMenu(event) { itemContextMenu(event) {
const itemId = event.target.getAttribute('data-id'); const itemId = event.target.getAttribute("data-id");
if (itemId === Folder.conflictFolderId()) return; if (itemId === Folder.conflictFolderId()) return;
const itemType = Number(event.target.getAttribute('data-type'));
if (!itemId || !itemType) throw new Error('No data on element');
let deleteMessage = ''; const itemType = Number(event.target.getAttribute("data-type"));
if (!itemId || !itemType) throw new Error("No data on element");
let deleteMessage = "";
if (itemType === BaseModel.TYPE_FOLDER) { if (itemType === BaseModel.TYPE_FOLDER) {
deleteMessage = _('Delete notebook? All notes within this notebook will also be deleted.'); deleteMessage = _("Delete notebook? All notes within this notebook will also be deleted.");
} else if (itemType === BaseModel.TYPE_TAG) { } else if (itemType === BaseModel.TYPE_TAG) {
deleteMessage = _('Remove this tag from all the notes?'); deleteMessage = _("Remove this tag from all the notes?");
} else if (itemType === BaseModel.TYPE_SEARCH) { } else if (itemType === BaseModel.TYPE_SEARCH) {
deleteMessage = _('Remove this search from the sidebar?'); deleteMessage = _("Remove this search from the sidebar?");
} }
const menu = new Menu(); const menu = new Menu();
@ -114,40 +115,55 @@ class SideBarComponent extends React.Component {
item = BaseModel.byId(this.props.folders, itemId); item = BaseModel.byId(this.props.folders, itemId);
} }
menu.append(new MenuItem({label: _('Delete'), click: async () => { menu.append(
const ok = bridge().showConfirmMessageBox(deleteMessage); new MenuItem({
if (!ok) return; label: _("Delete"),
click: async () => {
const ok = bridge().showConfirmMessageBox(deleteMessage);
if (!ok) return;
if (itemType === BaseModel.TYPE_FOLDER) { if (itemType === BaseModel.TYPE_FOLDER) {
await Folder.delete(itemId); await Folder.delete(itemId);
} else if (itemType === BaseModel.TYPE_TAG) { } else if (itemType === BaseModel.TYPE_TAG) {
await Tag.untagAll(itemId); await Tag.untagAll(itemId);
} else if (itemType === BaseModel.TYPE_SEARCH) { } else if (itemType === BaseModel.TYPE_SEARCH) {
this.props.dispatch({ this.props.dispatch({
type: 'SEARCH_DELETE', type: "SEARCH_DELETE",
id: itemId, id: itemId,
}); });
} }
}})) },
})
);
if (itemType === BaseModel.TYPE_FOLDER && !item.encryption_applied) { if (itemType === BaseModel.TYPE_FOLDER && !item.encryption_applied) {
menu.append(new MenuItem({label: _('Rename'), click: async () => { menu.append(
this.props.dispatch({ new MenuItem({
type: 'WINDOW_COMMAND', label: _("Rename"),
name: 'renameFolder', click: async () => {
id: itemId, this.props.dispatch({
}); type: "WINDOW_COMMAND",
}})); name: "renameFolder",
id: itemId,
});
},
})
);
menu.append(new MenuItem({ type: 'separator' })); menu.append(new MenuItem({ type: "separator" }));
const InteropService = require('lib/services/InteropService.js'); const InteropService = require("lib/services/InteropService.js");
menu.append(new MenuItem({label: _('Export'), click: async () => { menu.append(
const ioService = new InteropService(); new MenuItem({
const module = ioService.moduleByFormat_('exporter', 'jex'); label: _("Export"),
await InteropServiceHelper.export(this.props.dispatch.bind(this), module, { sourceFolderIds: [itemId] }); click: async () => {
}})); const ioService = new InteropService();
const module = ioService.moduleByFormat_("exporter", "jex");
await InteropServiceHelper.export(this.props.dispatch.bind(this), module, { sourceFolderIds: [itemId] });
},
})
);
} }
menu.popup(bridge().window()); menu.popup(bridge().window());
@ -155,21 +171,21 @@ class SideBarComponent extends React.Component {
folderItem_click(folder) { folderItem_click(folder) {
this.props.dispatch({ this.props.dispatch({
type: 'FOLDER_SELECT', type: "FOLDER_SELECT",
id: folder ? folder.id : null, id: folder ? folder.id : null,
}); });
} }
tagItem_click(tag) { tagItem_click(tag) {
this.props.dispatch({ this.props.dispatch({
type: 'TAG_SELECT', type: "TAG_SELECT",
id: tag ? tag.id : null, id: tag ? tag.id : null,
}); });
} }
searchItem_click(search) { searchItem_click(search) {
this.props.dispatch({ this.props.dispatch({
type: 'SEARCH_SELECT', type: "SEARCH_SELECT",
id: search ? search.id : null, id: search ? search.id : null,
}); });
} }
@ -184,105 +200,180 @@ class SideBarComponent extends React.Component {
if (folder.id === Folder.conflictFolderId()) style = Object.assign(style, this.style().conflictFolder); if (folder.id === Folder.conflictFolderId()) style = Object.assign(style, this.style().conflictFolder);
const onDragOver = (event, folder) => { const onDragOver = (event, folder) => {
if (event.dataTransfer.types.indexOf('text/x-jop-note-ids') >= 0) event.preventDefault(); if (event.dataTransfer.types.indexOf("text/x-jop-note-ids") >= 0) event.preventDefault();
} };
const onDrop = async (event, folder) => { const onDrop = async (event, folder) => {
if (event.dataTransfer.types.indexOf('text/x-jop-note-ids') < 0) return; if (event.dataTransfer.types.indexOf("text/x-jop-note-ids") < 0) return;
event.preventDefault(); event.preventDefault();
const noteIds = JSON.parse(event.dataTransfer.getData('text/x-jop-note-ids')); const noteIds = JSON.parse(event.dataTransfer.getData("text/x-jop-note-ids"));
for (let i = 0; i < noteIds.length; i++) { for (let i = 0; i < noteIds.length; i++) {
await Note.moveToFolder(noteIds[i], folder.id); await Note.moveToFolder(noteIds[i], folder.id);
} }
} };
const itemTitle = Folder.displayTitle(folder); const itemTitle = Folder.displayTitle(folder);
return <a return (
className="list-item" <a
onDragOver={(event) => { onDragOver(event, folder) } } className="list-item"
onDrop={(event) => { onDrop(event, folder) } } onDragOver={event => {
href="#" onDragOver(event, folder);
data-id={folder.id} }}
data-type={BaseModel.TYPE_FOLDER} onDrop={event => {
onContextMenu={(event) => this.itemContextMenu(event)} onDrop(event, folder);
key={folder.id} }}
style={style} onClick={() => {this.folderItem_click(folder)}}>{itemTitle} href="#"
</a> data-id={folder.id}
data-type={BaseModel.TYPE_FOLDER}
onContextMenu={event => this.itemContextMenu(event)}
key={folder.id}
style={style}
onClick={() => {
this.folderItem_click(folder);
}}
>
{itemTitle}
</a>
);
} }
tagItem(tag, selected) { tagItem(tag, selected) {
let style = Object.assign({}, this.style().listItem); let style = Object.assign({}, this.style().listItem);
if (selected) style = Object.assign(style, this.style().listItemSelected); if (selected) style = Object.assign(style, this.style().listItemSelected);
return <a className="list-item" href="#" data-id={tag.id} data-type={BaseModel.TYPE_TAG} onContextMenu={(event) => this.itemContextMenu(event)} key={tag.id} style={style} onClick={() => {this.tagItem_click(tag)}}>{Tag.displayTitle(tag)}</a> return (
<a
className="list-item"
href="#"
data-id={tag.id}
data-type={BaseModel.TYPE_TAG}
onContextMenu={event => this.itemContextMenu(event)}
key={tag.id}
style={style}
onClick={() => {
this.tagItem_click(tag);
}}
>
{Tag.displayTitle(tag)}
</a>
);
} }
searchItem(search, selected) { searchItem(search, selected) {
let style = Object.assign({}, this.style().listItem); let style = Object.assign({}, this.style().listItem);
if (selected) style = Object.assign(style, this.style().listItemSelected); if (selected) style = Object.assign(style, this.style().listItemSelected);
return <a className="list-item" href="#" data-id={search.id} data-type={BaseModel.TYPE_SEARCH} onContextMenu={(event) => this.itemContextMenu(event)} key={search.id} style={style} onClick={() => {this.searchItem_click(search)}}>{search.title}</a> return (
<a
className="list-item"
href="#"
data-id={search.id}
data-type={BaseModel.TYPE_SEARCH}
onContextMenu={event => this.itemContextMenu(event)}
key={search.id}
style={style}
onClick={() => {
this.searchItem_click(search);
}}
>
{search.title}
</a>
);
} }
makeDivider(key) { makeDivider(key) {
return <div style={{height:2, backgroundColor:'blue' }} key={key}></div> return <div style={{ height: 2, backgroundColor: "blue" }} key={key} />;
} }
makeHeader(key, label, iconName) { makeHeader(key, label, iconName) {
const style = this.style().header; const style = this.style().header;
const icon = <i style={{fontSize: style.fontSize * 1.2, marginRight: 5}} className={"fa " + iconName}></i> const icon = <i style={{ fontSize: style.fontSize * 1.2, marginRight: 5 }} className={"fa " + iconName} />;
return <div style={style} key={key}>{icon}{label}</div> return (
<div style={style} key={key}>
{icon}
{label}
</div>
);
} }
synchronizeButton(type) { synchronizeButton(type) {
const style = this.style().button; const style = this.style().button;
const iconName = type === 'sync' ? 'fa-refresh' : 'fa-times'; const iconName = type === "sync" ? "fa-refresh" : "fa-times";
const label = type === 'sync' ? _('Synchronise') : _('Cancel'); const label = type === "sync" ? _("Synchronise") : _("Cancel");
const icon = <i style={{fontSize: style.fontSize, marginRight: 5}} className={"fa " + iconName}></i> const icon = <i style={{ fontSize: style.fontSize, marginRight: 5 }} className={"fa " + iconName} />;
return <a className="synchronize-button" style={style} href="#" key="sync_button" onClick={() => {this.sync_click()}}>{icon}{label}</a> return (
<a
className="synchronize-button"
style={style}
href="#"
key="sync_button"
onClick={() => {
this.sync_click();
}}
>
{icon}
{label}
</a>
);
} }
render() { render() {
const theme = themeStyle(this.props.theme); const theme = themeStyle(this.props.theme);
const style = Object.assign({}, this.style().root, this.props.style, { const style = Object.assign({}, this.style().root, this.props.style, {
overflowX: 'hidden', overflowX: "hidden",
overflowY: 'auto', overflowY: "auto",
}); });
let items = []; let items = [];
items.push(this.makeHeader('folderHeader', _('Notebooks'), 'fa-folder-o')); items.push(this.makeHeader("folderHeader", _("Notebooks"), "fa-folder-o"));
if (this.props.folders.length) { if (this.props.folders.length) {
const folderItems = shared.renderFolders(this.props, this.folderItem.bind(this)); const folderItems = shared.renderFolders(this.props, this.folderItem.bind(this));
items = items.concat(folderItems); items = items.concat(folderItems);
} }
items.push(this.makeHeader('tagHeader', _('Tags'), 'fa-tags')); items.push(this.makeHeader("tagHeader", _("Tags"), "fa-tags"));
if (this.props.tags.length) { if (this.props.tags.length) {
const tagItems = shared.renderTags(this.props, this.tagItem.bind(this)); const tagItems = shared.renderTags(this.props, this.tagItem.bind(this));
items.push(<div className="tags" key="tag_items">{tagItems}</div>); items.push(
<div className="tags" key="tag_items">
{tagItems}
</div>
);
} }
if (this.props.searches.length) { // if (this.props.searches.length) {
items.push(this.makeHeader('searchHeader', _('Searches'), 'fa-search')); // items.push(this.makeHeader("searchHeader", _("Searches"), "fa-search"));
const searchItems = shared.renderSearches(this.props, this.searchItem.bind(this)); // const searchItems = shared.renderSearches(this.props, this.searchItem.bind(this));
items.push(<div className="searches" key="search_items">{searchItems}</div>); // items.push(
} // <div className="searches" key="search_items">
// {searchItems}
// </div>
// );
// }
let lines = Synchronizer.reportToLines(this.props.syncReport); let lines = Synchronizer.reportToLines(this.props.syncReport);
const syncReportText = []; const syncReportText = [];
for (let i = 0; i < lines.length; i++) { for (let i = 0; i < lines.length; i++) {
syncReportText.push(<div key={i}>{lines[i]}</div>); syncReportText.push(
<div key={i} style={{ wordWrap: "break-word", width: "100%" }}>
{lines[i]}
</div>
);
} }
items.push(this.synchronizeButton(this.props.syncStarted ? 'cancel' : 'sync')); items.push(this.synchronizeButton(this.props.syncStarted ? "cancel" : "sync"));
items.push(<div style={this.style().syncReport} key='sync_report'>{syncReportText}</div>); items.push(
<div style={this.style().syncReport} key="sync_report">
{syncReportText}
</div>
);
return ( return (
<div className="side-bar" style={style}> <div className="side-bar" style={style}>
@ -290,10 +381,9 @@ class SideBarComponent extends React.Component {
</div> </div>
); );
} }
} }
const mapStateToProps = (state) => { const mapStateToProps = state => {
return { return {
folders: state.folders, folders: state.folders,
tags: state.tags, tags: state.tags,
@ -311,4 +401,4 @@ const mapStateToProps = (state) => {
const SideBar = connect(mapStateToProps)(SideBarComponent); const SideBar = connect(mapStateToProps)(SideBarComponent);
module.exports = { SideBar }; module.exports = { SideBar };

View File

@ -1,6 +1,8 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="UTF-8">
<style> <style>
body { body {
overflow: hidden; overflow: hidden;
@ -13,12 +15,18 @@
padding-right: 10px; padding-right: 10px;
} }
mark {
background: #CF3F00;
color: white;
}
.katex { font-size: 1.3em; } /* This controls the global Katex font size*/ .katex { font-size: 1.3em; } /* This controls the global Katex font size*/
</style> </style>
</head> </head>
<body id="body"> <body id="body">
<div id="hlScriptContainer"></div> <div id="hlScriptContainer"></div>
<div id="markScriptContainer"></div>
<div id="content" ondragstart="return false;" ondrop="return false;"></div> <div id="content" ondragstart="return false;" ondrop="return false;"></div>
<script> <script>
@ -38,7 +46,7 @@
const script = document.createElement('script'); const script = document.createElement('script');
script.onload = function () { script.onload = function () {
hljsLoaded = true; hljsLoaded = true;
applyHljs(); applyHljs();
}; };
script.src = 'highlight/highlight.pack.js'; script.src = 'highlight/highlight.pack.js';
document.getElementById('hlScriptContainer').appendChild(script); document.getElementById('hlScriptContainer').appendChild(script);
@ -160,6 +168,36 @@
setPercentScroll(percent); setPercentScroll(percent);
}); });
let mark_ = null;
function setMarkers(keywords) {
if (!mark_) {
mark_ = new Mark(document.getElementById('content'), {
exclude: ['img'],
acrossElements: true,
});
}
mark_.mark(keywords);
}
let markLoaded_ = false;
ipcRenderer.on('setMarkers', (event, keywords) => {
if (!keywords.length && !markLoaded_) return;
if (!markLoaded_) {
const script = document.createElement('script');
script.onload = function() {
setMarkers(keywords);
};
script.src = '../../node_modules/mark.js/dist/mark.min.js';
document.getElementById('markScriptContainer').appendChild(script);
markLoaded_ = true;
} else {
setMarkers(keywords);
}
});
function maxScrollTop() { function maxScrollTop() {
return Math.max(0, contentElement.scrollHeight - contentElement.clientHeight); return Math.max(0, contentElement.scrollHeight - contentElement.clientHeight);
} }
@ -181,7 +219,11 @@
}); });
document.addEventListener('contextmenu', function(event) { document.addEventListener('contextmenu', function(event) {
const element = event.target; let element = event.target;
// To handle right clicks on resource icons
if (element && !element.getAttribute('data-resource-id')) element = element.parentElement;
if (element && element.getAttribute('data-resource-id')) { if (element && element.getAttribute('data-resource-id')) {
ipcRenderer.sendToHost('contextMenu', { ipcRenderer.sendToHost('contextMenu', {
type: element.getAttribute('src') ? 'image' : 'link', type: element.getAttribute('src') ? 'image' : 'link',

View File

@ -17,6 +17,10 @@
.smalltalk .page { .smalltalk .page {
max-width: 30em; max-width: 30em;
} }
mark {
background: #CF3F00;
color: white;
}
</style> </style>
</head> </head>
<body> <body>

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

View File

@ -1,5 +1,6 @@
var locales = {}; var locales = {};
locales['en_GB'] = require('./en_GB.json'); locales['en_GB'] = require('./en_GB.json');
locales['da_DK'] = require('./da_DK.json');
locales['de_DE'] = require('./de_DE.json'); locales['de_DE'] = require('./de_DE.json');
locales['es_ES'] = require('./es_ES.json'); locales['es_ES'] = require('./es_ES.json');
locales['eu'] = require('./eu.json'); locales['eu'] = require('./eu.json');

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

View File

@ -64,11 +64,17 @@ document.addEventListener('click', (event) => event.preventDefault());
app().start(bridge().processArgv()).then(() => { app().start(bridge().processArgv()).then(() => {
require('./gui/Root.min.js'); require('./gui/Root.min.js');
}).catch((error) => { }).catch((error) => {
// If something goes wrong at this stage we don't have a console or a log file if (error.code == 'flagError') {
// so display the error in a message box. bridge().showErrorMessageBox(error.message);
let msg = ['Fatal error:', error.message]; } else {
if (error.fileName) msg.push(error.fileName); // If something goes wrong at this stage we don't have a console or a log file
if (error.lineNumber) msg.push(error.lineNumber); // so display the error in a message box.
if (error.stack) msg.push(error.stack); let msg = ['Fatal error:', error.message];
bridge().showErrorMessageBox(msg.join('\n')); if (error.fileName) msg.push(error.fileName);
if (error.lineNumber) msg.push(error.lineNumber);
if (error.stack) msg.push(error.stack);
bridge().showErrorMessageBox(msg.join('\n\n'));
}
bridge().electronApp().exit(1);
}); });

View File

@ -1,6 +1,6 @@
{ {
"name": "Joplin", "name": "Joplin",
"version": "1.0.70", "version": "1.0.79",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -614,12 +614,14 @@
"bluebird": { "bluebird": {
"version": "3.5.1", "version": "3.5.1",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
"integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==",
"dev": true
}, },
"bluebird-lst": { "bluebird-lst": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.5.tgz", "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.5.tgz",
"integrity": "sha512-Ey0bDNys5qpYPhZ/oQ9vOEvD0TYQDTILMXWP2iGfvMg7rSDde+oV4aQQgqRH+CvBFNz2BSDQnPGMUl6LKBUUQA==", "integrity": "sha512-Ey0bDNys5qpYPhZ/oQ9vOEvD0TYQDTILMXWP2iGfvMg7rSDde+oV4aQQgqRH+CvBFNz2BSDQnPGMUl6LKBUUQA==",
"dev": true,
"requires": { "requires": {
"bluebird": "3.5.1" "bluebird": "3.5.1"
} }
@ -641,7 +643,7 @@
"requires": { "requires": {
"ansi-align": "2.0.0", "ansi-align": "2.0.0",
"camelcase": "4.1.0", "camelcase": "4.1.0",
"chalk": "2.3.1", "chalk": "2.3.2",
"cli-boxes": "1.0.0", "cli-boxes": "1.0.0",
"string-width": "2.1.1", "string-width": "2.1.1",
"term-size": "1.2.0", "term-size": "1.2.0",
@ -655,9 +657,9 @@
"dev": true "dev": true
}, },
"ansi-styles": { "ansi-styles": {
"version": "3.2.0", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true, "dev": true,
"requires": { "requires": {
"color-convert": "1.9.1" "color-convert": "1.9.1"
@ -670,14 +672,14 @@
"dev": true "dev": true
}, },
"chalk": { "chalk": {
"version": "2.3.1", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz",
"integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"ansi-styles": "3.2.0", "ansi-styles": "3.2.1",
"escape-string-regexp": "1.0.5", "escape-string-regexp": "1.0.5",
"supports-color": "5.2.0" "supports-color": "5.3.0"
} }
}, },
"is-fullwidth-code-point": { "is-fullwidth-code-point": {
@ -706,9 +708,9 @@
} }
}, },
"supports-color": { "supports-color": {
"version": "5.2.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz",
"integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==",
"dev": true, "dev": true,
"requires": { "requires": {
"has-flag": "3.0.0" "has-flag": "3.0.0"
@ -746,45 +748,77 @@
} }
}, },
"builder-util": { "builder-util": {
"version": "5.6.0", "version": "5.6.5",
"resolved": "https://registry.npmjs.org/builder-util/-/builder-util-5.6.0.tgz", "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-5.6.5.tgz",
"integrity": "sha512-5Enhnnm9gCHjzOUnVqqGjuMlx6pPA36VImQ9wgpRIIyfqLPXLVyWHOYbd0CThm/+GFMWx9xwAUyU6uL93+vwMg==", "integrity": "sha512-J03MEuyUf8li8KjTpml3r0K2Q9jZl21bHTEuGiZZX9vSMY81mXZe5AmEutsquyDrcsQFjCrDkbEDaqSc7vj5sw==",
"dev": true, "dev": true,
"requires": { "requires": {
"7zip-bin": "3.1.0", "7zip-bin": "3.1.0",
"app-builder-bin": "1.5.0", "app-builder-bin": "1.7.2",
"bluebird-lst": "1.0.5", "bluebird-lst": "1.0.5",
"builder-util-runtime": "4.0.5", "builder-util-runtime": "4.0.5",
"chalk": "2.3.1", "chalk": "2.3.2",
"debug": "3.1.0", "debug": "3.1.0",
"fs-extra-p": "4.5.2", "fs-extra-p": "4.5.2",
"is-ci": "1.1.0", "is-ci": "1.1.0",
"js-yaml": "3.10.0", "js-yaml": "3.11.0",
"lazy-val": "1.0.3", "lazy-val": "1.0.3",
"semver": "5.5.0", "semver": "5.5.0",
"source-map-support": "0.5.3", "source-map-support": "0.5.4",
"stat-mode": "0.2.2", "stat-mode": "0.2.2",
"temp-file": "3.1.1" "temp-file": "3.1.1"
}, },
"dependencies": { "dependencies": {
"ansi-styles": { "ansi-styles": {
"version": "3.2.0", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true, "dev": true,
"requires": { "requires": {
"color-convert": "1.9.1" "color-convert": "1.9.1"
} }
}, },
"chalk": { "app-builder-bin": {
"version": "2.3.1", "version": "1.7.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-1.7.2.tgz",
"integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", "integrity": "sha512-2uJICLdVnkDqizLZa4HclhBsAWiSf1sEPeKS5+GhuxGaDdWnabXZ4ed9hYQ5u81P3hW3lB+xvxDw2TTinDB9Tw==",
"dev": true, "dev": true,
"requires": { "requires": {
"ansi-styles": "3.2.0", "app-builder-bin-linux": "1.7.2",
"app-builder-bin-mac": "1.7.2",
"app-builder-bin-win": "1.7.2"
}
},
"app-builder-bin-linux": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/app-builder-bin-linux/-/app-builder-bin-linux-1.7.2.tgz",
"integrity": "sha512-spoW8f6sqo5aKpoZx+scIPMonSTrh8JtKWM3MuDqBJiXiUCtpVIPez5c4AycGwQnmh167KFjK4pn129o3k+aHQ==",
"dev": true,
"optional": true
},
"app-builder-bin-mac": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/app-builder-bin-mac/-/app-builder-bin-mac-1.7.2.tgz",
"integrity": "sha512-GLrQ9r17Hnc8dap2rKJ1N7ZukLBbTN88BSG4EC3xmNeafoWbekuxq3IdJYkZAT/eS1Ig4Q6nRcLI9TfnafwZEQ==",
"dev": true,
"optional": true
},
"app-builder-bin-win": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/app-builder-bin-win/-/app-builder-bin-win-1.7.2.tgz",
"integrity": "sha512-/7tvJZas9T5TBM3QUV0xQkRQAyUlsXdtUsqtOg48mgp1ogPqDjs4W2Jr31YhhiUHDdNgamZc655PzWqAEnbZfQ==",
"dev": true,
"optional": true
},
"chalk": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz",
"integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==",
"dev": true,
"requires": {
"ansi-styles": "3.2.1",
"escape-string-regexp": "1.0.5", "escape-string-regexp": "1.0.5",
"supports-color": "5.2.0" "supports-color": "5.3.0"
} }
}, },
"debug": { "debug": {
@ -796,22 +830,6 @@
"ms": "2.0.0" "ms": "2.0.0"
} }
}, },
"fs-extra-p": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/fs-extra-p/-/fs-extra-p-4.5.2.tgz",
"integrity": "sha512-ZYqFpBdy9w7PsK+vB30j+TnHOyWHm/CJbUq1qqoE8tb71m6qgk5Wa7gp3MYQdlGFxb9vfznF+yD4jcl8l+y91A==",
"dev": true,
"requires": {
"bluebird-lst": "1.0.5",
"fs-extra": "5.0.0"
}
},
"lazy-val": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.3.tgz",
"integrity": "sha512-pjCf3BYk+uv3ZcPzEVM0BFvO9Uw58TmlrU0oG5tTrr9Kcid3+kdKxapH8CjdYmVa2nO5wOoZn2rdvZx2PKj/xg==",
"dev": true
},
"semver": { "semver": {
"version": "5.5.0", "version": "5.5.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
@ -825,18 +843,18 @@
"dev": true "dev": true
}, },
"source-map-support": { "source-map-support": {
"version": "0.5.3", "version": "0.5.4",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.3.tgz", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.4.tgz",
"integrity": "sha512-eKkTgWYeBOQqFGXRfKabMFdnWepo51vWqEdoeikaEPFiJC7MCU5j2h4+6Q8npkZTeLGbSyecZvRxiSoWl3rh+w==", "integrity": "sha512-PETSPG6BjY1AHs2t64vS2aqAgu6dMIMXJULWFBGbh2Gr8nVLbCFDo6i/RMMvviIQ2h1Z8+5gQhVKSn2je9nmdg==",
"dev": true, "dev": true,
"requires": { "requires": {
"source-map": "0.6.1" "source-map": "0.6.1"
} }
}, },
"supports-color": { "supports-color": {
"version": "5.2.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz",
"integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==",
"dev": true, "dev": true,
"requires": { "requires": {
"has-flag": "3.0.0" "has-flag": "3.0.0"
@ -848,6 +866,7 @@
"version": "4.0.5", "version": "4.0.5",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-4.0.5.tgz", "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-4.0.5.tgz",
"integrity": "sha512-NT8AxWH6miZQHnZzaTVjVp1uc6C/mWlxi6GQXKpd4CwyTQd3rT7+poOGrcOhtIiHYCL9VEbRsVfxUAPPsgqJdg==", "integrity": "sha512-NT8AxWH6miZQHnZzaTVjVp1uc6C/mWlxi6GQXKpd4CwyTQd3rT7+poOGrcOhtIiHYCL9VEbRsVfxUAPPsgqJdg==",
"dev": true,
"requires": { "requires": {
"bluebird-lst": "1.0.5", "bluebird-lst": "1.0.5",
"debug": "3.1.0", "debug": "3.1.0",
@ -859,18 +878,10 @@
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": { "requires": {
"ms": "2.0.0" "ms": "2.0.0"
} }
},
"fs-extra-p": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/fs-extra-p/-/fs-extra-p-4.5.2.tgz",
"integrity": "sha512-ZYqFpBdy9w7PsK+vB30j+TnHOyWHm/CJbUq1qqoE8tb71m6qgk5Wa7gp3MYQdlGFxb9vfznF+yD4jcl8l+y91A==",
"requires": {
"bluebird-lst": "1.0.5",
"fs-extra": "5.0.0"
}
} }
} }
}, },
@ -966,9 +977,9 @@
"dev": true "dev": true
}, },
"ci-info": { "ci-info": {
"version": "1.1.2", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.1.2.tgz", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.1.3.tgz",
"integrity": "sha512-uTGIPNx/nSpBdsF6xnseRXLLtfr9VLqkz8ZqHXr3Y7b6SftyRxBGjwMtJj1OhNbmlc1wZzLNAlAcvyIiE8a6ZA==", "integrity": "sha512-SK/846h/Rcy8q9Z9CAwGBLfCJ6EkjJWdpelWDufQpqVDYq2Wnnv8zlSO6AMQap02jvhVruKKpEtQOufo3pFhLg==",
"dev": true "dev": true
}, },
"cli-boxes": { "cli-boxes": {
@ -1084,6 +1095,11 @@
"integrity": "sha1-AWLsLZNR9d3VmpICy6k1NmpyUIA=", "integrity": "sha1-AWLsLZNR9d3VmpICy6k1NmpyUIA=",
"dev": true "dev": true
}, },
"compare-versions": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.1.0.tgz",
"integrity": "sha512-4hAxDSBypT/yp2ySFD346So6Ragw5xmBn/e/agIGl3bZr6DLUqnoRZPusxKrXdYRZpgexO9daejmIenlq/wrIQ=="
},
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@ -1163,7 +1179,7 @@
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
"dev": true, "dev": true,
"requires": { "requires": {
"lru-cache": "4.1.1", "lru-cache": "4.1.2",
"shebang-command": "1.2.0", "shebang-command": "1.2.0",
"which": "1.3.0" "which": "1.3.0"
} }
@ -1272,31 +1288,19 @@
} }
}, },
"dmg-builder": { "dmg-builder": {
"version": "4.1.1", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-4.1.1.tgz", "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-4.1.2.tgz",
"integrity": "sha512-AhRa1J1coSVIUE2KpmievfIA8WI3G1Rxhf7qJYkiR5XWkJYp+6W1Z7vaCabRwEtEYyeJ0M8EdZnFT2BCYxEYVA==", "integrity": "sha512-nH7PYrRG9er8sVEOEV9lhngRyZDznViBFVTV/E5p4ZDFy5YZLDHNFWI3m7RKiWnRli5UpOwqLVn0Nxn1vJqsGg==",
"dev": true, "dev": true,
"requires": { "requires": {
"bluebird-lst": "1.0.5", "bluebird-lst": "1.0.5",
"builder-util": "5.6.0", "builder-util": "5.6.5",
"electron-builder-lib": "20.2.0", "electron-builder-lib": "20.5.1",
"fs-extra-p": "4.5.2", "fs-extra-p": "4.5.2",
"iconv-lite": "0.4.19", "iconv-lite": "0.4.19",
"js-yaml": "3.10.0", "js-yaml": "3.11.0",
"parse-color": "1.0.0", "parse-color": "1.0.0",
"sanitize-filename": "1.6.1" "sanitize-filename": "1.6.1"
},
"dependencies": {
"fs-extra-p": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/fs-extra-p/-/fs-extra-p-4.5.2.tgz",
"integrity": "sha512-ZYqFpBdy9w7PsK+vB30j+TnHOyWHm/CJbUq1qqoE8tb71m6qgk5Wa7gp3MYQdlGFxb9vfznF+yD4jcl8l+y91A==",
"dev": true,
"requires": {
"bluebird-lst": "1.0.5",
"fs-extra": "5.0.0"
}
}
} }
}, },
"dot-prop": { "dot-prop": {
@ -1353,17 +1357,17 @@
} }
}, },
"electron-builder": { "electron-builder": {
"version": "20.2.0", "version": "20.5.1",
"resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-20.2.0.tgz", "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-20.5.1.tgz",
"integrity": "sha512-gl+veD9FqunV5oGwBVhoHlHeHbVeXYiLoMw8/Cv3b91gC7XuXHoZ3oGbgaVGgycjm7suW8O6QXYaFujE8osnfw==", "integrity": "sha512-TBBWDXnqrRjTKAqnBJU/fxSxcwHjnXepTBVUEG02TZPUp7jQEdZjdvZDRvK0jM2xsGrL7q/UUS4jFqe4tRIF6g==",
"dev": true, "dev": true,
"requires": { "requires": {
"bluebird-lst": "1.0.5", "bluebird-lst": "1.0.5",
"builder-util": "5.6.0", "builder-util": "5.6.5",
"builder-util-runtime": "4.0.5", "builder-util-runtime": "4.0.5",
"chalk": "2.3.1", "chalk": "2.3.2",
"dmg-builder": "4.1.1", "dmg-builder": "4.1.2",
"electron-builder-lib": "20.2.0", "electron-builder-lib": "20.5.1",
"electron-download-tf": "4.3.4", "electron-download-tf": "4.3.4",
"fs-extra-p": "4.5.2", "fs-extra-p": "4.5.2",
"is-ci": "1.1.0", "is-ci": "1.1.0",
@ -1375,23 +1379,23 @@
}, },
"dependencies": { "dependencies": {
"ansi-styles": { "ansi-styles": {
"version": "3.2.0", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true, "dev": true,
"requires": { "requires": {
"color-convert": "1.9.1" "color-convert": "1.9.1"
} }
}, },
"chalk": { "chalk": {
"version": "2.3.1", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz",
"integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"ansi-styles": "3.2.0", "ansi-styles": "3.2.1",
"escape-string-regexp": "1.0.5", "escape-string-regexp": "1.0.5",
"supports-color": "5.2.0" "supports-color": "5.3.0"
} }
}, },
"debug": { "debug": {
@ -1431,35 +1435,6 @@
"universalify": "0.1.1" "universalify": "0.1.1"
} }
}, },
"fs-extra-p": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/fs-extra-p/-/fs-extra-p-4.5.2.tgz",
"integrity": "sha512-ZYqFpBdy9w7PsK+vB30j+TnHOyWHm/CJbUq1qqoE8tb71m6qgk5Wa7gp3MYQdlGFxb9vfznF+yD4jcl8l+y91A==",
"dev": true,
"requires": {
"bluebird-lst": "1.0.5",
"fs-extra": "5.0.0"
},
"dependencies": {
"fs-extra": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz",
"integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==",
"dev": true,
"requires": {
"graceful-fs": "4.1.11",
"jsonfile": "4.0.0",
"universalify": "0.1.1"
}
}
}
},
"lazy-val": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.3.tgz",
"integrity": "sha512-pjCf3BYk+uv3ZcPzEVM0BFvO9Uw58TmlrU0oG5tTrr9Kcid3+kdKxapH8CjdYmVa2nO5wOoZn2rdvZx2PKj/xg==",
"dev": true
},
"minimist": { "minimist": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
@ -1487,9 +1462,9 @@
} }
}, },
"supports-color": { "supports-color": {
"version": "5.2.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz",
"integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==",
"dev": true, "dev": true,
"requires": { "requires": {
"has-flag": "3.0.0" "has-flag": "3.0.0"
@ -1498,27 +1473,27 @@
} }
}, },
"electron-builder-lib": { "electron-builder-lib": {
"version": "20.2.0", "version": "20.5.1",
"resolved": "https://registry.npmjs.org/electron-builder-lib/-/electron-builder-lib-20.2.0.tgz", "resolved": "https://registry.npmjs.org/electron-builder-lib/-/electron-builder-lib-20.5.1.tgz",
"integrity": "sha512-bHESbb/OjO0F+tyUAj2wFXVDpuXweB5YR94/f7CKqdpd7k2LeYJvy+cYtgtVXt4CJyg5Vs4Kmak2VvDfWxbO/A==", "integrity": "sha512-YAUu4KHZQNFPdHqnvwOHmYWmqnwiExKuB4fnETZl5jmN3ZUgxCQFqWdwIGQWoAIdxtkQxTervJMt+uJ/wJWbZA==",
"dev": true, "dev": true,
"requires": { "requires": {
"7zip-bin": "3.1.0", "7zip-bin": "3.1.0",
"app-builder-bin": "1.5.0", "app-builder-bin": "1.7.2",
"async-exit-hook": "2.0.1", "async-exit-hook": "2.0.1",
"bluebird-lst": "1.0.5", "bluebird-lst": "1.0.5",
"builder-util": "5.6.0", "builder-util": "5.6.5",
"builder-util-runtime": "4.0.5", "builder-util-runtime": "4.0.5",
"chromium-pickle-js": "0.2.0", "chromium-pickle-js": "0.2.0",
"debug": "3.1.0", "debug": "3.1.0",
"ejs": "2.5.7", "ejs": "2.5.7",
"electron-osx-sign": "0.4.8", "electron-osx-sign": "0.4.10",
"electron-publish": "20.2.0", "electron-publish": "20.5.0",
"fs-extra-p": "4.5.2", "fs-extra-p": "4.5.2",
"hosted-git-info": "2.5.0", "hosted-git-info": "2.6.0",
"is-ci": "1.1.0", "is-ci": "1.1.0",
"isbinaryfile": "3.0.2", "isbinaryfile": "3.0.2",
"js-yaml": "3.10.0", "js-yaml": "3.11.0",
"lazy-val": "1.0.3", "lazy-val": "1.0.3",
"minimatch": "3.0.4", "minimatch": "3.0.4",
"normalize-package-data": "2.4.0", "normalize-package-data": "2.4.0",
@ -1529,6 +1504,38 @@
"temp-file": "3.1.1" "temp-file": "3.1.1"
}, },
"dependencies": { "dependencies": {
"app-builder-bin": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-1.7.2.tgz",
"integrity": "sha512-2uJICLdVnkDqizLZa4HclhBsAWiSf1sEPeKS5+GhuxGaDdWnabXZ4ed9hYQ5u81P3hW3lB+xvxDw2TTinDB9Tw==",
"dev": true,
"requires": {
"app-builder-bin-linux": "1.7.2",
"app-builder-bin-mac": "1.7.2",
"app-builder-bin-win": "1.7.2"
}
},
"app-builder-bin-linux": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/app-builder-bin-linux/-/app-builder-bin-linux-1.7.2.tgz",
"integrity": "sha512-spoW8f6sqo5aKpoZx+scIPMonSTrh8JtKWM3MuDqBJiXiUCtpVIPez5c4AycGwQnmh167KFjK4pn129o3k+aHQ==",
"dev": true,
"optional": true
},
"app-builder-bin-mac": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/app-builder-bin-mac/-/app-builder-bin-mac-1.7.2.tgz",
"integrity": "sha512-GLrQ9r17Hnc8dap2rKJ1N7ZukLBbTN88BSG4EC3xmNeafoWbekuxq3IdJYkZAT/eS1Ig4Q6nRcLI9TfnafwZEQ==",
"dev": true,
"optional": true
},
"app-builder-bin-win": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/app-builder-bin-win/-/app-builder-bin-win-1.7.2.tgz",
"integrity": "sha512-/7tvJZas9T5TBM3QUV0xQkRQAyUlsXdtUsqtOg48mgp1ogPqDjs4W2Jr31YhhiUHDdNgamZc655PzWqAEnbZfQ==",
"dev": true,
"optional": true
},
"debug": { "debug": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
@ -1538,20 +1545,10 @@
"ms": "2.0.0" "ms": "2.0.0"
} }
}, },
"fs-extra-p": { "hosted-git-info": {
"version": "4.5.2", "version": "2.6.0",
"resolved": "https://registry.npmjs.org/fs-extra-p/-/fs-extra-p-4.5.2.tgz", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz",
"integrity": "sha512-ZYqFpBdy9w7PsK+vB30j+TnHOyWHm/CJbUq1qqoE8tb71m6qgk5Wa7gp3MYQdlGFxb9vfznF+yD4jcl8l+y91A==", "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==",
"dev": true,
"requires": {
"bluebird-lst": "1.0.5",
"fs-extra": "5.0.0"
}
},
"lazy-val": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.3.tgz",
"integrity": "sha512-pjCf3BYk+uv3ZcPzEVM0BFvO9Uw58TmlrU0oG5tTrr9Kcid3+kdKxapH8CjdYmVa2nO5wOoZn2rdvZx2PKj/xg==",
"dev": true "dev": true
}, },
"semver": { "semver": {
@ -1642,15 +1639,10 @@
"resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-0.1.2.tgz", "resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-0.1.2.tgz",
"integrity": "sha1-ihBD4ys6HaHD9VPc4oznZCRhZ+M=" "integrity": "sha1-ihBD4ys6HaHD9VPc4oznZCRhZ+M="
}, },
"electron-log": {
"version": "2.2.11",
"resolved": "https://registry.npmjs.org/electron-log/-/electron-log-2.2.11.tgz",
"integrity": "sha512-e+DHJkLJoaawDxyy9i2SMIViG/GV12DUE3T9a6yMdFp695c2u/kWYUZbUy31RzgX9uC/JuewXrO54tXdANgRRQ=="
},
"electron-osx-sign": { "electron-osx-sign": {
"version": "0.4.8", "version": "0.4.10",
"resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.4.8.tgz", "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.4.10.tgz",
"integrity": "sha1-8Ln63e2eHlTsNfqJh3tcbDTHvEA=", "integrity": "sha1-vk87ibKnWh3F8eckkIGrKSnKOiY=",
"dev": true, "dev": true,
"requires": { "requires": {
"bluebird": "3.5.1", "bluebird": "3.5.1",
@ -1670,56 +1662,40 @@
} }
}, },
"electron-publish": { "electron-publish": {
"version": "20.2.0", "version": "20.5.0",
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-20.2.0.tgz", "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-20.5.0.tgz",
"integrity": "sha512-n8MEDVSYXi8ZC8sHJMoSzTrOrV6X+6cWmyQP4M6nh0RZaLcyPa/txWuHDeRNysvhOKJvgQJrf09Fuc+CMSY6zg==", "integrity": "sha512-BejALjAMG0QbjJNjN66pruwhWt07Iy86VBDxHWXO9IoupYykCEyFdy20jjl5rNTpfnojvynqZyj8QpRkNjJBZg==",
"dev": true, "dev": true,
"requires": { "requires": {
"bluebird-lst": "1.0.5", "bluebird-lst": "1.0.5",
"builder-util": "5.6.0", "builder-util": "5.6.5",
"builder-util-runtime": "4.0.5", "builder-util-runtime": "4.0.5",
"chalk": "2.3.1", "chalk": "2.3.2",
"fs-extra-p": "4.5.2", "fs-extra-p": "4.5.2",
"lazy-val": "1.0.3", "lazy-val": "1.0.3",
"mime": "2.2.0" "mime": "2.2.0"
}, },
"dependencies": { "dependencies": {
"ansi-styles": { "ansi-styles": {
"version": "3.2.0", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true, "dev": true,
"requires": { "requires": {
"color-convert": "1.9.1" "color-convert": "1.9.1"
} }
}, },
"chalk": { "chalk": {
"version": "2.3.1", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz",
"integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"ansi-styles": "3.2.0", "ansi-styles": "3.2.1",
"escape-string-regexp": "1.0.5", "escape-string-regexp": "1.0.5",
"supports-color": "5.2.0" "supports-color": "5.3.0"
} }
}, },
"fs-extra-p": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/fs-extra-p/-/fs-extra-p-4.5.2.tgz",
"integrity": "sha512-ZYqFpBdy9w7PsK+vB30j+TnHOyWHm/CJbUq1qqoE8tb71m6qgk5Wa7gp3MYQdlGFxb9vfznF+yD4jcl8l+y91A==",
"dev": true,
"requires": {
"bluebird-lst": "1.0.5",
"fs-extra": "5.0.0"
}
},
"lazy-val": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.3.tgz",
"integrity": "sha512-pjCf3BYk+uv3ZcPzEVM0BFvO9Uw58TmlrU0oG5tTrr9Kcid3+kdKxapH8CjdYmVa2nO5wOoZn2rdvZx2PKj/xg==",
"dev": true
},
"mime": { "mime": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.2.0.tgz", "resolved": "https://registry.npmjs.org/mime/-/mime-2.2.0.tgz",
@ -1727,9 +1703,9 @@
"dev": true "dev": true
}, },
"supports-color": { "supports-color": {
"version": "5.2.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz",
"integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==",
"dev": true, "dev": true,
"requires": { "requires": {
"has-flag": "3.0.0" "has-flag": "3.0.0"
@ -1737,47 +1713,6 @@
} }
} }
}, },
"electron-updater": {
"version": "2.20.1",
"resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-2.20.1.tgz",
"integrity": "sha512-7LJBcHY0T8So5Q/NZqYlU43DPXfyVssGq9ObamXbSL01mRt/gdpcJlXPU1w3vA/L6tC1IMVI9cAulAjI2oSYAw==",
"requires": {
"bluebird-lst": "1.0.5",
"builder-util-runtime": "4.0.5",
"electron-is-dev": "0.3.0",
"fs-extra-p": "4.5.2",
"js-yaml": "3.10.0",
"lazy-val": "1.0.3",
"lodash.isequal": "4.5.0",
"semver": "5.5.0",
"source-map-support": "0.5.3"
},
"dependencies": {
"electron-is-dev": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-0.3.0.tgz",
"integrity": "sha1-FOb9pcaOnk7L7/nM8DfL18BcWv4="
},
"semver": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"source-map-support": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.3.tgz",
"integrity": "sha512-eKkTgWYeBOQqFGXRfKabMFdnWepo51vWqEdoeikaEPFiJC7MCU5j2h4+6Q8npkZTeLGbSyecZvRxiSoWl3rh+w==",
"requires": {
"source-map": "0.6.1"
}
}
}
},
"electron-window-state": { "electron-window-state": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/electron-window-state/-/electron-window-state-4.1.1.tgz", "resolved": "https://registry.npmjs.org/electron-window-state/-/electron-window-state-4.1.1.tgz",
@ -1848,7 +1783,8 @@
"esprima": { "esprima": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
"integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==" "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==",
"dev": true
}, },
"esutils": { "esutils": {
"version": "2.0.2", "version": "2.0.2",
@ -2095,6 +2031,7 @@
"version": "4.5.2", "version": "4.5.2",
"resolved": "https://registry.npmjs.org/fs-extra-p/-/fs-extra-p-4.5.2.tgz", "resolved": "https://registry.npmjs.org/fs-extra-p/-/fs-extra-p-4.5.2.tgz",
"integrity": "sha512-ZYqFpBdy9w7PsK+vB30j+TnHOyWHm/CJbUq1qqoE8tb71m6qgk5Wa7gp3MYQdlGFxb9vfznF+yD4jcl8l+y91A==", "integrity": "sha512-ZYqFpBdy9w7PsK+vB30j+TnHOyWHm/CJbUq1qqoE8tb71m6qgk5Wa7gp3MYQdlGFxb9vfznF+yD4jcl8l+y91A==",
"dev": true,
"requires": { "requires": {
"bluebird-lst": "1.0.5", "bluebird-lst": "1.0.5",
"fs-extra": "5.0.0" "fs-extra": "5.0.0"
@ -2559,8 +2496,7 @@
"jsbn": { "jsbn": {
"version": "0.1.1", "version": "0.1.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"json-schema": { "json-schema": {
"version": "0.2.3", "version": "0.2.3",
@ -3374,7 +3310,7 @@
"integrity": "sha512-c7TnwxLePuqIlxHgr7xtxzycJPegNHFuIrBkwbf8hc58//+Op1CqFkyS+xnIMkwn9UsJIwc174BIjkyBmSpjKg==", "integrity": "sha512-c7TnwxLePuqIlxHgr7xtxzycJPegNHFuIrBkwbf8hc58//+Op1CqFkyS+xnIMkwn9UsJIwc174BIjkyBmSpjKg==",
"dev": true, "dev": true,
"requires": { "requires": {
"ci-info": "1.1.2" "ci-info": "1.1.3"
} }
}, },
"is-dotfile": { "is-dotfile": {
@ -3590,9 +3526,10 @@
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
}, },
"js-yaml": { "js-yaml": {
"version": "3.10.0", "version": "3.11.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz",
"integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==",
"dev": true,
"requires": { "requires": {
"argparse": "1.0.9", "argparse": "1.0.9",
"esprima": "4.0.0" "esprima": "4.0.0"
@ -3698,7 +3635,8 @@
"lazy-val": { "lazy-val": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.3.tgz", "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.3.tgz",
"integrity": "sha512-pjCf3BYk+uv3ZcPzEVM0BFvO9Uw58TmlrU0oG5tTrr9Kcid3+kdKxapH8CjdYmVa2nO5wOoZn2rdvZx2PKj/xg==" "integrity": "sha512-pjCf3BYk+uv3ZcPzEVM0BFvO9Uw58TmlrU0oG5tTrr9Kcid3+kdKxapH8CjdYmVa2nO5wOoZn2rdvZx2PKj/xg==",
"dev": true
}, },
"lcid": { "lcid": {
"version": "1.0.0", "version": "1.0.0",
@ -3797,9 +3735,9 @@
"integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=" "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY="
}, },
"lru-cache": { "lru-cache": {
"version": "4.1.1", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz",
"integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", "integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"pseudomap": "1.0.2", "pseudomap": "1.0.2",
@ -3829,6 +3767,11 @@
"integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
"dev": true "dev": true
}, },
"mark.js": {
"version": "8.11.1",
"resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz",
"integrity": "sha1-GA8fnr74sOY45BZq1S24eb6y/8U="
},
"markdown-it": { "markdown-it": {
"version": "8.4.0", "version": "8.4.0",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.0.tgz", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.0.tgz",
@ -4602,38 +4545,9 @@
"dotenv": "5.0.1", "dotenv": "5.0.1",
"dotenv-expand": "4.2.0", "dotenv-expand": "4.2.0",
"fs-extra-p": "4.5.2", "fs-extra-p": "4.5.2",
"js-yaml": "3.10.0", "js-yaml": "3.11.0",
"json5": "0.5.1", "json5": "0.5.1",
"lazy-val": "1.0.3" "lazy-val": "1.0.3"
},
"dependencies": {
"ajv": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.2.0.tgz",
"integrity": "sha1-r6wpW7qgFSRJ5SJ0LkVHwa6TKNI=",
"dev": true,
"requires": {
"fast-deep-equal": "1.0.0",
"fast-json-stable-stringify": "2.0.0",
"json-schema-traverse": "0.3.1"
}
},
"fs-extra-p": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/fs-extra-p/-/fs-extra-p-4.5.2.tgz",
"integrity": "sha512-ZYqFpBdy9w7PsK+vB30j+TnHOyWHm/CJbUq1qqoE8tb71m6qgk5Wa7gp3MYQdlGFxb9vfznF+yD4jcl8l+y91A==",
"dev": true,
"requires": {
"bluebird-lst": "1.0.5",
"fs-extra": "5.0.0"
}
},
"lazy-val": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.3.tgz",
"integrity": "sha512-pjCf3BYk+uv3ZcPzEVM0BFvO9Uw58TmlrU0oG5tTrr9Kcid3+kdKxapH8CjdYmVa2nO5wOoZn2rdvZx2PKj/xg==",
"dev": true
}
} }
}, },
"read-pkg": { "read-pkg": {
@ -5908,24 +5822,6 @@
"bluebird-lst": "1.0.5", "bluebird-lst": "1.0.5",
"fs-extra-p": "4.5.2", "fs-extra-p": "4.5.2",
"lazy-val": "1.0.3" "lazy-val": "1.0.3"
},
"dependencies": {
"fs-extra-p": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/fs-extra-p/-/fs-extra-p-4.5.2.tgz",
"integrity": "sha512-ZYqFpBdy9w7PsK+vB30j+TnHOyWHm/CJbUq1qqoE8tb71m6qgk5Wa7gp3MYQdlGFxb9vfznF+yD4jcl8l+y91A==",
"dev": true,
"requires": {
"bluebird-lst": "1.0.5",
"fs-extra": "5.0.0"
}
},
"lazy-val": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.3.tgz",
"integrity": "sha512-pjCf3BYk+uv3ZcPzEVM0BFvO9Uw58TmlrU0oG5tTrr9Kcid3+kdKxapH8CjdYmVa2nO5wOoZn2rdvZx2PKj/xg==",
"dev": true
}
} }
}, },
"term-size": { "term-size": {
@ -6081,7 +5977,7 @@
"dev": true, "dev": true,
"requires": { "requires": {
"boxen": "1.3.0", "boxen": "1.3.0",
"chalk": "2.3.1", "chalk": "2.3.2",
"configstore": "3.1.1", "configstore": "3.1.1",
"import-lazy": "2.1.0", "import-lazy": "2.1.0",
"is-installed-globally": "0.1.0", "is-installed-globally": "0.1.0",
@ -6092,29 +5988,29 @@
}, },
"dependencies": { "dependencies": {
"ansi-styles": { "ansi-styles": {
"version": "3.2.0", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true, "dev": true,
"requires": { "requires": {
"color-convert": "1.9.1" "color-convert": "1.9.1"
} }
}, },
"chalk": { "chalk": {
"version": "2.3.1", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz",
"integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"ansi-styles": "3.2.0", "ansi-styles": "3.2.1",
"escape-string-regexp": "1.0.5", "escape-string-regexp": "1.0.5",
"supports-color": "5.2.0" "supports-color": "5.3.0"
} }
}, },
"supports-color": { "supports-color": {
"version": "5.2.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz",
"integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==",
"dev": true, "dev": true,
"requires": { "requires": {
"has-flag": "3.0.0" "has-flag": "3.0.0"

View File

@ -1,6 +1,6 @@
{ {
"name": "Joplin", "name": "Joplin",
"version": "1.0.70", "version": "1.0.79",
"description": "Joplin for Desktop", "description": "Joplin for Desktop",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {
@ -48,7 +48,7 @@
"babel-cli": "^6.26.0", "babel-cli": "^6.26.0",
"babel-preset-react": "^6.24.1", "babel-preset-react": "^6.24.1",
"electron": "^1.7.11", "electron": "^1.7.11",
"electron-builder": "^20.2.0" "electron-builder": "^20.5.1"
}, },
"optionalDependencies": { "optionalDependencies": {
"7zip-bin-mac": "^1.0.1", "7zip-bin-mac": "^1.0.1",
@ -59,9 +59,8 @@
"app-module-path": "^2.2.0", "app-module-path": "^2.2.0",
"async-mutex": "^0.1.3", "async-mutex": "^0.1.3",
"base-64": "^0.1.0", "base-64": "^0.1.0",
"compare-versions": "^3.1.0",
"electron-context-menu": "^0.9.1", "electron-context-menu": "^0.9.1",
"electron-log": "^2.2.11",
"electron-updater": "^2.18.2",
"electron-window-state": "^4.1.1", "electron-window-state": "^4.1.1",
"follow-redirects": "^1.2.5", "follow-redirects": "^1.2.5",
"form-data": "^2.3.1", "form-data": "^2.3.1",
@ -72,6 +71,7 @@
"katex": "^0.9.0-beta1", "katex": "^0.9.0-beta1",
"levenshtein": "^1.0.5", "levenshtein": "^1.0.5",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"mark.js": "^8.11.1",
"markdown-it": "^8.4.0", "markdown-it": "^8.4.0",
"markdown-it-katex": "^2.0.3", "markdown-it-katex": "^2.0.3",
"md5": "^2.2.1", "md5": "^2.2.1",

View File

@ -36,12 +36,14 @@ table td, table th {
.side-bar .list-item:hover, .side-bar .list-item:hover,
.side-bar .synchronize-button:hover { .side-bar .synchronize-button:hover {
background-color: #453E53; /*background-color: #453E53;*/
background-color: #01427B;
} }
.side-bar .list-item:active, .side-bar .list-item:active,
.side-bar .synchronize-button:active { .side-bar .synchronize-button:active {
background-color: #564B6C; /*background-color: #564B6C;*/
background-color: #0465BB;
} }
.editor-toolbar .button:not(.disabled):hover, .editor-toolbar .button:not(.disabled):hover,

View File

@ -1,7 +1,7 @@
const Setting = require('lib/models/Setting.js'); const Setting = require('lib/models/Setting.js');
const globalStyle = { const globalStyle = {
fontSize: 12 * Setting.value('style.zoom')/100, fontSize: 12 * Setting.value('style.zoom') / 100,
fontFamily: 'sans-serif', fontFamily: 'sans-serif',
margin: 15, // No text and no interactive component should be within this margin margin: 15, // No text and no interactive component should be within this margin
itemMarginTop: 10, itemMarginTop: 10,
@ -21,9 +21,11 @@ const globalStyle = {
buttonMinHeight: 30, buttonMinHeight: 30,
textAreaLineHeight: 17, textAreaLineHeight: 17,
backgroundColor2: "#2B2634", //backgroundColor2: "#2B2634",
backgroundColor2: "#162B3D",
color2: "#ffffff", color2: "#ffffff",
selectedColor2: "#5A4D70", //selectedColor2: "#5A4D70",
selectedColor2: "#0269C2",
colorError2: "#ff6c6c", colorError2: "#ff6c6c",
warningBackgroundColor: "#FFD08D", warningBackgroundColor: "#FFD08D",

View File

@ -1,9 +0,0 @@
License MIT
Copyright (c) 2016-2018 Laurent Cozic
L'autorisation est accordée, gracieusement, à toute personne acquérant une copie de ce logiciel et des fichiers de documentation associés (le "Logiciel"), de commercialiser le Logiciel sans restriction, notamment les droits d'utiliser, de copier, de modifier, de fusionner, de publier, de distribuer, de sous-licencier et/ou de vendre des copies du Logiciel, ainsi que d'autoriser les personnes auxquelles le Logiciel est fourni à le faire, sous réserve des conditions suivantes :
La déclaration de copyright ci-dessus et la présente autorisation doivent être incluses dans toutes copies ou parties substantielles du Logiciel.
LE LOGICIEL EST FOURNI "TEL QUEL", SANS GARANTIE D'AUCUNE SORTE, EXPLICITE OU IMPLICITE, NOTAMMENT SANS GARANTIE DE QUALITÉ MARCHANDE, D’ADÉQUATION À UN USAGE PARTICULIER ET D'ABSENCE DE CONTREFAÇON. EN AUCUN CAS, LES AUTEURS OU TITULAIRES DU DROIT D'AUTEUR NE SERONT RESPONSABLES DE TOUT DOMMAGE, RÉCLAMATION OU AUTRE RESPONSABILITÉ, QUE CE SOIT DANS LE CADRE D'UN CONTRAT, D'UN DÉLIT OU AUTRE, EN PROVENANCE DE, CONSÉCUTIF À OU EN RELATION AVEC LE LOGICIEL OU SON UTILISATION, OU AVEC D'AUTRES ÉLÉMENTS DU LOGICIEL.

View File

@ -1,10 +1,12 @@
# Joplin # Joplin
Joplin is a free, open source note taking and to-do application, which can handle a large number of notes organised into notebooks. The notes are searchable, can be copied, tagged and modified either from the applications directly or from your own text editor. The notes are in [Markdown format](https://daringfireball.net/projects/markdown/basics). [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=E8JMYD2LQ8MMA&lc=GB&item_name=Joplin+Development&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted) [![Donate with Bitcoin](https://en.cryptobadges.io/badge/micro/1AnbeRd5NZT1ssG93jXzaDoHwzgjQAHX3R)](https://en.cryptobadges.io/donate/1AnbeRd5NZT1ssG93jXzaDoHwzgjQAHX3R) [![Travis Build Status](https://travis-ci.org/laurent22/joplin.svg?branch=master)](https://travis-ci.org/laurent22/joplin) [![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/laurent22/joplin?branch=master&passingText=master%20-%20OK&svg=true)](https://ci.appveyor.com/project/laurent22/joplin)
Joplin is a free, open source note taking and to-do application, which can handle a large number of notes organised into notebooks. The notes are searchable, can be copied, tagged and modified either from the applications directly or from your own text editor. The notes are in [Markdown format](#markdown).
Notes exported from Evernote via .enex files [can be imported](#importing) into Joplin, including the formatted content (which is converted to Markdown), resources (images, attachments, etc.) and complete metadata (geolocation, updated time, created time, etc.). Plain Markdown files can also be imported. Notes exported from Evernote via .enex files [can be imported](#importing) into Joplin, including the formatted content (which is converted to Markdown), resources (images, attachments, etc.) and complete metadata (geolocation, updated time, created time, etc.). Plain Markdown files can also be imported.
The notes can be [synchronised](#synchronisation) with various targets including [Nextcloud](https://nextcloud.com/), the file system (for example with a network directory) or with Microsoft OneDrive. When synchronising the notes, notebooks, tags and other metadata are saved to plain text files which can be easily inspected, backed up and moved around. The notes can be [synchronised](#synchronisation) with various cloud services including [Nextcloud](https://nextcloud.com/), the file system (for example with a network directory) or with Microsoft OneDrive. When synchronising the notes, notebooks, tags and other metadata are saved to plain text files which can be easily inspected, backed up and moved around.
The UI of the terminal client is built on top of the great [terminal-kit](https://github.com/cronvel/terminal-kit) library, the desktop client using [Electron](https://electronjs.org/), and the Android client front end is done using [React Native](https://facebook.github.io/react-native/). The UI of the terminal client is built on top of the great [terminal-kit](https://github.com/cronvel/terminal-kit) library, the desktop client using [Electron](https://electronjs.org/), and the Android client front end is done using [React Native](https://facebook.github.io/react-native/).
@ -18,15 +20,15 @@ Three types of applications are available: for the **desktop** (Windows, macOS a
Operating System | Download Operating System | Download
-----------------|-------- -----------------|--------
Windows | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.70/Joplin-Setup-1.0.70.exe'><img alt='Get it on Windows' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeWindows.png'/></a> Windows | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.78/Joplin-Setup-1.0.78.exe'><img alt='Get it on Windows' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeWindows.png'/></a>
macOS | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.70/Joplin-1.0.70.dmg'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeMacOS.png'/></a> macOS | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.78/Joplin-1.0.78.dmg'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeMacOS.png'/></a>
Linux | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.70/Joplin-1.0.70-x86_64.AppImage'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeLinux.png'/></a> Linux | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.78/Joplin-1.0.78-x86_64.AppImage'><img alt='Get it on Linux' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeLinux.png'/></a>
## Mobile applications ## Mobile applications
Operating System | Download | Alt. Download 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://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeAndroid.png'/></a> | or [Download APK File](https://github.com/laurent22/joplin-android/releases/download/android-v1.0.103/joplin-v1.0.103.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://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeAndroid.png'/></a> | or [Download APK File](https://github.com/laurent22/joplin-android/releases/download/android-v1.0.112/joplin-v1.0.112.apk)
iOS | <a href='https://itunes.apple.com/us/app/joplin/id1315599797'><img alt='Get it on the App Store' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeIOS.png'/></a> | - iOS | <a href='https://itunes.apple.com/us/app/joplin/id1315599797'><img alt='Get it on the App Store' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeIOS.png'/></a> | -
## Terminal application ## Terminal application
@ -108,7 +110,9 @@ Currently, synchronisation is possible with Nextcloud and OneDrive (by default)
## Nextcloud synchronisation ## Nextcloud synchronisation
On the **desktop application** or **mobile application**, go to the config screen and select Nextcloud as the synchronisation target. Then input [the WebDAV URL](https://docs.nextcloud.com/server/9/user_manual/files/access_webdav.html), this is normally `https://example.com/nextcloud/remote.php/dav/files/USERNAME/Joplin` (make sure to create the "Joplin" directory in Nextcloud and to replace USERNAME by your Nextcloud username), and set the username and password. <img src="https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/nextcloud-logo-background.png" width="100" align="left"> <a href="https://nextcloud.com/">Nextcloud</a> is a self-hosted, private cloud solution. It can store documents, images and videos but also calendars, passwords and countless other things and can sync them to your laptop or phone. As you can host your own Nextcloud server, you own both the data on your device and infrastructure used for synchronisation. As such it is a good fit for Joplin. The platform is also well supported and with a strong community, so it is likely to be around for a while - since it's open source anyway, it is not a service that can be closed, it can exist on a server for as long as one chooses.
On the **desktop application** or **mobile application**, go to the config screen and select Nextcloud as the synchronisation target. Then input the WebDAV URL (to get it, click on Settings in the bottom left corner of the page, in Nextcloud), this is normally `https://example.com/nextcloud/remote.php/webdav/Joplin` (**make sure to create the "Joplin" directory in Nextcloud**), and set the username and password. If it does not work, please [see this explanation](https://github.com/laurent22/joplin/issues/61#issuecomment-373282608) for more details.
On the **terminal application**, you will need to set the `sync.target` config variable and all the `sync.5.path`, `sync.5.username` and `sync.5.password` config variables to, respectively the Nextcloud WebDAV URL, your username and your password. This can be done from the command line mode using: On the **terminal application**, you will need to set the `sync.target` config variable and all the `sync.5.path`, `sync.5.username` and `sync.5.password` config variables to, respectively the Nextcloud WebDAV URL, your username and your password. This can be done from the command line mode using:
@ -123,10 +127,11 @@ If synchronisation does not work, please consult the logs in the app profile dir
Select the "WebDAV" synchronisation target and follow the same instructions as for Nextcloud above. Select the "WebDAV" synchronisation target and follow the same instructions as for Nextcloud above.
WebDAV-compatible services that are known to work with Joplin: WebDAV-compatible services that are known to work with Joplin.
- [Box.com](https://www.box.com/) - [Box.com](https://www.box.com/)
- [DriveHQ](https://www.drivehq.com) - [DriveHQ](https://www.drivehq.com)
- [HiDrive](https://www.strato.fr/stockage-en-ligne/) from Strato. [Setup help](https://github.com/laurent22/joplin/issues/309)
- [OwnCloud](https://owncloud.org/) - [OwnCloud](https://owncloud.org/)
- [Seafile](https://www.seafile.com/) - [Seafile](https://www.seafile.com/)
- [Stack](https://www.transip.nl/stack/) - [Stack](https://www.transip.nl/stack/)
@ -200,6 +205,12 @@ Checkboxes can be added like so:
The checkboxes can then be ticked in the mobile and desktop applications. The checkboxes can then be ticked in the mobile and desktop applications.
# 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.
Please see the [donation page](http://joplin.cozic.net/donate/) for information on how to support the development of Joplin.
# Contributing # Contributing
Please see the guide for information on how to contribute to the development of Joplin: https://github.com/laurent22/joplin/blob/master/CONTRIBUTING.md Please see the guide for information on how to contribute to the development of Joplin: https://github.com/laurent22/joplin/blob/master/CONTRIBUTING.md
@ -222,27 +233,30 @@ Current translations:
<!-- LOCALE-TABLE-AUTO-GENERATED --> <!-- LOCALE-TABLE-AUTO-GENERATED -->
&nbsp; | Language | Po File | Last translator | Percent done &nbsp; | Language | Po File | Last translator | Percent done
---|---|---|---|--- ---|---|---|---|---
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/es/basque_country.png) | Basque | [eu](https://github.com/laurent22/joplin/blob/master/CliClient/locales/eu.po) | juan.abasolo@ehu.eus | 81% ![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/es/basque_country.png) | Basque | [eu](https://github.com/laurent22/joplin/blob/master/CliClient/locales/eu.po) | juan.abasolo@ehu.eus | 79%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/hr.png) | Croatian | [hr_HR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/hr_HR.po) | Hrvoje Mandić <trbuhom@net.hr> | 66% ![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/hr.png) | Croatian | [hr_HR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/hr_HR.po) | Hrvoje Mandić <trbuhom@net.hr> | 64%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/de.png) | Deutsch | [de_DE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/de_DE.po) | Tobias Strobel <git@strobeltobias.de> | 83% ![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/dk.png) | Dansk | [da_DK](https://github.com/laurent22/joplin/blob/master/CliClient/locales/da_DK.po) | | 99%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/de.png) | Deutsch | [de_DE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/de_DE.po) | Tobias Grasse <mail@tobias-grasse.net> | 98%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/gb.png) | English | [en_GB](https://github.com/laurent22/joplin/blob/master/CliClient/locales/en_GB.po) | | 100% ![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/gb.png) | English | [en_GB](https://github.com/laurent22/joplin/blob/master/CliClient/locales/en_GB.po) | | 100%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/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> | 93% ![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/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://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/fr.png) | Français | [fr_FR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/fr_FR.po) | Laurent Cozic | 100% ![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/fr.png) | Français | [fr_FR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/fr_FR.po) | Laurent Cozic | 100%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/it.png) | Italiano | [it_IT](https://github.com/laurent22/joplin/blob/master/CliClient/locales/it_IT.po) | | 68% ![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/it.png) | Italiano | [it_IT](https://github.com/laurent22/joplin/blob/master/CliClient/locales/it_IT.po) | | 66%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/be.png) | Nederlands | [nl_BE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nl_BE.po) | | 82% ![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/be.png) | Nederlands | [nl_BE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nl_BE.po) | | 79%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/br.png) | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/pt_BR.po) | | 66% ![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/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://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/ru.png) | Русский | [ru_RU](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ru_RU.po) | Artyom Karlov <artyom.karlov@gmail.com> | 85% ![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/ru.png) | Русский | [ru_RU](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ru_RU.po) | Artyom Karlov <artyom.karlov@gmail.com> | 99%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/cn.png) | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_CN.po) | RCJacH <RCJacH@outlook.com> | 68% ![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/cn.png) | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_CN.po) | RCJacH <RCJacH@outlook.com> | 66%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/jp.png) | 日本語 | [ja_JP](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ja_JP.po) | | 66% ![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/jp.png) | 日本語 | [ja_JP](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ja_JP.po) | | 64%
<!-- LOCALE-TABLE-AUTO-GENERATED --> <!-- LOCALE-TABLE-AUTO-GENERATED -->
# Known bugs # Known bugs
- Non-alphabetical characters such as Chinese or Arabic might create glitches in the terminal on Windows. This is a limitation of the current Windows console. - Non-alphabetical characters such as Chinese or Arabic might create glitches in the terminal on Windows. This is a limitation of the current Windows console.
- While the mobile can sync and load tags, it is not currently possible to create new ones. The desktop and terminal apps can create, delete and edit tags. - It is only possible to upload files of up to 4MB to OneDrive due to a limitation of [the API](https://docs.microsoft.com/en-gb/onedrive/developer/rest-api/api/driveitem_put_content) being currently used. There is currently no plan to support OneDrive "large file" API.
# License # License
MIT License
Copyright (c) 2016-2018 Laurent Cozic Copyright (c) 2016-2018 Laurent Cozic
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -1,4 +0,0 @@
# When I open a note in vim, the cursor is not visible
It seems to be due to the setting `set term=ansi` in .vimrc. Removing it should fix the issue. See https://github.com/laurent22/joplin/issues/147 for more information.

View File

@ -90,8 +90,8 @@ android {
applicationId "net.cozic.joplin" applicationId "net.cozic.joplin"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 22 targetSdkVersion 22
versionCode 2097281 versionCode 2097290
versionName "1.0.103" versionName "1.0.112"
ndk { ndk {
abiFilters "armeabi-v7a", "x86" abiFilters "armeabi-v7a", "x86"
} }

View File

@ -1,51 +1,3 @@
const { main } = require('./main.js'); const { main } = require('./main.js');
main(); main();
// const React = require('react'); const Component = React.Component;
// import {
// AppRegistry,
// StyleSheet,
// Text,
// View
// } from 'react-native';
// module.exports = default class Joplin extends Component {;
// render() {
// return (
// <View style={styles.container}>
// <Text style={styles.welcome}>
// Welcome to React Native!
// </Text>
// <Text style={styles.instructions}>
// To get started, edit index.ios.js
// </Text>
// <Text style={styles.instructions}>
// Press Cmd+R to reload,{'\n'}
// Cmd+D or shake for dev menu
// </Text>
// </View>
// );
// }
// }
// const styles = StyleSheet.create({
// container: {
// flex: 1,
// justifyContent: 'center',
// alignItems: 'center',
// backgroundColor: '#F5FCFF',
// },
// welcome: {
// fontSize: 20,
// textAlign: 'center',
// margin: 10,
// },
// instructions: {
// textAlign: 'center',
// color: '#333333',
// marginBottom: 5,
// },
// });
// AppRegistry.registerComponent('Joplin', () => Joplin);

View File

@ -21,6 +21,7 @@ const { shim } = require('lib/shim.js');
const { _, setLocale, defaultLocale, closestSupportedLocale } = require('lib/locale.js'); const { _, setLocale, defaultLocale, closestSupportedLocale } = require('lib/locale.js');
const os = require('os'); const os = require('os');
const fs = require('fs-extra'); const fs = require('fs-extra');
const JoplinError = require('lib/JoplinError');
const EventEmitter = require('events'); const EventEmitter = require('events');
const SyncTargetRegistry = require('lib/SyncTargetRegistry.js'); const SyncTargetRegistry = require('lib/SyncTargetRegistry.js');
const SyncTargetFilesystem = require('lib/SyncTargetFilesystem.js'); const SyncTargetFilesystem = require('lib/SyncTargetFilesystem.js');
@ -30,6 +31,7 @@ const SyncTargetNextcloud = require('lib/SyncTargetNextcloud.js');
const SyncTargetWebDAV = require('lib/SyncTargetWebDAV.js'); const SyncTargetWebDAV = require('lib/SyncTargetWebDAV.js');
const EncryptionService = require('lib/services/EncryptionService'); const EncryptionService = require('lib/services/EncryptionService');
const DecryptionWorker = require('lib/services/DecryptionWorker'); const DecryptionWorker = require('lib/services/DecryptionWorker');
const BaseService = require('lib/services/BaseService');
SyncTargetRegistry.addClass(SyncTargetFilesystem); SyncTargetRegistry.addClass(SyncTargetFilesystem);
SyncTargetRegistry.addClass(SyncTargetOneDrive); SyncTargetRegistry.addClass(SyncTargetOneDrive);
@ -95,14 +97,14 @@ class BaseApplication {
let nextArg = argv.length >= 2 ? argv[1] : null; let nextArg = argv.length >= 2 ? argv[1] : null;
if (arg == '--profile') { if (arg == '--profile') {
if (!nextArg) throw new Error(_('Usage: %s', '--profile <dir-path>')); if (!nextArg) throw new JoplinError(_('Usage: %s', '--profile <dir-path>'), 'flagError');
matched.profileDir = nextArg; matched.profileDir = nextArg;
argv.splice(0, 2); argv.splice(0, 2);
continue; continue;
} }
if (arg == '--env') { if (arg == '--env') {
if (!nextArg) throw new Error(_('Usage: %s', '--env <dev|prod>')); if (!nextArg) throw new JoplinError(_('Usage: %s', '--env <dev|prod>'), 'flagError');
matched.env = nextArg; matched.env = nextArg;
argv.splice(0, 2); argv.splice(0, 2);
continue; continue;
@ -133,14 +135,14 @@ class BaseApplication {
} }
if (arg == '--log-level') { if (arg == '--log-level') {
if (!nextArg) throw new Error(_('Usage: %s', '--log-level <none|error|warn|info|debug>')); if (!nextArg) throw new JoplinError(_('Usage: %s', '--log-level <none|error|warn|info|debug>'), 'flagError');
matched.logLevel = Logger.levelStringToId(nextArg); matched.logLevel = Logger.levelStringToId(nextArg);
argv.splice(0, 2); argv.splice(0, 2);
continue; continue;
} }
if (arg.length && arg[0] == '-') { if (arg.length && arg[0] == '-') {
throw new Error(_('Unknown flag: %s', arg)); throw new JoplinError(_('Unknown flag: %s', arg), 'flagError');
} else { } else {
break; break;
} }
@ -258,7 +260,7 @@ class BaseApplication {
const newState = store.getState(); const newState = store.getState();
let refreshNotes = false; let refreshNotes = false;
if (action.type == 'FOLDER_SELECT' || action.type === 'FOLDER_DELETE') { if (action.type == 'FOLDER_SELECT' || action.type === 'FOLDER_DELETE' || (action.type === 'SEARCH_UPDATE' && newState.notesParentType === 'Folder')) {
Setting.setValue('activeFolderId', newState.selectedFolderId); Setting.setValue('activeFolderId', newState.selectedFolderId);
this.currentFolder_ = newState.selectedFolderId ? await Folder.load(newState.selectedFolderId) : null; this.currentFolder_ = newState.selectedFolderId ? await Folder.load(newState.selectedFolderId) : null;
refreshNotes = true; refreshNotes = true;
@ -425,6 +427,7 @@ class BaseApplication {
setLocale(Setting.value('locale')); setLocale(Setting.value('locale'));
} }
BaseService.logger_ = this.logger_;
EncryptionService.instance().setLogger(this.logger_); EncryptionService.instance().setLogger(this.logger_);
BaseItem.encryptionService_ = EncryptionService.instance(); BaseItem.encryptionService_ = EncryptionService.instance();
DecryptionWorker.instance().setLogger(this.logger_); DecryptionWorker.instance().setLogger(this.logger_);

View File

@ -1,4 +1,3 @@
const { Log } = require('lib/log.js');
const { Database } = require('lib/database.js'); const { Database } = require('lib/database.js');
const { uuid } = require('lib/uuid.js'); const { uuid } = require('lib/uuid.js');
const { time } = require('lib/time-utils.js'); const { time } = require('lib/time-utils.js');
@ -122,15 +121,6 @@ class BaseModel {
return id.substr(0, 5); return id.substr(0, 5);
} }
// static minimalPartialId(id) {
// let length = 2;
// while (true) {
// const partialId = id.substr(0, length);
// const r = await this.db().selectOne('SELECT count(*) as total FROM `' + this.tableName() + '` WHERE `id` LIKE ?', [partialId + '%']);
// if (r['total'] <= 1) return partialId;
// }
// }
static loadByPartialId(partialId) { static loadByPartialId(partialId) {
return this.modelSelectAll('SELECT * FROM `' + this.tableName() + '` WHERE `id` LIKE ?', [partialId + '%']); return this.modelSelectAll('SELECT * FROM `' + this.tableName() + '` WHERE `id` LIKE ?', [partialId + '%']);
} }
@ -222,20 +212,6 @@ class BaseModel {
} }
if ('type_' in newModel) output.type_ = newModel.type_; if ('type_' in newModel) output.type_ = newModel.type_;
return output; return output;
// let output = {};
// let type = null;
// for (let n in newModel) {
// if (!newModel.hasOwnProperty(n)) continue;
// if (n == 'type_') {
// type = newModel[n];
// continue;
// }
// if (!(n in oldModel) || newModel[n] !== oldModel[n]) {
// output[n] = newModel[n];
// }
// }
// if (type !== null) output.type_ = type;
// return output;
} }
static diffObjectsFields(oldModel, newModel) { static diffObjectsFields(oldModel, newModel) {
@ -422,11 +398,10 @@ class BaseModel {
} }
output = this.filter(o); output = this.filter(o);
} catch (error) { } finally {
Log.error('Cannot save model', error); this.releaseSaveMutex(o, mutexRelease);
} }
this.releaseSaveMutex(o, mutexRelease);
return output; return output;
} }
@ -505,6 +480,8 @@ BaseModel.typeEnum_ = [
['TYPE_SEARCH', 7], ['TYPE_SEARCH', 7],
['TYPE_ALARM', 8], ['TYPE_ALARM', 8],
['TYPE_MASTER_KEY', 9], ['TYPE_MASTER_KEY', 9],
['TYPE_ITEM_CHANGE', 10],
['TYPE_NOTE_RESOURCE', 11],
]; ];
for (let i = 0; i < BaseModel.typeEnum_.length; i++) { for (let i = 0; i < BaseModel.typeEnum_.length; i++) {
@ -512,16 +489,6 @@ for (let i = 0; i < BaseModel.typeEnum_.length; i++) {
BaseModel[e[0]] = e[1]; BaseModel[e[0]] = e[1];
} }
// BaseModel.TYPE_NOTE = 1;
// BaseModel.TYPE_FOLDER = 2;
// BaseModel.TYPE_SETTING = 3;
// BaseModel.TYPE_RESOURCE = 4;
// BaseModel.TYPE_TAG = 5;
// BaseModel.TYPE_NOTE_TAG = 6;
// BaseModel.TYPE_SEARCH = 7;
// BaseModel.TYPE_ALARM = 8;
// BaseModel.TYPE_MASTER_KEY = 9;
BaseModel.db_ = null; BaseModel.db_ = null;
BaseModel.dispatch = function(o) {}; BaseModel.dispatch = function(o) {};
BaseModel.saveMutexes_ = {}; BaseModel.saveMutexes_ = {};

View File

@ -3,7 +3,9 @@ const Entities = require('html-entities').AllHtmlEntities;
const htmlentities = (new Entities()).encode; const htmlentities = (new Entities()).encode;
const Resource = require('lib/models/Resource.js'); const Resource = require('lib/models/Resource.js');
const ModelCache = require('lib/ModelCache'); const ModelCache = require('lib/ModelCache');
const ObjectUtils = require('lib/ObjectUtils');
const { shim } = require('lib/shim.js'); const { shim } = require('lib/shim.js');
const { _ } = require('lib/locale');
const md5 = require('md5'); const md5 = require('md5');
const MdToHtml_Katex = require('lib/MdToHtml_Katex'); const MdToHtml_Katex = require('lib/MdToHtml_Katex');
@ -54,11 +56,11 @@ class MdToHtml {
return output.join(' '); return output.join(' ');
} }
getAttr_(attrs, name) { getAttr_(attrs, name, defaultValue = null) {
for (let i = 0; i < attrs.length; i++) { for (let i = 0; i < attrs.length; i++) {
if (attrs[i][0] === name) return attrs[i].length > 1 ? attrs[i][1] : null; if (attrs[i][0] === name) return attrs[i].length > 1 ? attrs[i][1] : null;
} }
return null; return defaultValue;
} }
setAttr_(attrs, name, value) { setAttr_(attrs, name, value) {
@ -135,13 +137,17 @@ class MdToHtml {
// Ideally they should be opened in the user's browser. // Ideally they should be opened in the user's browser.
return '<span style="opacity: 0.5">(Resource not yet supported: '; //+ htmlentities(text) + ']'; return '<span style="opacity: 0.5">(Resource not yet supported: '; //+ htmlentities(text) + ']';
} else { } else {
let resourceIdAttr = "";
let icon = "";
if (isResourceUrl) { if (isResourceUrl) {
const resourceId = Resource.pathToId(href); const resourceId = Resource.pathToId(href);
href = 'joplin://' + resourceId; href = "joplin://" + resourceId;
resourceIdAttr = "data-resource-id='" + resourceId + "'";
icon = '<span class="resource-icon"></span>';
} }
const js = options.postMessageSyntax + "(" + JSON.stringify(href) + "); return false;"; const js = options.postMessageSyntax + "(" + JSON.stringify(href) + "); return false;";
let output = "<a title='" + htmlentities(title) + "' href='#' onclick='" + js + "'>"; let output = "<a " + resourceIdAttr + " title='" + htmlentities(title) + "' href='#' onclick='" + js + "'>" + icon;
return output; return output;
} }
} }
@ -178,11 +184,23 @@ class MdToHtml {
return null; return null;
} }
urldecode_(str) {
try {
return decodeURIComponent((str+'').replace(/\+/g, '%20'));
} catch (error) {
// decodeURIComponent can throw if the string contains non-encoded data (for example "100%")
// so in this case just return the non encoded string.
return str;
}
}
renderTokens_(markdownIt, tokens, options) { renderTokens_(markdownIt, tokens, options) {
let output = []; let output = [];
let previousToken = null; let previousToken = null;
let anchorAttrs = []; let anchorAttrs = [];
let extraCssBlocks = {}; let extraCssBlocks = {};
let anchorHrefs = [];
for (let i = 0; i < tokens.length; i++) { for (let i = 0; i < tokens.length; i++) {
let t = tokens[i]; let t = tokens[i];
@ -192,12 +210,13 @@ class MdToHtml {
let openTag = null; let openTag = null;
let closeTag = null; let closeTag = null;
let attrs = t.attrs ? t.attrs : []; let attrs = t.attrs ? t.attrs : [];
let tokenContent = t.content ? t.content : null; let tokenContent = t.content ? t.content : '';
const isCodeBlock = tag === 'code' && t.block; const isCodeBlock = tag === 'code' && t.block;
const isInlineCode = t.type === 'code_inline'; const isInlineCode = t.type === 'code_inline';
const codeBlockLanguage = t && t.info ? t.info : null; const codeBlockLanguage = t && t.info ? t.info : null;
let rendererPlugin = null; let rendererPlugin = null;
let rendererPluginOptions = { tagType: 'inline' }; let rendererPluginOptions = { tagType: 'inline' };
let linkHref = null;
if (isCodeBlock) rendererPlugin = this.rendererPlugin_(codeBlockLanguage); if (isCodeBlock) rendererPlugin = this.rendererPlugin_(codeBlockLanguage);
@ -229,6 +248,7 @@ class MdToHtml {
if (openTag) { if (openTag) {
if (openTag === 'a') { if (openTag === 'a') {
anchorAttrs.push(attrs); anchorAttrs.push(attrs);
anchorHrefs.push(this.getAttr_(attrs, 'href'));
output.push(this.renderOpenLink_(attrs, options)); output.push(this.renderOpenLink_(attrs, options));
} else { } else {
const attrsHtml = this.renderAttrs_(attrs); const attrsHtml = this.renderAttrs_(attrs);
@ -313,7 +333,28 @@ class MdToHtml {
if (closeTag) { if (closeTag) {
if (closeTag === 'a') { if (closeTag === 'a') {
output.push(this.renderCloseLink_(anchorAttrs.pop(), options)); const currentAnchorAttrs = anchorAttrs.pop();
const previousContent = output.length ? output[output.length - 1].trim() : '';
const anchorHref = this.getAttr_(currentAnchorAttrs, 'href', '').trim();
// Optimisation: If the content of the anchor is the same as the URL, we replace the content
// by (Link). This is to shorten the text, which is important especially when the note comes
// from imported HTML, which can contain many such links and make the text unreadble. An example
// would be a movie review that has multiple links to allow a user to rate the film from 1 to 5 stars.
// In the original page, it might be rendered as stars, via CSS, but in the imported note it would look like this:
// http://example.com/rate/1 http://example.com/rate/2 http://example.com/rate/3
// http://example.com/rate/4 http://example.com/rate/5
// which would take a lot of screen space even though it doesn't matter since the user is unlikely
// to rate the film from the note. This is actually a nice example, still readable, but there is way
// worse that this in notes that come from web-clipped content.
// With this change, the links will still be preserved but displayed like
// (link) (link) (link) (link) (link)
if (this.urldecode_(previousContent) === htmlentities(this.urldecode_(anchorHref))) {
output.pop();
output.push(_('(Link)'));
}
output.push(this.renderCloseLink_(currentAnchorAttrs, options));
} else { } else {
output.push('</' + closeTag + '>'); output.push('</' + closeTag + '>');
} }
@ -332,14 +373,16 @@ class MdToHtml {
// Insert the extra CSS at the top of the HTML // Insert the extra CSS at the top of the HTML
const temp = ['<style>']; if (!ObjectUtils.isEmpty(extraCssBlocks)) {
for (let n in extraCssBlocks) { const temp = ['<style>'];
if (!extraCssBlocks.hasOwnProperty(n)) continue; for (let n in extraCssBlocks) {
temp.push(extraCssBlocks[n]); if (!extraCssBlocks.hasOwnProperty(n)) continue;
} temp.push(extraCssBlocks[n]);
temp.push('</style>'); }
temp.push('</style>');
output = temp.concat(output); output = temp.concat(output);
}
return output.join(''); return output.join('');
} }
@ -394,7 +437,6 @@ class MdToHtml {
let loopCount = 0; let loopCount = 0;
while (renderedBody.indexOf('mJOPm') >= 0) { while (renderedBody.indexOf('mJOPm') >= 0) {
renderedBody = renderedBody.replace(/mJOPmCHECKBOXm([A-Z]+)m(\d+)m/, function(v, type, index) { renderedBody = renderedBody.replace(/mJOPmCHECKBOXm([A-Z]+)m(\d+)m/, function(v, type, index) {
const js = options.postMessageSyntax + "('checkboxclick:" + type + ':' + index + "'); this.classList.contains('tick') ? this.classList.remove('tick') : this.classList.add('tick'); return false;"; const js = options.postMessageSyntax + "('checkboxclick:" + type + ':' + index + "'); this.classList.contains('tick') ? this.classList.remove('tick') : this.classList.add('tick'); return false;";
return '<a href="#" onclick="' + js + '" class="checkbox ' + (type == 'NOTICK' ? '' : 'tick') + '"><span>' + '' + '</span></a>'; return '<a href="#" onclick="' + js + '" class="checkbox ' + (type == 'NOTICK' ? '' : 'tick') + '"><span>' + '' + '</span></a>';
}); });
@ -443,6 +485,18 @@ class MdToHtml {
ul { ul {
padding-left: 1.3em; padding-left: 1.3em;
} }
.resource-icon {
display: inline-block;
position: relative;
top: .5em;
text-decoration: none;
width: 1.15em;
height: 1.5em;
margin-right: 0.4em;
background-color: ` + style.htmlColor + `;
/* Awesome Font file */
-webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 384 512'><path d='M369.9 97.9L286 14C277 5 264.8-.1 252.1-.1H48C21.5 0 0 21.5 0 48v416c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48V131.9c0-12.7-5.1-25-14.1-34zM332.1 128H256V51.9l76.1 76.1zM48 464V48h160v104c0 13.3 10.7 24 24 24h104v288H48z'/></svg>");
}
a.checkbox { a.checkbox {
display: inline-block; display: inline-block;
position: relative; position: relative;
@ -480,13 +534,34 @@ class MdToHtml {
max-width: 100%; max-width: 100%;
} }
.katex .mfrac .frac-line:before { @media print {
/* top: 50%; */ body {
/* padding-bottom: .7em; */ height: auto !important;
}
a.checkbox {
border: 1pt solid ` + style.htmlColor + `;
border-radius: 2pt;
width: 1em;
height: 1em;
line-height: 1em;
text-align: center;
top: .4em;
}
a.checkbox.tick:after {
content: "X";
}
a.checkbox.tick {
top: 0;
left: -0.02em;
color: ` + style.htmlColor + `;
}
} }
`; `;
const styleHtml = '<style>' + normalizeCss + "\n" + css + '</style>'; //+ '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.5.1/katex.min.css">'; const styleHtml = '<style>' + normalizeCss + "\n" + css + '</style>';
const output = styleHtml + renderedBody; const output = styleHtml + renderedBody;

View File

@ -44,4 +44,18 @@ ObjectUtils.fieldsEqual = function(o1, o2) {
return true; return true;
} }
ObjectUtils.convertValuesToFunctions = function(o) {
const output = {};
for (let n in o) {
if (!o.hasOwnProperty(n)) continue;
output[n] = () => { return typeof o[n] === 'function' ? o[n]() : o[n]; }
}
return output;
}
ObjectUtils.isEmpty = function(o) {
if (!o) return true;
return Object.keys(o).length === 0 && o.constructor === Object;
}
module.exports = ObjectUtils; module.exports = ObjectUtils;

View File

@ -38,9 +38,9 @@ class SyncTargetNextcloud extends BaseSyncTarget {
async initFileApi() { async initFileApi() {
const fileApi = await SyncTargetWebDAV.newFileApi_(SyncTargetNextcloud.id(), { const fileApi = await SyncTargetWebDAV.newFileApi_(SyncTargetNextcloud.id(), {
path: Setting.value('sync.5.path'), path: () => Setting.value('sync.5.path'),
username: Setting.value('sync.5.username'), username: () => Setting.value('sync.5.username'),
password: Setting.value('sync.5.password'), password: () => Setting.value('sync.5.password'),
}); });
fileApi.setLogger(this.logger()); fileApi.setLogger(this.logger());

View File

@ -30,9 +30,9 @@ class SyncTargetWebDAV extends BaseSyncTarget {
static async newFileApi_(syncTargetId, options) { static async newFileApi_(syncTargetId, options) {
const apiOptions = { const apiOptions = {
baseUrl: () => options.path, baseUrl: () => options.path(),
username: () => options.username, username: () => options.username(),
password: () => options.password, password: () => options.password(),
}; };
const api = new WebDavApi(apiOptions); const api = new WebDavApi(apiOptions);
@ -65,9 +65,9 @@ class SyncTargetWebDAV extends BaseSyncTarget {
async initFileApi() { async initFileApi() {
const fileApi = await SyncTargetWebDAV.newFileApi_(SyncTargetWebDAV.id(), { const fileApi = await SyncTargetWebDAV.newFileApi_(SyncTargetWebDAV.id(), {
path: Setting.value('sync.6.path'), path: () => Setting.value('sync.6.path'),
username: Setting.value('sync.6.username'), username: () => Setting.value('sync.6.username'),
password: Setting.value('sync.6.password'), password: () => Setting.value('sync.6.password'),
}); });
fileApi.setLogger(this.logger()); fileApi.setLogger(this.logger());

View File

@ -30,21 +30,21 @@ class ModalDialog extends React.Component {
borderColor:theme.dividerColor, borderColor:theme.dividerColor,
margin: 20, margin: 20,
padding: 10, padding: 10,
borderRadius: 5,
}, },
modalContentWrapper2: { modalContentWrapper2: {
paddingTop: 10,
flex:1, flex:1,
}, },
title: { title: {
borderBottomWidth: 1, borderBottomWidth: 1,
borderBottomColor: theme.dividerColor, borderBottomColor: theme.dividerColor,
paddingBottom: 10, paddingBottom: 10,
fontWeight: 'bold',
}, },
buttonRow: { buttonRow: {
flexDirection: 'row', flexDirection: 'row',
borderTopWidth: 1, borderTopWidth: 1,
borderTopColor: theme.dividerColor, borderTopColor: theme.dividerColor,
paddingTop: 10,
}, },
}; };
@ -54,21 +54,22 @@ class ModalDialog extends React.Component {
render() { render() {
const ContentComponent = this.props.ContentComponent; const ContentComponent = this.props.ContentComponent;
const buttonBarEnabled = this.props.buttonBarEnabled !== false;
return ( return (
<View style={this.styles().modalWrapper}> <View style={this.styles().modalWrapper}>
<Modal transparent={true} visible={true} onRequestClose={() => { }} > <Modal transparent={true} visible={true} onRequestClose={() => { }} >
<View style={this.styles().modalContentWrapper}> <View elevation={10} style={this.styles().modalContentWrapper}>
<Text style={this.styles().title}>Title</Text> <Text style={this.styles().title}>{this.props.title}</Text>
<View style={this.styles().modalContentWrapper2}> <View style={this.styles().modalContentWrapper2}>
{ContentComponent} {ContentComponent}
</View> </View>
<View style={this.styles().buttonRow}> <View style={this.styles().buttonRow}>
<View style={{flex:1}}> <View style={{flex:1}}>
<Button title={_('OK')} onPress={() => {}}></Button> <Button disabled={!buttonBarEnabled} title={_('OK')} onPress={this.props.onOkPress}></Button>
</View> </View>
<View style={{flex:1, marginLeft: 5}}> <View style={{flex:1, marginLeft: 5}}>
<Button title={_('Cancel')} onPress={() => {}}></Button> <Button disabled={!buttonBarEnabled} title={_('Cancel')} onPress={this.props.onCancelPress}></Button>
</View> </View>
</View> </View>
</View> </View>

View File

@ -4,7 +4,6 @@ const Icon = require('react-native-vector-icons/Ionicons').default;
const ReactNativeActionButton = require('react-native-action-button').default; const ReactNativeActionButton = require('react-native-action-button').default;
const { connect } = require('react-redux'); const { connect } = require('react-redux');
const { globalStyle } = require('lib/components/global-style.js'); const { globalStyle } = require('lib/components/global-style.js');
const { Log } = require('lib/log.js');
const { _ } = require('lib/locale.js'); const { _ } = require('lib/locale.js');
const styles = StyleSheet.create({ const styles = StyleSheet.create({

View File

@ -46,6 +46,13 @@ globalStyle.lineInput = {
backgroundColor: globalStyle.backgroundColor, backgroundColor: globalStyle.backgroundColor,
}; };
globalStyle.buttonRow = {
flexDirection: 'row',
borderTopWidth: 1,
borderTopColor: globalStyle.dividerColor,
paddingTop: 10,
};
let themeCache_ = {}; let themeCache_ = {};
function themeStyle(theme) { function themeStyle(theme) {

View File

@ -104,7 +104,7 @@ class NoteBodyViewer extends Component {
style={webViewStyle} style={webViewStyle}
source={source} source={source}
onLoadEnd={() => this.onLoadEnd()} onLoadEnd={() => this.onLoadEnd()}
onError={(e) => reg.logger().error('WebView error', e) } onError={() => reg.logger().error('WebView error') }
onMessage={(event) => { onMessage={(event) => {
let msg = event.nativeEvent.data; let msg = event.nativeEvent.data;

View File

@ -1,7 +1,6 @@
const React = require('react'); const Component = React.Component; const React = require('react'); const Component = React.Component;
const { connect } = require('react-redux'); const { connect } = require('react-redux');
const { ListView, Text, TouchableOpacity , View, StyleSheet } = require('react-native'); const { ListView, Text, TouchableOpacity , View, StyleSheet } = require('react-native');
const { Log } = require('lib/log.js');
const { _ } = require('lib/locale.js'); const { _ } = require('lib/locale.js');
const { Checkbox } = require('lib/components/checkbox.js'); const { Checkbox } = require('lib/components/checkbox.js');
const { reg } = require('lib/registry.js'); const { reg } = require('lib/registry.js');

View File

@ -1,7 +1,6 @@
const React = require('react'); const Component = React.Component; const React = require('react'); const Component = React.Component;
const { connect } = require('react-redux'); const { connect } = require('react-redux');
const { ListView, Text, TouchableHighlight, Switch, View, StyleSheet } = require('react-native'); const { ListView, Text, TouchableHighlight, Switch, View, StyleSheet } = require('react-native');
const { Log } = require('lib/log.js');
const { _ } = require('lib/locale.js'); const { _ } = require('lib/locale.js');
const { Checkbox } = require('lib/components/checkbox.js'); const { Checkbox } = require('lib/components/checkbox.js');
const { NoteItem } = require('lib/components/note-item.js'); const { NoteItem } = require('lib/components/note-item.js');

View File

@ -2,7 +2,6 @@ const React = require('react'); const Component = React.Component;
const { connect } = require('react-redux'); const { connect } = require('react-redux');
const { Platform, View, Text, Button, StyleSheet, TouchableOpacity, Image, ScrollView, Dimensions } = require('react-native'); const { Platform, View, Text, Button, StyleSheet, TouchableOpacity, Image, ScrollView, Dimensions } = require('react-native');
const Icon = require('react-native-vector-icons/Ionicons').default; const Icon = require('react-native-vector-icons/Ionicons').default;
const { Log } = require('lib/log.js');
const { BackButtonService } = require('lib/services/back-button.js'); const { BackButtonService } = require('lib/services/back-button.js');
const NavService = require('lib/services/NavService.js'); const NavService = require('lib/services/NavService.js');
const { ReportService } = require('lib/services/report.js'); const { ReportService } = require('lib/services/report.js');
@ -129,6 +128,8 @@ class ScreenHeaderComponent extends Component {
color: theme.raisedHighlightedColor, color: theme.raisedHighlightedColor,
fontWeight: 'bold', fontWeight: 'bold',
fontSize: theme.fontSize, fontSize: theme.fontSize,
paddingTop: 15,
paddingBottom: 15,
}, },
warningBox: { warningBox: {
backgroundColor: "#ff9900", backgroundColor: "#ff9900",
@ -429,15 +430,19 @@ class ScreenHeaderComponent extends Component {
</TouchableOpacity> </TouchableOpacity>
) : null; ) : null;
const showSideMenuButton = this.props.showSideMenuButton !== false && !this.props.noteSelectionEnabled;
const showSearchButton = this.props.showSearchButton !== false && !this.props.noteSelectionEnabled;
const showContextMenuButton = this.props.showContextMenuButton !== false;
const titleComp = createTitleComponent(); const titleComp = createTitleComponent();
const sideMenuComp = this.props.noteSelectionEnabled ? null : sideMenuButton(this.styles(), () => this.sideMenuButton_press()); const sideMenuComp = !showSideMenuButton ? null : sideMenuButton(this.styles(), () => this.sideMenuButton_press());
const backButtonComp = backButton(this.styles(), () => this.backButton_press(), !this.props.historyCanGoBack); const backButtonComp = backButton(this.styles(), () => this.backButton_press(), !this.props.historyCanGoBack);
const searchButtonComp = this.props.noteSelectionEnabled ? null : searchButton(this.styles(), () => this.searchButton_press()); const searchButtonComp = !showSearchButton ? null : searchButton(this.styles(), () => this.searchButton_press());
const deleteButtonComp = this.props.noteSelectionEnabled ? deleteButton(this.styles(), () => this.deleteButton_press()) : null; const deleteButtonComp = this.props.noteSelectionEnabled ? deleteButton(this.styles(), () => this.deleteButton_press()) : null;
const sortButtonComp = this.props.sortButton_press ? sortButton(this.styles(), () => this.props.sortButton_press()) : null; const sortButtonComp = this.props.sortButton_press ? sortButton(this.styles(), () => this.props.sortButton_press()) : null;
const windowHeight = Dimensions.get('window').height - 50; const windowHeight = Dimensions.get('window').height - 50;
const menuComp = ( const menuComp = !showContextMenuButton ? null : (
<Menu onSelect={(value) => this.menu_select(value)} style={this.styles().contextMenu}> <Menu onSelect={(value) => this.menu_select(value)} style={this.styles().contextMenu}>
<MenuTrigger style={{ paddingTop: PADDING_V, paddingBottom: PADDING_V }}> <MenuTrigger style={{ paddingTop: PADDING_V, paddingBottom: PADDING_V }}>
<Text style={this.styles().contextMenuTrigger}> &#8942;</Text> <Text style={this.styles().contextMenuTrigger}> &#8942;</Text>

View File

@ -0,0 +1,196 @@
const React = require('react'); const Component = React.Component;
const { ListView, StyleSheet, View, Text, Button, FlatList, TouchableOpacity, TextInput } = require('react-native');
const Setting = require('lib/models/Setting.js');
const { connect } = require('react-redux');
const { reg } = require('lib/registry.js');
const { ScreenHeader } = require('lib/components/screen-header.js');
const { time } = require('lib/time-utils');
const { Logger } = require('lib/logger.js');
const BaseItem = require('lib/models/BaseItem.js');
const Tag = require('lib/models/Tag.js');
const { Database } = require('lib/database.js');
const Folder = require('lib/models/Folder.js');
const { ReportService } = require('lib/services/report.js');
const { _ } = require('lib/locale.js');
const { globalStyle, themeStyle } = require('lib/components/global-style.js');
const Icon = require('react-native-vector-icons/Ionicons').default;
const ModalDialog = require('lib/components/ModalDialog');
const naturalCompare = require('string-natural-compare');
class NoteTagsDialogComponent extends React.Component {
constructor() {
super();
this.styles_ = {};
this.state = {
noteTagIds: [],
noteId: null,
tagListData: [],
newTags: '',
savingTags: false,
};
const noteHasTag = (tagId) => {
for (let i = 0; i < this.state.tagListData.length; i++) {
if (this.state.tagListData[i].id === tagId) return this.state.tagListData[i].selected;
}
return false;
}
const newTagTitles = () => {
return this.state.newTags
.split(',')
.map(t => t.trim().toLowerCase())
.filter(t => !!t);
}
this.tag_press = (tagId) => {
const newData = this.state.tagListData.slice();
for (let i = 0; i < newData.length; i++) {
const t = newData[i];
if (t.id === tagId) {
const newTag = Object.assign({}, t);
newTag.selected = !newTag.selected;
newData[i] = newTag;
break;
}
}
this.setState({ tagListData: newData });
}
this.renderTag = (data) => {
const tag = data.item;
const iconName = noteHasTag(tag.id) ? 'md-checkbox-outline' : 'md-square-outline';
return (
<TouchableOpacity key={tag.id} onPress={() => this.tag_press(tag.id)} style={this.styles().tag}>
<View style={this.styles().tagIconText}>
<Icon name={iconName} style={this.styles().tagCheckbox}/><Text>{tag.title}</Text>
</View>
</TouchableOpacity>
);
}
this.tagKeyExtractor = (tag, index) => tag.id;
this.okButton_press = async () => {
this.setState({ savingTags: true });
try {
const tagIds = this.state.tagListData.filter(t => t.selected).map(t => t.id);
await Tag.setNoteTagsByIds(this.state.noteId, tagIds);
const extraTitles = newTagTitles();
for (let i = 0; i < extraTitles.length; i++) {
await Tag.addNoteTagByTitle(this.state.noteId, extraTitles[i]);
}
} finally {
this.setState({ savingTags: false });
}
if (this.props.onCloseRequested) this.props.onCloseRequested();
}
this.cancelButton_press = () => {
if (this.props.onCloseRequested) this.props.onCloseRequested();
}
}
componentWillMount() {
const noteId = this.props.noteId;
this.setState({ noteId: noteId });
this.loadNoteTags(noteId);
}
async loadNoteTags(noteId) {
const tags = await Tag.tagsByNoteId(noteId);
const tagIds = tags.map(t => t.id);
const tagListData = this.props.tags.map(tag => { return {
id: tag.id,
title: tag.title,
selected: tagIds.indexOf(tag.id) >= 0,
}});
tagListData.sort((a, b) => {
return naturalCompare.caseInsensitive(a.title, b.title);
//return a.title.toLowerCase() < b.title.toLowerCase() ? -1 : +1;
});
this.setState({ tagListData: tagListData });
}
styles() {
const themeId = this.props.theme;
const theme = themeStyle(themeId);
if (this.styles_[themeId]) return this.styles_[themeId];
this.styles_ = {};
let styles = {
tag: {
padding: 10,
borderBottomWidth: 1,
borderBottomColor: theme.dividerColor,
},
tagIconText: {
flexDirection: 'row',
alignItems: 'center',
},
tagCheckbox: {
marginRight: 5,
fontSize: 20,
},
newTagBox: {
flexDirection:'row',
alignItems: 'center',
paddingLeft: theme.marginLeft,
paddingRight: theme.marginRight,
borderBottomWidth: 1,
borderBottomColor: theme.dividerColor
},
};
this.styles_[themeId] = StyleSheet.create(styles);
return this.styles_[themeId];
}
render() {
const theme = themeStyle(this.props.theme);
const dialogContent = (
<View style={{flex:1}}>
<View style={this.styles().newTagBox}>
<Text>{_('New tags:')}</Text><TextInput value={this.state.newTags} onChangeText={value => { this.setState({ newTags: value }) }} style={{flex:1}}/>
</View>
<FlatList
data={this.state.tagListData}
renderItem={this.renderTag}
keyExtractor={this.tagKeyExtractor}
/>
</View>
);
return <ModalDialog
theme={this.props.theme}
ContentComponent={dialogContent}
title={_('Type new tags or select from list')}
onOkPress={this.okButton_press}
onCancelPress={this.cancelButton_press}
buttonBarEnabled={!this.state.savingTags}
/>
}
}
const NoteTagsDialog = connect(
(state) => {
return {
theme: state.settings.theme,
tags: state.tags,
noteId: state.selectedNoteIds.length ? state.selectedNoteIds[0] : null,
};
}
)(NoteTagsDialogComponent)
module.exports = NoteTagsDialog;

View File

@ -200,11 +200,19 @@ class ConfigScreenComponent extends BaseScreenComponent {
</View> </View>
</View>); </View>);
} }
settingComps.push(
<View key="donate_link" style={this.styles().settingContainer}>
<TouchableOpacity onPress={() => { Linking.openURL('http://joplin.cozic.net/donate/') }}>
<Text key="label" style={this.styles().linkText}>{_('Make a donation')}</Text>
</TouchableOpacity>
</View>
);
settingComps.push( settingComps.push(
<View key="website_link" style={this.styles().settingContainer}> <View key="website_link" style={this.styles().settingContainer}>
<TouchableOpacity onPress={() => { Linking.openURL('http://joplin.cozic.net/') }}> <TouchableOpacity onPress={() => { Linking.openURL('http://joplin.cozic.net/') }}>
<Text key="label" style={this.styles().linkText}>Joplin Website</Text> <Text key="label" style={this.styles().linkText}>{_('Joplin website')}</Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
); );

View File

@ -1,7 +1,6 @@
const React = require('react'); const Component = React.Component; const React = require('react'); const Component = React.Component;
const { View, Button, TextInput, StyleSheet } = require('react-native'); const { View, Button, TextInput, StyleSheet } = require('react-native');
const { connect } = require('react-redux'); const { connect } = require('react-redux');
const { Log } = require('lib/log.js');
const { ActionButton } = require('lib/components/action-button.js'); const { ActionButton } = require('lib/components/action-button.js');
const Folder = require('lib/models/Folder.js'); const Folder = require('lib/models/Folder.js');
const BaseModel = require('lib/BaseModel.js'); const BaseModel = require('lib/BaseModel.js');

View File

@ -1,7 +1,6 @@
const React = require('react'); const Component = React.Component; const React = require('react'); const Component = React.Component;
const { ListView, View, Text, Button, StyleSheet, Platform } = require('react-native'); const { ListView, View, Text, Button, StyleSheet, Platform } = require('react-native');
const { connect } = require('react-redux'); const { connect } = require('react-redux');
const { Log } = require('lib/log.js');
const { reg } = require('lib/registry.js'); const { reg } = require('lib/registry.js');
const { ScreenHeader } = require('lib/components/screen-header.js'); const { ScreenHeader } = require('lib/components/screen-header.js');
const { time } = require('lib/time-utils'); const { time } = require('lib/time-utils');

View File

@ -2,7 +2,6 @@ const React = require('react'); const Component = React.Component;
const { Platform, Keyboard, BackHandler, View, Button, TextInput, WebView, Text, StyleSheet, Linking, Image } = require('react-native'); const { Platform, Keyboard, BackHandler, View, Button, TextInput, WebView, Text, StyleSheet, Linking, Image } = require('react-native');
const { connect } = require('react-redux'); const { connect } = require('react-redux');
const { uuid } = require('lib/uuid.js'); const { uuid } = require('lib/uuid.js');
const { Log } = require('lib/log.js');
const RNFS = require('react-native-fs'); const RNFS = require('react-native-fs');
const Note = require('lib/models/Note.js'); const Note = require('lib/models/Note.js');
const Setting = require('lib/models/Setting.js'); const Setting = require('lib/models/Setting.js');
@ -16,6 +15,7 @@ const Icon = require('react-native-vector-icons/Ionicons').default;
const { fileExtension, basename, safeFileExtension } = require('lib/path-utils.js'); const { fileExtension, basename, safeFileExtension } = require('lib/path-utils.js');
const mimeUtils = require('lib/mime-utils.js').mime; const mimeUtils = require('lib/mime-utils.js').mime;
const { ScreenHeader } = require('lib/components/screen-header.js'); const { ScreenHeader } = require('lib/components/screen-header.js');
const NoteTagsDialog = require('lib/components/screens/NoteTagsDialog');
const { time } = require('lib/time-utils.js'); const { time } = require('lib/time-utils.js');
const { Checkbox } = require('lib/components/checkbox.js'); const { Checkbox } = require('lib/components/checkbox.js');
const { _ } = require('lib/locale.js'); const { _ } = require('lib/locale.js');
@ -52,7 +52,8 @@ class NoteScreenComponent extends BaseScreenComponent {
isLoading: true, isLoading: true,
titleTextInputHeight: 20, titleTextInputHeight: 20,
alarmDialogShown: false, alarmDialogShown: false,
heightBumpView:0 heightBumpView:0,
noteTagDialogShown: false,
}; };
// iOS doesn't support multiline text fields properly so disable it // iOS doesn't support multiline text fields properly so disable it
@ -102,6 +103,10 @@ class NoteScreenComponent extends BaseScreenComponent {
return false; return false;
}; };
this.noteTagDialog_closeRequested = () => {
this.setState({ noteTagDialogShown: false });
}
} }
styles() { styles() {
@ -358,6 +363,12 @@ class NoteScreenComponent extends BaseScreenComponent {
shared.toggleIsTodo_onPress(this); shared.toggleIsTodo_onPress(this);
} }
tags_onPress() {
if (!this.state.note || !this.state.note.id) return;
this.setState({ noteTagDialogShown: true });
}
setAlarm_onPress() { setAlarm_onPress() {
this.setState({ alarmDialogShown: true }); this.setState({ alarmDialogShown: true });
} }
@ -394,6 +405,7 @@ class NoteScreenComponent extends BaseScreenComponent {
menuOptions() { menuOptions() {
const note = this.state.note; const note = this.state.note;
const isTodo = note && !!note.is_todo; const isTodo = note && !!note.is_todo;
const isSaved = note && note.id;
let output = []; let output = [];
@ -411,6 +423,7 @@ class NoteScreenComponent extends BaseScreenComponent {
output.push({ title: _('Set alarm'), onPress: () => { this.setState({ alarmDialogShown: true }) }});; output.push({ title: _('Set alarm'), onPress: () => { this.setState({ alarmDialogShown: true }) }});;
} }
if (isSaved) output.push({ title: _('Tags'), onPress: () => { this.tags_onPress(); } });
output.push({ title: isTodo ? _('Convert to note') : _('Convert to todo'), onPress: () => { this.toggleIsTodo_onPress(); } }); output.push({ title: isTodo ? _('Convert to note') : _('Convert to todo'), onPress: () => { this.toggleIsTodo_onPress(); } });
output.push({ isDivider: true }); output.push({ isDivider: true });
if (this.props.showAdvancedOptions) output.push({ title: this.state.showNoteMetadata ? _('Hide metadata') : _('Show metadata'), onPress: () => { this.showMetadata_onPress(); } }); if (this.props.showAdvancedOptions) output.push({ title: this.state.showNoteMetadata ? _('Hide metadata') : _('Show metadata'), onPress: () => { this.showMetadata_onPress(); } });
@ -536,6 +549,8 @@ class NoteScreenComponent extends BaseScreenComponent {
</View> </View>
); );
const noteTagDialog = !this.state.noteTagDialogShown ? null : <NoteTagsDialog onCloseRequested={this.noteTagDialog_closeRequested}/>;
return ( return (
<View style={this.rootStyle(this.props.theme).root}> <View style={this.rootStyle(this.props.theme).root}>
<ScreenHeader <ScreenHeader
@ -573,6 +588,7 @@ class NoteScreenComponent extends BaseScreenComponent {
/> />
<DialogBox ref={dialogbox => { this.dialogbox = dialogbox }}/> <DialogBox ref={dialogbox => { this.dialogbox = dialogbox }}/>
{ noteTagDialog }
</View> </View>
); );
} }

View File

@ -3,7 +3,6 @@ const { View, Button, Text } = require('react-native');
const { stateUtils } = require('lib/reducer.js'); const { stateUtils } = require('lib/reducer.js');
const { connect } = require('react-redux'); const { connect } = require('react-redux');
const { reg } = require('lib/registry.js'); const { reg } = require('lib/registry.js');
const { Log } = require('lib/log.js');
const { NoteList } = require('lib/components/note-list.js'); const { NoteList } = require('lib/components/note-list.js');
const Folder = require('lib/models/Folder.js'); const Folder = require('lib/models/Folder.js');
const Tag = require('lib/models/Tag.js'); const Tag = require('lib/models/Tag.js');

View File

@ -2,7 +2,6 @@ const React = require('react'); const Component = React.Component;
const { View } = require('react-native'); const { View } = require('react-native');
const { WebView, Button, Text } = require('react-native'); const { WebView, Button, Text } = require('react-native');
const { connect } = require('react-redux'); const { connect } = require('react-redux');
const { Log } = require('lib/log.js');
const Setting = require('lib/models/Setting.js'); const Setting = require('lib/models/Setting.js');
const { ScreenHeader } = require('lib/components/screen-header.js'); const { ScreenHeader } = require('lib/components/screen-header.js');
const { reg } = require('lib/registry.js'); const { reg } = require('lib/registry.js');
@ -44,8 +43,6 @@ class OneDriveLoginScreenComponent extends BaseScreenComponent {
const parsedUrl = parseUri(url); const parsedUrl = parseUri(url);
if (!this.authCode_ && parsedUrl && parsedUrl.queryKey && parsedUrl.queryKey.code) { if (!this.authCode_ && parsedUrl && parsedUrl.queryKey && parsedUrl.queryKey.code) {
Log.info('URL: ', url, parsedUrl.queryKey);
this.authCode_ = parsedUrl.queryKey.code this.authCode_ = parsedUrl.queryKey.code
try { try {
@ -60,8 +57,8 @@ class OneDriveLoginScreenComponent extends BaseScreenComponent {
} }
} }
async webview_error(error) { async webview_error() {
Log.error(error); alert('Could not load page. Please check your connection and try again.');
} }
retryButton_click() { retryButton_click() {
@ -93,7 +90,7 @@ class OneDriveLoginScreenComponent extends BaseScreenComponent {
<WebView <WebView
source={source} source={source}
onNavigationStateChange={(o) => { this.webview_load(o); }} onNavigationStateChange={(o) => { this.webview_load(o); }}
onError={(error) => { this.webview_error(error); }} onError={() => { this.webview_error(); }}
/> />
<Button title={_("Refresh")} onPress={() => { this.retryButton_click(); }}></Button> <Button title={_("Refresh")} onPress={() => { this.retryButton_click(); }}></Button>
</View> </View>

View File

@ -2,7 +2,6 @@ const React = require('react'); const Component = React.Component;
const { ListView, StyleSheet, View, Text, Button, FlatList } = require('react-native'); const { ListView, StyleSheet, View, Text, Button, FlatList } = require('react-native');
const Setting = require('lib/models/Setting.js'); const Setting = require('lib/models/Setting.js');
const { connect } = require('react-redux'); const { connect } = require('react-redux');
const { Log } = require('lib/log.js');
const { reg } = require('lib/registry.js'); const { reg } = require('lib/registry.js');
const { ScreenHeader } = require('lib/components/screen-header.js'); const { ScreenHeader } = require('lib/components/screen-header.js');
const { time } = require('lib/time-utils'); const { time } = require('lib/time-utils');

View File

@ -1,7 +1,6 @@
const React = require('react'); const Component = React.Component; const React = require('react'); const Component = React.Component;
const { View, Text, StyleSheet } = require('react-native'); const { View, Text, StyleSheet } = require('react-native');
const { connect } = require('react-redux'); const { connect } = require('react-redux');
const { Log } = require('lib/log.js');
const { ScreenHeader } = require('lib/components/screen-header.js'); const { ScreenHeader } = require('lib/components/screen-header.js');
const { ActionButton } = require('lib/components/action-button.js'); const { ActionButton } = require('lib/components/action-button.js');
const { BaseScreenComponent } = require('lib/components/base-screen.js'); const { BaseScreenComponent } = require('lib/components/base-screen.js');

View File

@ -1,5 +1,6 @@
const Setting = require('lib/models/Setting.js'); const Setting = require('lib/models/Setting.js');
const SyncTargetRegistry = require('lib/SyncTargetRegistry'); const SyncTargetRegistry = require('lib/SyncTargetRegistry');
const ObjectUtils = require('lib/ObjectUtils');
const { _ } = require('lib/locale.js'); const { _ } = require('lib/locale.js');
const shared = {} const shared = {}
@ -16,7 +17,7 @@ shared.checkSyncConfig = async function(comp, settings) {
const SyncTargetClass = SyncTargetRegistry.classById(syncTargetId); const SyncTargetClass = SyncTargetRegistry.classById(syncTargetId);
const options = Setting.subValues('sync.' + syncTargetId, settings); const options = Setting.subValues('sync.' + syncTargetId, settings);
comp.setState({ checkSyncConfigResult: 'checking' }); comp.setState({ checkSyncConfigResult: 'checking' });
const result = await SyncTargetClass.checkConfig(options); const result = await SyncTargetClass.checkConfig(ObjectUtils.convertValuesToFunctions(options));
comp.setState({ checkSyncConfigResult: result }); comp.setState({ checkSyncConfigResult: result });
} }

View File

@ -63,7 +63,9 @@ shared.checkPasswords = async function(comp) {
shared.decryptedStatText = function(comp) { shared.decryptedStatText = function(comp) {
const stats = comp.state.stats; const stats = comp.state.stats;
return _('Decrypted items: %s / %s', stats.encrypted !== null ? (stats.total - stats.encrypted) : '-', stats.total !== null ? stats.total : '-'); const doneCount = stats.encrypted !== null ? (stats.total - stats.encrypted) : '-';
const totalCount = stats.total !== null ? stats.total : '-';
return _('Decrypted items: %s / %s', doneCount, totalCount);
} }
shared.onSavePasswordClick = function(comp, mk) { shared.onSavePasswordClick = function(comp, mk) {

View File

@ -2,7 +2,6 @@ const React = require('react'); const Component = React.Component;
const { TouchableOpacity , Button, Text, Image, StyleSheet, ScrollView, View } = require('react-native'); const { TouchableOpacity , Button, Text, Image, StyleSheet, ScrollView, View } = require('react-native');
const { connect } = require('react-redux'); const { connect } = require('react-redux');
const Icon = require('react-native-vector-icons/Ionicons').default; const Icon = require('react-native-vector-icons/Ionicons').default;
const { Log } = require('lib/log.js');
const Tag = require('lib/models/Tag.js'); const Tag = require('lib/models/Tag.js');
const Note = require('lib/models/Note.js'); const Note = require('lib/models/Note.js');
const Folder = require('lib/models/Folder.js'); const Folder = require('lib/models/Folder.js');

View File

@ -1,6 +1,5 @@
const React = require('react'); const Component = React.Component; const React = require('react'); const Component = React.Component;
const { connect } = require('react-redux'); const { connect } = require('react-redux');
const { Log } = require('lib/log.js');
const SideMenu_ = require('react-native-side-menu').default; const SideMenu_ = require('react-native-side-menu').default;
class SideMenuComponent extends SideMenu_ {}; class SideMenuComponent extends SideMenu_ {};

View File

@ -3,16 +3,16 @@ const { promiseChain } = require('lib/promise-utils.js');
const { Logger } = require('lib/logger.js'); const { Logger } = require('lib/logger.js');
const { time } = require('lib/time-utils.js'); const { time } = require('lib/time-utils.js');
const { sprintf } = require('sprintf-js'); const { sprintf } = require('sprintf-js');
const Mutex = require('async-mutex').Mutex;
class Database { class Database {
constructor(driver) { constructor(driver) {
this.debugMode_ = false; this.debugMode_ = false;
this.driver_ = driver; this.driver_ = driver;
this.inTransaction_ = false;
this.logger_ = new Logger(); this.logger_ = new Logger();
this.logExcludedQueryTypes_ = []; this.logExcludedQueryTypes_ = [];
this.batchTransactionMutex_ = new Mutex();
} }
setLogExcludedQueryTypes(v) { setLogExcludedQueryTypes(v) {
@ -113,92 +113,24 @@ class Database {
return; return;
} }
// There can be only one transaction running at a time so queue // There can be only one transaction running at a time so use a mutex
// any new transaction here. const release = await this.batchTransactionMutex_.acquire();
if (this.inTransaction_) {
while (true) { try {
await time.msleep(100); await this.exec('BEGIN TRANSACTION');
if (!this.inTransaction_) {
this.inTransaction_ = true; for (let i = 0; i < queries.length; i++) {
break; let query = this.wrapQuery(queries[i]);
} await this.exec(query.sql, query.params);
} }
// return new Promise((resolve, reject) => { await this.exec('COMMIT');
// let iid = setInterval(() => { } catch (error) {
// if (!this.inTransaction_) { await this.exec('ROLLBACK');
// clearInterval(iid); throw error;
// this.transactionExecBatch(queries).then(() => { } finally {
// resolve(); release();
// }).catch((error) => {
// reject(error);
// });
// }
// }, 100);
// });
} }
this.inTransaction_ = true;
queries.splice(0, 0, 'BEGIN TRANSACTION');
queries.push('COMMIT'); // Note: ROLLBACK is currently not supported
for (let i = 0; i < queries.length; i++) {
let query = this.wrapQuery(queries[i]);
await this.exec(query.sql, query.params);
}
this.inTransaction_ = false;
// return promiseChain(chain).then(() => {
// this.inTransaction_ = false;
// });
// if (queries.length <= 0) return Promise.resolve();
// if (queries.length == 1) {
// let q = this.wrapQuery(queries[0]);
// return this.exec(q.sql, q.params);
// }
// // There can be only one transaction running at a time so queue
// // any new transaction here.
// if (this.inTransaction_) {
// return new Promise((resolve, reject) => {
// let iid = setInterval(() => {
// if (!this.inTransaction_) {
// clearInterval(iid);
// this.transactionExecBatch(queries).then(() => {
// resolve();
// }).catch((error) => {
// reject(error);
// });
// }
// }, 100);
// });
// }
// this.inTransaction_ = true;
// queries.splice(0, 0, 'BEGIN TRANSACTION');
// queries.push('COMMIT'); // Note: ROLLBACK is currently not supported
// let chain = [];
// for (let i = 0; i < queries.length; i++) {
// let query = this.wrapQuery(queries[i]);
// chain.push(() => {
// return this.exec(query.sql, query.params);
// });
// }
// return promiseChain(chain).then(() => {
// this.inTransaction_ = false;
// });
} }
static enumId(type, s) { static enumId(type, s) {

View File

@ -243,7 +243,9 @@ async function basicDelta(path, getDirStatFn, options) {
newContext.statsCache.sort(function(a, b) { newContext.statsCache.sort(function(a, b) {
return a.updated_time - b.updated_time; return a.updated_time - b.updated_time;
}); });
newContext.statIdsCache = newContext.statsCache.map((item) => BaseItem.pathToId(item.path)); newContext.statIdsCache = newContext.statsCache
.filter(item => BaseItem.isSystemPath(item.path))
.map(item => BaseItem.pathToId(item.path));
newContext.statIdsCache.sort(); // Items must be sorted to use binary search below newContext.statIdsCache.sort(); // Items must be sorted to use binary search below
} }

View File

@ -35,7 +35,11 @@ class FsDriverNode extends FsDriverBase {
async writeFile(path, string, encoding = 'base64') { async writeFile(path, string, encoding = 'base64') {
try { try {
return await fs.writeFile(path, string, { encoding: encoding }); if (encoding === 'buffer') {
return await fs.writeFile(path, string);
} else {
return await fs.writeFile(path, string, { encoding: encoding });
}
} catch (error) { } catch (error) {
throw this.fsErrorToJsError_(error, path); throw this.fsErrorToJsError_(error, path);
} }

View File

@ -17,7 +17,7 @@ class FsDriverRN extends FsDriverBase {
// same as rm -rf // same as rm -rf
async remove(path) { async remove(path) {
throw new Error('Not implemented'); return await this.unlink(path);
} }
writeBinaryFile(path, content) { writeBinaryFile(path, content) {

View File

@ -476,12 +476,16 @@ function enexXmlToMdArray(stream, resources) {
// </note> // </note>
// </en-export> // </en-export>
// Note that there's also the case of resources with no ID where the ID is actually the MD5 of the content.
// This is handled in import-enex.js
let found = false; let found = false;
for (let i = 0; i < remainingResources.length; i++) { for (let i = 0; i < remainingResources.length; i++) {
let r = remainingResources[i]; let r = remainingResources[i];
if (!r.id) { if (!r.id) {
r.id = hash; resource = Object.assign({}, r);
remainingResources[i] = r; resource.id = hash;
remainingResources.splice(i, 1);
found = true; found = true;
break; break;
} }
@ -490,13 +494,13 @@ function enexXmlToMdArray(stream, resources) {
if (!found) { if (!found) {
console.warn('Hash with no associated resource: ' + hash); console.warn('Hash with no associated resource: ' + hash);
} }
} else { }
// If the resource does not appear among the note's resources, it
// means it's an attachement. It will be appended along with the // If the resource does not appear among the note's resources, it
// other remaining resources at the bottom of the markdown text. // means it's an attachement. It will be appended along with the
if (!!resource.id) { // other remaining resources at the bottom of the markdown text.
section.lines = addResourceTag(section.lines, resource, nodeAttributes.alt); if (resource && !!resource.id) {
} section.lines = addResourceTag(section.lines, resource, nodeAttributes.alt);
} }
} else if (["span", "font", 'sup', 'cite', 'abbr', 'small', 'tt', 'sub', 'colgroup', 'col', 'ins', 'caption', 'var', 'map', 'area'].indexOf(n) >= 0) { } else if (["span", "font", 'sup', 'cite', 'abbr', 'small', 'tt', 'sub', 'colgroup', 'col', 'ins', 'caption', 'var', 'map', 'area'].indexOf(n) >= 0) {
// Inline tags that can be ignored in Markdown // Inline tags that can be ignored in Markdown
@ -545,10 +549,6 @@ function enexXmlToMdArray(stream, resources) {
if (section.lines.length < 1) throw new Error('Invalid anchor tag closing'); // Sanity check, but normally not possible if (section.lines.length < 1) throw new Error('Invalid anchor tag closing'); // Sanity check, but normally not possible
const pushEmptyAnchor = (url) => {
section.lines.push('[link](' + url + ')');
}
// When closing the anchor tag, check if there's is any text content. If not // When closing the anchor tag, check if there's is any text content. If not
// put the URL as is (don't wrap it in [](url)). The markdown parser, using // 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 // GitHub flavour, will turn this URL into a link. This is to generate slightly
@ -556,11 +556,11 @@ function enexXmlToMdArray(stream, resources) {
let previous = section.lines[section.lines.length - 1]; let previous = section.lines[section.lines.length - 1];
if (previous == '[') { if (previous == '[') {
section.lines.pop(); section.lines.pop();
pushEmptyAnchor(url); section.lines.push(url);
} else if (!previous || previous == url) { } else if (!previous || previous == url) {
section.lines.pop(); section.lines.pop();
section.lines.pop(); section.lines.pop();
pushEmptyAnchor(url); section.lines.push(url);
} else { } else {
// Need to remove any new line character between the current ']' and the previous '[' // Need to remove any new line character between the current ']' and the previous '['
// otherwise it won't render properly. // otherwise it won't render properly.
@ -583,8 +583,7 @@ function enexXmlToMdArray(stream, resources) {
const c = section.lines.pop(); const c = section.lines.pop();
if (c === '[') break; if (c === '[') break;
} }
//section.lines.push(url); section.lines.push(url);
pushEmptyAnchor(url);
} else { } else {
section.lines.push('](' + url + ')'); section.lines.push('](' + url + ')');
} }
@ -644,7 +643,6 @@ function drawTable(table) {
// https://gist.github.com/IanWang/28965e13cdafdef4e11dc91f578d160d#tables // https://gist.github.com/IanWang/28965e13cdafdef4e11dc91f578d160d#tables
const flatRender = tableHasSubTables(table); // Render the table has regular text const flatRender = tableHasSubTables(table); // Render the table has regular text
const minColWidth = 3;
let lines = []; let lines = [];
lines.push(BLOCK_OPEN); lines.push(BLOCK_OPEN);
let headerDone = false; let headerDone = false;
@ -687,9 +685,16 @@ function drawTable(table) {
// A cell in a Markdown table cannot have actual new lines so replace // A cell in a Markdown table cannot have actual new lines so replace
// them with <br>, which are supported by the markdown renderers. // them with <br>, which are supported by the markdown renderers.
const cellText = processMdArrayNewLines(td.lines).replace(/\n+/g, "<br>"); let cellText = processMdArrayNewLines(td.lines).replace(/\n+/g, "<br>");
const width = Math.max(cellText.length, 3); // Inside tables cells, "|" needs to be escaped
cellText = cellText.replace(/\|/g, "\\|");
// Previously the width of the cell was as big as the content since it looks nicer, however that often doesn't work
// since the content can be very long, resulting in unreadable markdown. So no solution is perfect but making it a
// width of 3 is a bit better. Note that 3 is the minimum width of a cell - below this, it won't be rendered by
// markdown parsers.
const width = 3;
line.push(stringPadding(cellText, width, ' ', stringPadding.RIGHT)); line.push(stringPadding(cellText, width, ' ', stringPadding.RIGHT));
if (!headerDone) { if (!headerDone) {

View File

@ -11,6 +11,7 @@ const { enexXmlToMd } = require('./import-enex-md-gen.js');
const { time } = require('lib/time-utils.js'); const { time } = require('lib/time-utils.js');
const Levenshtein = require('levenshtein'); const Levenshtein = require('levenshtein');
const jsSHA = require("jssha"); const jsSHA = require("jssha");
const md5 = require('md5');
//const Promise = require('promise'); //const Promise = require('promise');
const fs = require('fs-extra'); const fs = require('fs-extra');
@ -30,8 +31,8 @@ function extractRecognitionObjId(recognitionXml) {
return r && r.length >= 2 ? r[1] : null; return r && r.length >= 2 ? r[1] : null;
} }
function filePutContents(filePath, content) { async function filePutContents(filePath, content) {
return fs.writeFile(filePath, content); await fs.writeFile(filePath, content);
} }
function removeUndefinedProperties(note) { function removeUndefinedProperties(note) {
@ -255,49 +256,6 @@ function importEnex(parentFolderId, filePath, importOptions = null) {
stream.resume(); stream.resume();
processingNotes = false; processingNotes = false;
return true; return true;
// let chain = [];
// while (notes.length) {
// let note = notes.shift();
// const contentStream = stringToStream(note.bodyXml);
// chain.push(() => {
// return enexXmlToMd(contentStream, note.resources).then((body) => {
// delete note.bodyXml;
// // console.info('-----------------------------------------------------------');
// // console.info(body);
// // console.info('-----------------------------------------------------------');
// note.id = uuid.create();
// note.parent_id = parentFolderId;
// note.body = body;
// // Notes in enex files always have a created timestamp but not always an
// // updated timestamp (it the note has never been modified). For sync
// // we require an updated_time property, so set it to create_time in that case
// if (!note.updated_time) note.updated_time = note.created_time;
// return saveNoteToStorage(note, importOptions.fuzzyMatching);
// }).then((result) => {
// if (result.noteUpdated) {
// progressState.updated++;
// } else if (result.noteCreated) {
// progressState.created++;
// } else if (result.noteSkipped) {
// progressState.skipped++;
// }
// progressState.resourcesCreated += result.resourcesCreated;
// progressState.notesTagged += result.notesTagged;
// importOptions.onProgress(progressState);
// });
// });
// }
// return promiseChain(chain).then(() => {
// stream.resume();
// processingNotes = false;
// return true;
// });
} }
saxStream.on('error', (error) => { saxStream.on('error', (error) => {
@ -418,6 +376,7 @@ function importEnex(parentFolderId, filePath, importOptions = null) {
noteAttributes = null; noteAttributes = null;
} else if (n == 'resource') { } else if (n == 'resource') {
let decodedData = null; let decodedData = null;
let resourceId = noteResource.id;
if (noteResource.dataEncoding == 'base64') { if (noteResource.dataEncoding == 'base64') {
try { try {
decodedData = Buffer.from(noteResource.data, 'base64'); decodedData = Buffer.from(noteResource.data, 'base64');
@ -429,8 +388,14 @@ function importEnex(parentFolderId, filePath, importOptions = null) {
decodedData = noteResource.data; // Just put the encoded data directly in the file so it can, potentially, be manually decoded later decodedData = noteResource.data; // Just put the encoded data directly in the file so it can, potentially, be manually decoded later
} }
if (!resourceId && decodedData) {
// If no resource ID is present, the resource ID is actually the MD5 of the data.
// This ID will match the "hash" attribute of the corresponding <en-media> tag.
resourceId = md5(decodedData);
}
let r = { let r = {
id: noteResource.id, id: resourceId,
data: decodedData, data: decodedData,
mime: noteResource.mime, mime: noteResource.mime,
title: noteResource.filename ? noteResource.filename : '', title: noteResource.filename ? noteResource.filename : '',

View File

@ -202,7 +202,7 @@ class JoplinDatabase extends Database {
// default value and thus might cause problems. In that case, the default value // default value and thus might cause problems. In that case, the default value
// must be set in the synchronizer too. // must be set in the synchronizer too.
const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
let currentVersionIndex = existingDatabaseVersions.indexOf(fromVersion); let currentVersionIndex = existingDatabaseVersions.indexOf(fromVersion);
@ -298,6 +298,52 @@ class JoplinDatabase extends Database {
queries.push('ALTER TABLE resources ADD COLUMN encryption_blob_encrypted INT NOT NULL DEFAULT 0'); queries.push('ALTER TABLE resources ADD COLUMN encryption_blob_encrypted INT NOT NULL DEFAULT 0');
} }
const upgradeVersion10 = () => {
const itemChangesTable = `
CREATE TABLE item_changes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
item_type INT NOT NULL,
item_id TEXT NOT NULL,
type INT NOT NULL,
created_time INT NOT NULL
);
`;
const noteResourcesTable = `
CREATE TABLE note_resources (
id INTEGER PRIMARY KEY,
note_id TEXT NOT NULL,
resource_id TEXT NOT NULL,
is_associated INT NOT NULL,
last_seen_time INT NOT NULL
);
`;
queries.push(this.sqlStringToLines(itemChangesTable)[0]);
queries.push('CREATE INDEX item_changes_item_id ON item_changes (item_id)');
queries.push('CREATE INDEX item_changes_created_time ON item_changes (created_time)');
queries.push('CREATE INDEX item_changes_item_type ON item_changes (item_type)');
queries.push(this.sqlStringToLines(noteResourcesTable)[0]);
queries.push('CREATE INDEX note_resources_note_id ON note_resources (note_id)');
queries.push('CREATE INDEX note_resources_resource_id ON note_resources (resource_id)');
queries.push({ sql: 'INSERT INTO item_changes (item_type, item_id, type, created_time) SELECT 1, id, 1, ? FROM notes', params: [Date.now()] });
}
if (targetVersion == 10) {
upgradeVersion10();
}
if (targetVersion == 11) {
// This trick was needed because Electron Builder incorrectly released a dev branch containing v10 as it was
// still being developed, and the db schema was not final at that time. So this v11 was created to
// make sure any invalid db schema that was accidentally created was deleted and recreated.
queries.push('DROP TABLE item_changes');
queries.push('DROP TABLE note_resources');
upgradeVersion10();
}
queries.push({ sql: 'UPDATE version SET version = ?', params: [targetVersion] }); queries.push({ sql: 'UPDATE version SET version = ?', params: [targetVersion] });
await this.transactionExecBatch(queries); await this.transactionExecBatch(queries);

View File

@ -1,40 +0,0 @@
// Custom wrapper for `console` to allow for custom logging (to file, etc.) if needed.
class Log {
static setLevel(v) {
this.level_ = v;
}
static level() {
return this.level_ === undefined ? Log.LEVEL_DEBUG : this.level_;
}
static debug(...o) {
if (Log.level() > Log.LEVEL_DEBUG) return;
console.info(...o);
}
static info(...o) {
if (Log.level() > Log.LEVEL_INFO) return;
console.info(...o);
}
static warn(...o) {
if (Log.level() > Log.LEVEL_WARN) return;
console.info(...o);
}
static error(...o) {
if (Log.level() > Log.LEVEL_ERROR) return;
console.info(...o);
}
}
Log.LEVEL_DEBUG = 0;
Log.LEVEL_INFO = 10;
Log.LEVEL_WARN = 20;
Log.LEVEL_ERROR = 30;
module.exports = { Log };

View File

@ -1,5 +1,4 @@
const BaseModel = require('lib/BaseModel.js'); const BaseModel = require('lib/BaseModel.js');
const { Log } = require('lib/log.js');
const { promiseChain } = require('lib/promise-utils.js'); const { promiseChain } = require('lib/promise-utils.js');
const { time } = require('lib/time-utils.js'); const { time } = require('lib/time-utils.js');
const Note = require('lib/models/Note.js'); const Note = require('lib/models/Note.js');

View File

@ -0,0 +1,54 @@
const BaseModel = require('lib/BaseModel.js');
const Mutex = require('async-mutex').Mutex;
class ItemChange extends BaseModel {
static tableName() {
return 'item_changes';
}
static modelType() {
return BaseModel.TYPE_ITEM_CHANGE;
}
static async add(itemType, itemId, type) {
ItemChange.saveCalls_.push(true);
// Using a mutex so that records can be added to the database in the
// background, without making the UI wait.
const release = await ItemChange.addChangeMutex_.acquire();
try {
await this.db().transactionExecBatch([
{ sql: 'DELETE FROM item_changes WHERE item_id = ?', params: [itemId] },
{ sql: 'INSERT INTO item_changes (item_type, item_id, type, created_time) VALUES (?, ?, ?, ?)', params: [itemType, itemId, type, Date.now()] },
]);
} finally {
release();
ItemChange.saveCalls_.pop();
}
}
// Because item changes are recorded in the background, this function
// can be used for synchronous code, in particular when unit testing.
static async waitForAllSaved() {
return new Promise((resolve, reject) => {
const iid = setInterval(() => {
if (!ItemChange.saveCalls_.length) {
clearInterval(iid);
resolve();
}
}, 100);
});
}
}
ItemChange.addChangeMutex_ = new Mutex();
ItemChange.saveCalls_ = [];
ItemChange.TYPE_CREATE = 1;
ItemChange.TYPE_UPDATE = 2;
ItemChange.TYPE_DELETE = 3;
module.exports = ItemChange;

View File

@ -1,7 +1,7 @@
const BaseModel = require('lib/BaseModel.js'); const BaseModel = require('lib/BaseModel.js');
const { Log } = require('lib/log.js');
const { sprintf } = require('sprintf-js'); const { sprintf } = require('sprintf-js');
const BaseItem = require('lib/models/BaseItem.js'); const BaseItem = require('lib/models/BaseItem.js');
const ItemChange = require('lib/models/ItemChange.js');
const Setting = require('lib/models/Setting.js'); const Setting = require('lib/models/Setting.js');
const { shim } = require('lib/shim.js'); const { shim } = require('lib/shim.js');
const { time } = require('lib/time-utils.js'); const { time } = require('lib/time-utils.js');
@ -81,7 +81,17 @@ class Note extends BaseItem {
static defaultTitle(note) { static defaultTitle(note) {
if (note.body && note.body.length) { if (note.body && note.body.length) {
const lines = note.body.trim().split("\n"); const lines = note.body.trim().split("\n");
return lines[0].trim().substr(0, 80).trim(); let output = lines[0].trim();
// Remove the first #, *, etc.
while (output.length) {
const c = output[0];
if (['#', ' ', "\n", "\t", '*', '`', '-'].indexOf(c) >= 0) {
output = output.substr(1);
} else {
break;
}
}
return output.substr(0, 80).trim();
} }
return _('Untitled'); return _('Untitled');
@ -161,7 +171,7 @@ class Note extends BaseItem {
} }
static previewFields() { static previewFields() {
return ['id', 'title', 'body', 'is_todo', 'todo_completed', 'parent_id', 'updated_time', 'user_updated_time', 'encryption_applied']; return ['id', 'title', 'body', 'is_todo', 'todo_completed', 'parent_id', 'updated_time', 'user_updated_time', 'user_created_time', 'encryption_applied'];
} }
static previewFieldsSql() { static previewFieldsSql() {
@ -398,6 +408,8 @@ class Note extends BaseItem {
const note = await super.save(o, options); const note = await super.save(o, options);
ItemChange.add(BaseModel.TYPE_NOTE, note.id, isNew ? ItemChange.TYPE_CREATE : ItemChange.TYPE_UPDATE);
this.dispatch({ this.dispatch({
type: 'NOTE_UPDATE_ONE', type: 'NOTE_UPDATE_ONE',
note: note, note: note,
@ -413,18 +425,22 @@ class Note extends BaseItem {
return note; return note;
} }
static async delete(id, options = null) { // Not used?
let r = await super.delete(id, options);
this.dispatch({ // static async delete(id, options = null) {
type: 'NOTE_DELETE', // let r = await super.delete(id, options);
id: id,
});
}
static batchDelete(ids, options = null) { // this.dispatch({
const result = super.batchDelete(ids, options); // type: 'NOTE_DELETE',
// id: id,
// });
// }
static async batchDelete(ids, options = null) {
const result = await super.batchDelete(ids, options);
for (let i = 0; i < ids.length; i++) { for (let i = 0; i < ids.length; i++) {
ItemChange.add(BaseModel.TYPE_NOTE, ids[i], ItemChange.TYPE_DELETE);
this.dispatch({ this.dispatch({
type: 'NOTE_DELETE', type: 'NOTE_DELETE',
id: ids[i], id: ids[i],

View File

@ -0,0 +1,70 @@
const BaseModel = require('lib/BaseModel.js');
class NoteResource extends BaseModel {
static tableName() {
return 'note_resources';
}
static modelType() {
return BaseModel.TYPE_NOTE_RESOURCE;
}
static async setAssociatedResources(noteId, resourceIds) {
const existingRows = await this.modelSelectAll('SELECT * FROM note_resources WHERE note_id = ?', [noteId]);
const notProcessedResourceIds = resourceIds.slice();
const queries = [];
for (let i = 0; i < existingRows.length; i++) {
const row = existingRows[i];
const resourceIndex = resourceIds.indexOf(row.resource_id);
if (resourceIndex >= 0) {
queries.push({ sql: 'UPDATE note_resources SET last_seen_time = ?, is_associated = 1 WHERE id = ?', params: [Date.now(), row.id] });
notProcessedResourceIds.splice(notProcessedResourceIds.indexOf(row.resource_id), 1);
} else {
queries.push({ sql: 'UPDATE note_resources SET is_associated = 0 WHERE id = ?', params: [row.id] });
}
}
for (let i = 0; i < notProcessedResourceIds.length; i++) {
queries.push({ sql: 'INSERT INTO note_resources (note_id, resource_id, is_associated, last_seen_time) VALUES (?, ?, ?, ?)', params: [noteId, notProcessedResourceIds[i], 1, Date.now()] });
}
await this.db().transactionExecBatch(queries);
}
static async addOrphanedResources() {
const missingResources = await this.db().selectAll('SELECT id FROM resources WHERE id NOT IN (SELECT DISTINCT resource_id FROM note_resources)');
const queries = [];
for (let i = 0; i < missingResources.length; i++) {
const id = missingResources[i].id;
queries.push({ sql: 'INSERT INTO note_resources (note_id, resource_id, is_associated, last_seen_time) VALUES (?, ?, ?, ?)', params: ["", id, 0, Date.now()] });
}
await this.db().transactionExecBatch(queries);
}
static async remove(noteId) {
await this.db().exec({ sql: 'UPDATE note_resources SET is_associated = 0 WHERE note_id = ?', params: [noteId] });
}
static async orphanResources(expiryDelay = null) {
if (expiryDelay === null) expiryDelay = 1000 * 60 * 60 * 24;
const cutOffTime = Date.now() - expiryDelay;
const output = await this.modelSelectAll(`
SELECT resource_id, sum(is_associated)
FROM note_resources
GROUP BY resource_id
HAVING sum(is_associated) <= 0
AND last_seen_time < ?
`, [cutOffTime]);
return output.map(r => r.resource_id);
}
static async deleteByResource(resourceId) {
await this.db().exec('DELETE FROM note_resources WHERE resource_id = ?', [resourceId]);
}
}
module.exports = NoteResource;

View File

@ -1,5 +1,6 @@
const BaseModel = require('lib/BaseModel.js'); const BaseModel = require('lib/BaseModel.js');
const BaseItem = require('lib/models/BaseItem.js'); const BaseItem = require('lib/models/BaseItem.js');
const NoteResource = require('lib/models/NoteResource.js');
const Setting = require('lib/models/Setting.js'); const Setting = require('lib/models/Setting.js');
const ArrayUtils = require('lib/ArrayUtils.js'); const ArrayUtils = require('lib/ArrayUtils.js');
const pathUtils = require('lib/path-utils.js'); const pathUtils = require('lib/path-utils.js');
@ -143,6 +144,20 @@ class Resource extends BaseItem {
return url.substr(2); return url.substr(2);
} }
static async batchDelete(ids, options = null) {
// For resources, there's not really batch deleting since there's the file data to delete
// too, so each is processed one by one with the item being deleted last (since the db
// call is the less likely to fail).
for (let i = 0; i < ids.length; i++) {
const id = ids[i];
const resource = await Resource.load(id);
const path = Resource.fullPath(resource);
await this.fsDriver().remove(path);
await super.batchDelete([id], options);
await NoteResource.deleteByResource(id); // Clean up note/resource relationships
}
}
} }
Resource.IMAGE_MAX_DIMENSION = 1920; Resource.IMAGE_MAX_DIMENSION = 1920;

View File

@ -0,0 +1,23 @@
const BaseModel = require('lib/BaseModel.js');
const Note = require('lib/models/Note.js');
class Search extends BaseModel {
static tableName() {
throw new Error('Not using database');
}
static modelType() {
return BaseModel.TYPE_SEARCH;
}
static keywords(query) {
let output = query.trim();
output = output.split(/[\s\t\n]+/);
output = output.filter(o => !!o);
return output;
}
}
module.exports = Search;

Some files were not shown because too many files have changed in this diff Show More