1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-27 20:29:45 +02:00

Compare commits

...

127 Commits

Author SHA1 Message Date
Andros Fenollosa
5e82e62335 Linux script install: implement previous updates (#905)
Avoid an erroneous update and prevent installation icon only on gnome.
2018-10-24 19:17:18 +01:00
Yannis Mitsos
de954827df Support FreeBSD in terminal (#896) 2018-10-24 19:16:15 +01:00
Laurent Cozic
2cb24bf198 Mobile: Fixes #902: Don't change existing note when sharing with mobile app 2018-10-24 19:10:05 +01:00
Laurent Cozic
739a6a4a9c Documentation 2018-10-24 18:47:04 +01:00
Laurent Cozic
dfcf1193dc Electron: Handle internal anchors 2018-10-17 08:01:18 +01:00
Laurent Cozic
c72f92e22f Additional info 2018-10-15 18:40:11 +01:00
Laurent Cozic
f6d01ce7e1 Android release v1.0.174 2018-10-15 00:01:36 +01:00
Laurent Cozic
fed9700587 Merge branch 'master' of github.com:laurent22/joplin 2018-10-15 00:00:13 +01:00
Laurent Cozic
12a3a9a89e android release 2018-10-14 23:59:42 +01:00
Laurent Cozic
590c62c371 Merge branch 'fixing_android_build' 2018-10-14 23:56:42 +01:00
Laurent Cozic
df41f64b3c Revert "trying to fix android build"
This reverts commit 621d0260f4.
2018-10-14 21:47:12 +01:00
Laurent Cozic
1849355245 Android: Tryinc to fix release builkd 2018-10-14 21:42:34 +01:00
Laurent Cozic
fa1b471ea4 Android: Tryinc to fix release builkd 2018-10-14 21:41:29 +01:00
Laurent Cozic
0a67f8c947 Revert "Android: Updated project to build on macOS"
This reverts commit b547f9aa13.
2018-10-14 20:44:05 +01:00
Laurent Cozic
621d0260f4 trying to fix android build 2018-10-14 19:57:30 +01:00
Laurent Cozic
f4d830c2ef Android release v1.0.151 2018-10-13 11:09:03 +01:00
Laurent Cozic
1aa2844efa Android release v1.0.148 2018-10-13 10:39:55 +01:00
Laurent Cozic
f22b2adaad Mobile: Improved camera attachment 2018-10-13 10:32:44 +01:00
Laurent Cozic
b547f9aa13 Android: Updated project to build on macOS 2018-10-13 00:30:41 +01:00
Laurent Cozic
e4166e9da7 Electron: Fixes #312 (maybe): Removed power saving feature, which wasn\'t doing anything and added a possible fix to the UI freezing issue on Linux 2018-10-12 23:44:00 +01:00
Laurent Cozic
1634fdb421 Merge branch 'master' of github.com:laurent22/joplin 2018-10-12 23:26:11 +01:00
Laurent Cozic
7f51035f91 Mobile: Reload note when resource got downloaded. Also fixed Android build script to make it work in macOS. 2018-10-12 23:25:11 +01:00
ebayer
70e71cbc2a Mobile: Fixes #856: Add option to open source url (#872)
* Mobile: Fixes #856: Add option to open source url

* Mobile: Fixes #856: Change menu wording for opening source url
2018-10-12 19:30:00 +01:00
Laurent Cozic
ffd03bf34c Merge branch 'master' of github.com:laurent22/joplin 2018-10-11 17:53:40 +01:00
Christian Baer
f59a3dee78 Fixed some more inconsistencies in german language file. (#855)
* Fixed some typos and inconsistencies in the german language file and added missing translations.

* Fixed some more inconsistencies in german language file.
2018-10-11 17:21:12 +01:00
Timothy Cyrus
3ba3037242 Update README.md (#874) 2018-10-11 17:20:42 +01:00
Helmut K. C. Tessarek
dbb269fef6 add support for webp images (#858)
fixes #848
2018-10-11 17:18:33 +01:00
Laurent Cozic
e209189faa All: Added resource test units 2018-10-11 17:18:24 +01:00
Laurent Cozic
2d7065cde2 Android release v1.0.143 2018-10-10 20:36:42 +01:00
Laurent Cozic
59f5972c93 Electron release v1.0.113 2018-10-10 20:32:45 +01:00
Laurent Cozic
8bac5275c3 All: Fixed fetch logic - mark resource as fetched by default, unless it comes from sync 2018-10-10 18:53:09 +01:00
Helmut K. C. Tessarek
58d748e235 fix permissions for shell scripts (add executable flag) (#866) 2018-10-09 22:58:18 +01:00
Laurent Cozic
e69ac3e62a Android release v1.0.142 2018-10-09 22:05:29 +01:00
Laurent Cozic
7fc8ac4c0f Electron release v1.0.112 2018-10-09 22:02:47 +01:00
Laurent Cozic
069dce69cd Mobile: Added support for ResourceFetcher service 2018-10-09 22:01:50 +01:00
Laurent Cozic
3bdf621026 Api: Document tags parameter 2018-10-08 19:38:27 +01:00
Laurent Cozic
2f62897fb6 All: Improved resource side loading 2018-10-08 19:11:53 +01:00
Laurent Cozic
dbdd602f50 All: Created ResourceFetcher class to handle resource downloads 2018-10-08 07:36:45 +01:00
Laurent Cozic
d66fa87b2b All: Allow excluding certain keys during sync 2018-10-07 20:18:43 +01:00
Laurent Cozic
124a959c8d All: Simplifying serialisation of base items 2018-10-07 20:11:33 +01:00
Laurent Cozic
127dce1cd6 Fixed typo 2018-10-07 19:28:19 +01:00
Laurent Cozic
44986a35a4 Android: Fix crash when attaching certain files 2018-10-07 18:55:49 +01:00
Laurent Cozic
ea516301fd Udpated German translation 2018-10-05 19:53:54 +01:00
Christian Baer
90b684457a Fixed some typos and inconsistencies in the german language file and added missing translations. (#854) 2018-10-05 19:53:13 +01:00
Laurent Cozic
8517e2aa42 Mobile: Fixes #840: Images were not being displayed right after being attached in view mode 2018-10-05 19:49:36 +01:00
Laurent Cozic
b880be8b7c All: Fixes #853: Replace characters to equivalent US-ASCII ones when exporting files 2018-10-05 17:53:55 +01:00
Laurent Cozic
57fd1a7588 Electron: Prevent URLs added via A tag from being opened inside app 2018-10-05 18:21:23 +00:00
Laurent Cozic
5ed458f634 Electron: Fixed potential crash that can happen if editor is not ready 2018-10-05 18:19:47 +00:00
Laurent Cozic
ac12143d00 All: Fixes #671: Make string translatable 2018-10-05 18:17:49 +00:00
Helmut K. C. Tessarek
b6c36d1961 fix file permissions (#851) 2018-10-04 22:32:19 +01:00
FoxMaSk
3c2de70baa add shared folder (#850)
shared 📁 is needed, so the encryption menu will be translated
2018-10-04 22:30:48 +01:00
Laurent Cozic
f6c5620682 Electron: Resolves #751: Allow switching between todo and note when multiple notes are selected 2018-10-04 18:34:30 +01:00
Laurent Cozic
79b6f64bd0 Merge branch 'master' of github.com:laurent22/joplin 2018-10-04 18:01:23 +01:00
Laurent Cozic
ed89f55bff Electron: Fixes #847: Prevent view from scrolling to top when clicking checkbox and editor not visible 2018-10-04 17:56:39 +01:00
Laurent Cozic
8841a92142 Update README.md 2018-10-04 09:10:46 +01:00
Laurent Cozic
0bd19c97eb Merge branch 'master' of github.com:laurent22/joplin 2018-10-04 08:53:11 +01:00
Laurent Cozic
2fd026d107 Mentioned Hacktoberfest 🎃 in Readme 2018-10-04 08:51:48 +01:00
Laurent Cozic
5e7eb37ca7 Merge branch 'master' of github.com:laurent22/joplin 2018-10-04 08:18:19 +01:00
Laurent Cozic
6b10d5d821 Api: Fixes #843: Fixed regression that was preventing resource metadata from being downloaded 2018-10-04 08:17:53 +01:00
Laurent Cozic
0f4dbfbcbf Merge pull request #841 from shorty2380/patch-1
Joplin_install_and_update.sh
2018-10-04 08:10:51 +01:00
Laurent Cozic
99493174ec API: Fixed handling of PUT method and log errors to file 2018-10-04 08:05:22 +01:00
Patrick Petermann
333253fd4f Joplin_install_and_update.sh
The script was original written for “Ubuntu – Gnome” only. I change it a little bit to support more distributions and desktop enviroments.

This script could be used to install and update Joplin at several Linux distributions. I could test this script with “Fedora 28 – Cinnamon” and “Mint LMDE 3”.   There are a lot of requests how to install / start Joplin at Linux in the FAQ’s. Hopefully this could help the people
2018-10-03 20:48:14 +02:00
Laurent Cozic
01470e8d3b Android release v1.0.141 2018-10-03 08:30:19 +01:00
Laurent Cozic
bda2fe6717 Merge branch 'master' of github.com:laurent22/joplin 2018-10-03 08:19:15 +01:00
Laurent Cozic
d1f4c5be18 Disable non-working ShareExtension on iOS 2018-10-03 08:17:37 +01:00
Laurent Cozic
377adea51d Android release v1.0.140 2018-10-02 18:19:27 +01:00
Laurent Cozic
cda3d20834 Android release v1.0.138 2018-10-02 18:10:37 +01:00
Laurent Cozic
d11870b1eb Trying to get RN 0.57.1 to work 2018-10-02 19:02:38 +00:00
Laurent Cozic
53bda3eea7 Trying RN 0.57.1 upgrade 2018-10-02 18:57:31 +00:00
Laurent Cozic
30165e8d6a Electron: Fixes #798: Enable Select All shortcut in macOS 2018-10-02 17:45:39 +01:00
Laurent Cozic
2202eb6570 Merge branch 'master' of github.com:laurent22/joplin 2018-10-01 20:55:52 +01:00
Laurent Cozic
720927f488 All: Fixes #832: Enex import: Don't add extra line breaks at the beginning of list item when it contains a block element 2018-10-01 20:55:24 +01:00
Helmut K. C. Tessarek
2858c0fce0 CLI v1.0.116 2018-09-30 18:28:54 -04:00
Laurent Cozic
36c3521f40 Update website 2018-09-30 21:15:21 +01:00
Laurent Cozic
98a3b99d17 Electron release v1.0.111 2018-09-30 20:45:03 +01:00
Laurent Cozic
95a06c4531 All: Fixes #818: Use sort options when displayed tagged notes 2018-09-30 20:43:46 +01:00
Laurent Cozic
6ea77b36ce Electron: Resolves #820: Allow dragging and dropping a note in another note to create a link 2018-09-30 20:15:30 +01:00
Laurent Cozic
0cd7ebf9d3 Electron: Fixes resources being incorrectly auto-deleted when inside an IMG tag 2018-09-30 19:24:02 +01:00
Laurent Cozic
a816498fc6 Android release v1.0.135 2018-09-30 10:52:30 +01:00
Laurent Cozic
549c1a6767 Android: Make camera features optional to, maybe, enable Chromebooks 2018-09-30 10:48:10 +01:00
Laurent Cozic
f87d1f11b0 API: Allow downloading a resource data 2018-09-30 10:15:46 +01:00
Laurent Cozic
fb913bc33c Mobile: Fixes #654: Fixed auto-title logic so that set title doesn't get overwritten in some conditions for new notes 2018-09-29 15:57:18 +01:00
Laurent Cozic
53d7a51cb0 Removed API doc from translations and updated FR translation 2018-09-29 13:29:07 +01:00
Laurent Cozic
12da48c756 Electron release v1.0.110 2018-09-29 13:17:48 +01:00
Laurent Cozic
a0a6bdb684 Update website 2018-09-29 13:15:36 +01:00
Laurent Cozic
eb4aa2c026 API: Added more calls 2018-09-29 12:54:44 +01:00
Laurent Cozic
a9e789f845 Clipper: Fixed screenshot text box label colour 2018-09-29 12:53:16 +01:00
Laurent Cozic
89b76918bd Merge branch 'master' of github.com:laurent22/joplin 2018-09-28 21:04:23 +01:00
Laurent Cozic
e98575643c API: Added documentation generator and built documentation 2018-09-28 21:03:28 +01:00
Laurent Cozic
7c9e7743f1 Merge pull request #825 from sensor-freak/patch-2
Fixed some german translations
2018-09-28 20:58:32 +01:00
sensor-freak
435aa4845b Fixed some german translations 2018-09-28 21:43:17 +02:00
Laurent Cozic
9841488ce4 Updated translations 2018-09-28 19:25:56 +01:00
Laurent Cozic
9c907989a5 Merge branch 'master' of github.com:laurent22/joplin 2018-09-28 19:25:18 +01:00
Laurent Cozic
f684d8e59a Merge pull request #824 from sensor-freak/patch-1
Update de_DE.json
2018-09-28 19:25:05 +01:00
Laurent Cozic
a1ad6c9712 API: Added more API calls 2018-09-28 19:24:57 +01:00
Laurent Cozic
b6ca3090df Merge pull request #822 from foxmask/master
Forbidden is 403 - typo :)
2018-09-28 19:23:48 +01:00
Helmut K. C. Tessarek
ff2d793fbb CLI v1.0.115 2018-09-28 14:08:21 -04:00
sensor-freak
fcfb7f1111 Update de_DE.json
Fix and add some german translations
2018-09-28 07:39:13 +02:00
FoxMaSk
6125cde223 Forbidden is 403 - typo :) 2018-09-27 21:38:16 +02:00
Laurent Cozic
c83391e624 Update website 2018-09-27 19:02:01 +01:00
Laurent Cozic
a3a818ea74 Clipper release v1.0.7 2018-09-27 19:00:04 +01:00
Laurent Cozic
54a4965503 Android release v1.0.133 2018-09-27 18:54:14 +01:00
Laurent Cozic
2233d88c01 Electron release v1.0.109 2018-09-27 18:50:38 +01:00
Laurent Cozic
9680ab74a3 All: Allow loading image resources in IMG html tags 2018-09-27 18:42:34 +01:00
Laurent Cozic
ef711af5b5 Api: Added method to get notes 2018-09-27 18:35:10 +00:00
Laurent Cozic
8a619e4b8b All: Refactored REST API to make it testable and to allow further extension 2018-09-27 09:14:05 +01:00
Laurent Cozic
bc09d2c640 Clipper: Fixes #817: Added support for PICTURE tags, which will fix issues with certain pages from which images were not being imported 2018-09-25 21:19:54 +01:00
Laurent Cozic
f82dfde6f4 Clipper: Fixed importing certain images with sources that contain brackets 2018-09-24 20:15:23 +01:00
Laurent Cozic
312c7f2d27 Electron: Fixed regression following security fix - links were being opened inside Joplin instead of in external browser 2018-09-24 20:14:21 +01:00
Laurent Cozic
953cc327c6 Electron: Fixes #805: Fixed app freezing when opening note in external editor and then creating new note 2018-09-24 07:10:00 +01:00
Laurent Cozic
14cff96713 Mobile: Fix potential crash when going back to OneDrive login screen 2018-09-23 20:45:34 +01:00
Laurent Cozic
34b9af2ce0 Electron: Fixes #802: Scale note text correctly when using zoom 2018-09-23 19:48:50 +01:00
Laurent Cozic
6a6ee280c3 All: Fixes #801: Replaced freegeoip which is no longer free with ip-api 2018-09-23 19:45:13 +01:00
Laurent Cozic
861387707a CLI: Fixes #795: Display tree of notebooks correctly 2018-09-23 19:33:44 +01:00
Laurent Cozic
830e665366 Updated translation 2018-09-23 19:15:49 +01:00
Laurent Cozic
f14ae68ea0 Merge pull request #812 from ikunya/update-japo
Update Japanese Translations.
2018-09-23 19:15:16 +01:00
Laurent Cozic
c7084bf27e Updated French translation 2018-09-23 19:15:04 +01:00
Laurent Cozic
fc8ffcbe46 Clipper: Fixes #672: Make sure selected notebook is saved and restored correctly 2018-09-23 18:44:39 +01:00
Laurent Cozic
77f089654e Clipper: Resolves #681: Allow adding tags from Web Clipper 2018-09-23 18:03:11 +01:00
Laurent Cozic
e7a12bb0dd Clipper: Fixes #809: Saves full URL with note, including query parameters 2018-09-22 17:41:09 +01:00
AWASHIRO Ikuya
22fe3a4e44 Update Japanese Translations. 2018-09-22 23:34:07 +09:00
Laurent Cozic
afb8b92528 Clipper: Fixed init sequence 2018-09-22 11:21:39 +01:00
Laurent Cozic
5178f99100 Electron release v1.0.108 2018-09-21 18:20:34 +01:00
Laurent Cozic
72af564382 Electron: Fixed security issue by enabling contextIsolation and proxying IPC messages via preload script 2018-09-21 18:20:06 +01:00
Laurent Cozic
0a2b83998c Merge pull request #811 from mvonmaltitz/master
Fix ubuntu install script
2018-09-21 12:00:54 +01:00
Marcel von Maltitz
73e79213dc Fix ubuntu install script
If no former version is present, the script fails since the rm commands do not succeed.
Added -f to rm in order to ignore non-existent directories to be deleted.
2018-09-21 12:55:01 +02:00
Laurent Cozic
e31ffc9474 Update website 2018-09-16 20:51:28 +01:00
197 changed files with 8606 additions and 2779 deletions

0
.gitignore vendored Executable file → Normal file
View File

View File

@@ -13,6 +13,7 @@ tests/fuzzing.*
tests/fuzzing -*
tests/logs/*
tests/cli-integration/
tests/tmp/
*.mo
*.*~
tests/sync

View File

@@ -21,7 +21,6 @@ const { _, setLocale, defaultLocale, closestSupportedLocale } = require('lib/loc
const os = require('os');
const fs = require('fs-extra');
const { cliUtils } = require('./cli-utils.js');
const EventEmitter = require('events');
const Cache = require('lib/Cache');
class Application extends BaseApplication {

View File

@@ -0,0 +1,281 @@
const { BaseCommand } = require('./base-command.js');
const { _ } = require('lib/locale.js');
const { cliUtils } = require('./cli-utils.js');
const EncryptionService = require('lib/services/EncryptionService');
const DecryptionWorker = require('lib/services/DecryptionWorker');
const MasterKey = require('lib/models/MasterKey');
const BaseItem = require('lib/models/BaseItem');
const BaseModel = require('lib/BaseModel');
const Setting = require('lib/models/Setting.js');
const { toTitleCase } = require('lib/string-utils.js');
const { reg } = require('lib/registry.js');
const markdownUtils = require('lib/markdownUtils');
const { Database } = require('lib/database.js');
class Command extends BaseCommand {
usage() {
return 'apidoc';
}
description() {
return 'Build the API doc';
}
createPropertiesTable(tableFields) {
const headers = [
{ name: 'name', label: 'Name' },
{ name: 'type', label: 'Type', filter: (value) => {
return Database.enumName('fieldType', value);
}},
{ name: 'description', label: 'Description' },
];
return markdownUtils.createMarkdownTable(headers, tableFields);
}
async action(args) {
const models = [
{
type: BaseModel.TYPE_NOTE,
},
{
type: BaseModel.TYPE_FOLDER,
},
{
type: BaseModel.TYPE_RESOURCE,
},
{
type: BaseModel.TYPE_TAG,
},
];
const lines = [];
lines.push('# Joplin API');
lines.push('');
lines.push('When the Web Clipper service is enabled, Joplin exposes a [REST API](https://en.wikipedia.org/wiki/Representational_state_transfer) which allows third-party applications to access Joplin\'s data and to create, modify or delete notes, notebooks, resources or tags.');
lines.push('');
lines.push('In order to use it, you\'ll first need to find on which port the service is running. To do so, open the Web Clipper Options in Joplin and if the service is running it should tell you on which port. Normally it runs on port **41184**. If you want to find it programmatically, you may follow this kind of algorithm:');
lines.push('');
lines.push('```javascript');
lines.push('let port = null;');
lines.push('for (let portToTest = 41184; portToTest <= 41194; portToTest++) {');
lines.push(' const result = pingPort(portToTest); // Call GET /ping');
lines.push(' if (result == \'JoplinClipperServer\') {');
lines.push(' port = portToTest; // Found the port');
lines.push(' break;');
lines.push(' }');
lines.push('}');
lines.push('```');
lines.push('# Authorisation')
lines.push('');
lines.push('To prevent unauthorised applications from accessing the API, the calls must be authentified. To do so, you must provide a token as a query parameter for each API call. You can get this token from the Joplin desktop application, on the Web Clipper Options screen.');
lines.push('');
lines.push('This would be an example of valid cURL call using a token:');
lines.push('');
lines.push('\tcurl http://localhost:41184/notes?token=ABCD123ABCD123ABCD123ABCD123ABCD123');
lines.push('');
lines.push('In the documentation below, the token will not be specified every time however you will need to include it.');
lines.push('# Using the API');
lines.push('');
lines.push('All the calls, unless noted otherwise, receives and send **JSON data**. For example to create a new note:');
lines.push('');
lines.push('\tcurl --data \'{ "title": "My note", "body": "Some note in **Markdown**"}\' http://localhost:41184/notes');
lines.push('');
lines.push('In the documentation below, the calls may include special parameters such as :id or :note_id. You would replace this with the item ID or note ID.');
lines.push('');
lines.push('For example, for the endpoint `DELETE /tags/:id/notes/:note_id`, to remove the tag with ID "ABCD1234" from the note with ID "EFGH789", you would run for example:');
lines.push('');
lines.push('\tcurl -X DELETE http://localhost:41184/tags/ABCD1234/notes/EFGH789');
lines.push('');
lines.push('The four verbs supported by the API are the following ones:');
lines.push('');
lines.push('* **GET**: To retrieve items (notes, notebooks, etc.).');
lines.push('* **POST**: To create new items. In general most item properties are optional. If you omit any, a default value will be used.');
lines.push('* **PUT**: To update an item. Note in a REST API, traditionally PUT is used to completely replace an item, however in this API it will only replace the properties that are provided. For example if you PUT {"title": "my new title"}, only the "title" property will be changed. The other properties will be left untouched (they won\'t be cleared nor changed).');
lines.push('* **DELETE**: To delete items.');
lines.push('');
lines.push('# Filtering data');
lines.push('');
lines.push('You can change the fields that will be returned by the API using the `fields=` query parameter, which takes a list of comma separated fields. For example, to get the longitude and latitude of a note, use this:');
lines.push('');
lines.push('\tcurl http://localhost:41184/notes/ABCD123?fields=longitude,latitude');
lines.push('');
lines.push('To get the IDs only of all the tags:');
lines.push('');
lines.push('\tcurl http://localhost:41184/tags?fields=id');
lines.push('');
lines.push('# About the property types');
lines.push('');
lines.push('* Text is UTF-8.');
lines.push('* All date/time are Unix timestamps in milliseconds.');
lines.push('* Booleans are integer values 0 or 1.');
lines.push('');
lines.push('# Testing if the service is available');
lines.push('');
lines.push('Call **GET /ping** to check if the service is available. It should return "JoplinClipperServer" if it works.');
lines.push('');
for (let i = 0; i < models.length; i++) {
const model = models[i];
const ModelClass = BaseItem.getClassByItemType(model.type);
const tableName = ModelClass.tableName();
let tableFields = reg.db().tableFields(tableName, { includeDescription: true });
const singular = tableName.substr(0, tableName.length - 1);
if (model.type === BaseModel.TYPE_NOTE) {
tableFields = tableFields.slice();
tableFields.push({
name: 'body_html',
type: Database.enumId('fieldType', 'text'),
description: 'Note body, in HTML format',
});
tableFields.push({
name: 'base_url',
type: Database.enumId('fieldType', 'text'),
description: 'If `body_html` is provided and contains relative URLs, provide the `base_url` parameter too so that all the URLs can be converted to absolute ones. The base URL is basically where the HTML was fetched from, minus the query (everything after the \'?\'). For example if the original page was `https://stackoverflow.com/search?q=%5Bjava%5D+test`, the base URL is `https://stackoverflow.com/search`.',
});
tableFields.push({
name: 'image_data_url',
type: Database.enumId('fieldType', 'text'),
description: 'An image to attach to the note, in [Data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) format.',
});
tableFields.push({
name: 'crop_rect',
type: Database.enumId('fieldType', 'text'),
description: 'If an image is provided, you can also specify an optional rectangle that will be used to crop the image. In format `{ x: x, y: y, width: width, height: height }`',
});
tableFields.push({
name: 'tags',
type: Database.enumId('fieldType', 'text'),
description: 'Comma-separated list of tags. eg. `tag1,tag2`.',
});
}
lines.push('# ' + toTitleCase(tableName));
lines.push('');
if (model.type === BaseModel.TYPE_FOLDER) {
lines.push('This is actually a notebook. Internally notebooks are called "folders".');
lines.push('');
}
lines.push('## Properties');
lines.push('');
lines.push(this.createPropertiesTable(tableFields));
lines.push('');
lines.push('## GET /' + tableName);
lines.push('');
lines.push('Gets all ' + tableName);
lines.push('');
if (model.type === BaseModel.TYPE_FOLDER) {
lines.push('The folders are returned as a tree. The sub-notebooks of a notebook, if any, are under the `children` key.');
lines.push('');
}
lines.push('## GET /' + tableName + '/:id');
lines.push('');
lines.push('Gets ' + singular + ' with ID :id');
lines.push('');
if (model.type === BaseModel.TYPE_TAG) {
lines.push('## GET /tags/:id/notes');
lines.push('');
lines.push('Gets all the notes with this tag.');
lines.push('');
}
if (model.type === BaseModel.TYPE_NOTE) {
lines.push('## GET /notes/:id/tags');
lines.push('');
lines.push('Gets all the tags attached to this note.');
lines.push('');
}
if (model.type === BaseModel.TYPE_FOLDER) {
lines.push('## GET /folders/:id/notes');
lines.push('');
lines.push('Gets all the notes inside this folder.');
lines.push('');
}
if (model.type === BaseModel.TYPE_RESOURCE) {
lines.push('## GET /resources/:id/file');
lines.push('');
lines.push('Gets the actual file associated with this resource.');
lines.push('');
}
lines.push('## POST /' + tableName);
lines.push('');
lines.push('Creates a new ' + singular);
lines.push('');
if (model.type === BaseModel.TYPE_RESOURCE) {
lines.push('Creating a new resource is special because you also need to upload the file. Unlike other API calls, this one must have the "multipart/form-data" Content-Type. The file data must be passed to the "data" form field, and the other properties to the "props" form field. An example of a valid call with cURL would be:');
lines.push('');
lines.push('\tcurl -F \'data=@/path/to/file.jpg\' -F \'props={"title":"my resource title"}\' http://localhost:41184/resources');
lines.push('');
lines.push('The "data" field is required, while the "props" one is not. If not specified, default values will be used.');
lines.push('');
}
if (model.type === BaseModel.TYPE_TAG) {
lines.push('## POST /tags/:id/notes');
lines.push('');
lines.push('Post a note to this endpoint to add the tag to the note. The note data must at least contain an ID property (all other properties will be ignored).');
lines.push('');
}
if (model.type === BaseModel.TYPE_NOTE) {
lines.push('You can either specify the note body as Markdown by setting the `body` parameter, or in HTML by setting the `body_html`.');
lines.push('');
lines.push('Examples:');
lines.push('');
lines.push('* Create a note from some Markdown text');
lines.push('');
lines.push(' curl --data \'{ "title": "My note", "body": "Some note in **Markdown**"}\' http://127.0.0.1:41184/notes');
lines.push('');
lines.push('* Create a note from some HTML');
lines.push('');
lines.push(' curl --data \'{ "title": "My note", "body_html": "Some note in <b>HTML</b>"}\' http://127.0.0.1:41184/notes');
lines.push('');
lines.push('* Create a note and attach an image to it:');
lines.push('');
lines.push(' curl --data \'{ "title": "Image test", "body": "Here is Joplin icon:", "image_data_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAANZJREFUeNoAyAA3/wFwtO3K6gUB/vz2+Prw9fj/+/r+/wBZKAAExOgF4/MC9ff+MRH6Ui4E+/0Bqc/zutj6AgT+/Pz7+vv7++nu82c4DlMqCvLs8goA/gL8/fz09fb59vXa6vzZ6vjT5fbn6voD/fwC8vX4UiT9Zi//APHyAP8ACgUBAPv5APz7BPj2+DIaC2o3E+3o6ywaC5fT6gD6/QD9/QEVf9kD+/dcLQgJA/7v8vqfwOf18wA1IAIEVycAyt//v9XvAPv7APz8LhoIAPz9Ri4OAgwARgx4W/6fVeEAAAAASUVORK5CYII="}\' http://127.0.0.1:41184/notes');
lines.push('');
}
lines.push('## PUT /' + tableName + '/:id');
lines.push('');
lines.push('Sets the properties of the ' + singular + ' with ID :id');
lines.push('');
lines.push('## DELETE /' + tableName + '/:id');
lines.push('');
lines.push('Deletes the ' + singular + ' with ID :id');
lines.push('');
if (model.type === BaseModel.TYPE_TAG) {
lines.push('## DELETE /tags/:id/notes/:note_id');
lines.push('');
lines.push('Remove the tag from the note.');
lines.push('');
}
}
this.stdout(lines.join('\n'));
}
}
module.exports = Command;

View File

@@ -4,6 +4,7 @@ const { _ } = require('lib/locale.js');
const { OneDriveApiNodeUtils } = require('./onedrive-api-node-utils.js');
const Setting = require('lib/models/Setting.js');
const BaseItem = require('lib/models/BaseItem.js');
const ResourceFetcher = require('lib/services/ResourceFetcher');
const { Synchronizer } = require('lib/synchronizer.js');
const { reg } = require('lib/registry.js');
const { cliUtils } = require('./cli-utils.js');
@@ -191,6 +192,14 @@ class Command extends BaseCommand {
}
}
// When using the tool in command line mode, the ResourceFetcher service is
// not going to be running in the background, so the resources need to be
// explicitely downloaded below.
if (!app().hasGui()) {
await ResourceFetcher.instance().fetchAll();
await ResourceFetcher.instance().waitForAllFinished();
}
await app().refreshCurrentFolder();
} catch (error) {
cleanUp();

View File

@@ -32,8 +32,6 @@ class FolderListWidget extends ListWidget {
output.push(_('Search:'));
output.push(item.title);
}
// if (item && item.id) output.push(item.id.substr(0, 5));
return output.join(' ');
};
@@ -85,7 +83,6 @@ class FolderListWidget extends ListWidget {
}
set notesParentType(v) {
//if (this.notesParentType_ === v) return;
this.notesParentType_ = v;
this.updateIndexFromSelectedItemId()
this.invalidate();
@@ -123,6 +120,14 @@ class FolderListWidget extends ListWidget {
this.updateIndexFromSelectedItemId()
this.invalidate();
}
folderHasChildren_(folders, folderId) {
for (let i = 0; i < folders.length; i++) {
let folder = folders[i];
if (folder.parent_id === folderId) return true;
}
return false;
}
render() {
if (this.updateItems_) {
@@ -130,7 +135,18 @@ class FolderListWidget extends ListWidget {
const wasSelectedItemId = this.selectedJoplinItemId;
const previousParentType = this.notesParentType;
let newItems = this.folders.slice();
let newItems = [];
const orderFolders = (parentId) => {
for (let i = 0; i < this.folders.length; i++) {
const f = this.folders[i];
if (f.parent_id === parentId) {
newItems.push(f);
if (this.folderHasChildren_(this.folders, f.id)) orderFolders(f.id);
}
}
}
orderFolders('');
if (this.tags.length) {
if (newItems.length) newItems.push('-');

View File

@@ -633,6 +633,10 @@ msgstr "Retalla"
msgid "Paste"
msgstr "Enganxa"
#, fuzzy
msgid "Select all"
msgstr "Seleccioneu una data"
msgid "Bold"
msgstr ""
@@ -716,6 +720,9 @@ msgstr "Sí"
msgid "No"
msgstr "No"
msgid "Token has been copied to the clipboard!"
msgstr ""
msgid "The web clipper service is enabled and set to auto-start."
msgstr ""
"El servei de desa-retalls de webs és actiu i configurat per a iniciar-se "
@@ -766,6 +773,21 @@ msgstr "Pas 2: Instal·leu l'extensió"
msgid "Download and install the relevant extension for your browser:"
msgstr "Baixeu i instal·leu l'extensió adient per al vostre navegador:"
#, fuzzy
msgid "Advanced options"
msgstr "Mostra les opcions avançades"
msgid "Authorisation token:"
msgstr ""
msgid "Copy token"
msgstr ""
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
msgid "Check synchronisation configuration"
msgstr "Comprova la configuració de la sincronització"
@@ -930,6 +952,14 @@ msgstr "Copia"
msgid "Switch between note and to-do type"
msgstr "Alterna entre el tipus nota i tasques pendents"
#, fuzzy
msgid "Switch to note type"
msgstr "Alterna entre el tipus nota i tasques pendents"
#, fuzzy
msgid "Switch to to-do type"
msgstr "Alterna entre el tipus nota i tasques pendents"
msgid "Copy Markdown link"
msgstr "Copia l'enllaç Markdown"
@@ -1523,6 +1553,10 @@ msgstr "Voleu moure %d notes al bloc de notes «%s»?"
msgid "Press to set the decryption password."
msgstr "Premeu per a establir la contrasenya de desxifratge."
#, fuzzy
msgid "Clear alarm"
msgstr "Estableix una alarma"
msgid "Save alarm"
msgstr "Desa l'alarma"
@@ -1539,6 +1573,36 @@ msgstr "Cancel·la la sincronització"
msgid "Decrypting items: %d/%d"
msgstr "Elements obtinguts: %d/%d."
#, fuzzy
msgid "Checking... Please wait."
msgstr "S'està cancel·lant... Espereu."
#, fuzzy
msgid "Success! Synchronisation configuration appears to be correct."
msgstr "Comprova la configuració de la sincronització"
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
#, fuzzy
msgid "The application has been authorised!"
msgstr "L'aplicació s'ha autoritzat correctament."
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
#, fuzzy, javascript-format
msgid "Decrypted items: %s / %s"
msgstr "Elements obtinguts: %d/%d."
msgid "New tags:"
msgstr "Etiquetes noves:"

View File

@@ -615,6 +615,10 @@ msgstr "Vyjmout"
msgid "Paste"
msgstr "Vložit"
#, fuzzy
msgid "Select all"
msgstr "Vybrat datum"
msgid "Bold"
msgstr ""
@@ -698,6 +702,9 @@ msgstr "Ano"
msgid "No"
msgstr "Ne"
msgid "Token has been copied to the clipboard!"
msgstr ""
msgid "The web clipper service is enabled and set to auto-start."
msgstr ""
@@ -741,6 +748,21 @@ msgstr ""
msgid "Download and install the relevant extension for your browser:"
msgstr ""
#, fuzzy
msgid "Advanced options"
msgstr "Ukázat pokročilé volby"
msgid "Authorisation token:"
msgstr ""
msgid "Copy token"
msgstr ""
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
msgid "Check synchronisation configuration"
msgstr "Zkontrolujte nastavení synchronizace"
@@ -902,6 +924,14 @@ msgstr "Kopírovat"
msgid "Switch between note and to-do type"
msgstr "Přepnout mezi poznámkou a to-do"
#, fuzzy
msgid "Switch to note type"
msgstr "Přepnout mezi poznámkou a to-do"
#, fuzzy
msgid "Switch to to-do type"
msgstr "Přepnout mezi poznámkou a to-do"
#, fuzzy
msgid "Copy Markdown link"
msgstr "Markdown"
@@ -1498,6 +1528,10 @@ msgstr "Přesunout poznámky %d do zápisníku \"%s\"?"
msgid "Press to set the decryption password."
msgstr "Stiskněte pro zadání hesla k dešifrování."
#, fuzzy
msgid "Clear alarm"
msgstr "Nastavit alarm"
#, fuzzy
msgid "Save alarm"
msgstr "Nastavit alarm"
@@ -1515,6 +1549,36 @@ msgstr "Zrušit synchronizaci"
msgid "Decrypting items: %d/%d"
msgstr "Získané položky: %d/%d."
#, fuzzy
msgid "Checking... Please wait."
msgstr "Zastavuji, chvíli strpení."
#, fuzzy
msgid "Success! Synchronisation configuration appears to be correct."
msgstr "Zkontrolujte nastavení synchronizace"
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
#, fuzzy
msgid "The application has been authorised!"
msgstr "Aplikace byla úspěšně autorizována."
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
#, fuzzy, javascript-format
msgid "Decrypted items: %s / %s"
msgstr "Získané položky: %d/%d."
msgid "New tags:"
msgstr "Nové tagy:"

View File

@@ -620,6 +620,10 @@ msgstr "Klip"
msgid "Paste"
msgstr "Indsæt"
#, fuzzy
msgid "Select all"
msgstr "Vælg dato"
msgid "Bold"
msgstr ""
@@ -703,6 +707,9 @@ msgstr "Ja"
msgid "No"
msgstr "Nej"
msgid "Token has been copied to the clipboard!"
msgstr ""
msgid "The web clipper service is enabled and set to auto-start."
msgstr ""
@@ -746,6 +753,21 @@ msgstr ""
msgid "Download and install the relevant extension for your browser:"
msgstr ""
#, fuzzy
msgid "Advanced options"
msgstr "Vis avancerede indstillinger"
msgid "Authorisation token:"
msgstr ""
msgid "Copy token"
msgstr ""
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
msgid "Check synchronisation configuration"
msgstr "Check synkroniserings Indstillinger"
@@ -911,6 +933,14 @@ msgstr "Kopier"
msgid "Switch between note and to-do type"
msgstr "Skift mellem note- og opgave type"
#, fuzzy
msgid "Switch to note type"
msgstr "Skift mellem note- og opgave type"
#, fuzzy
msgid "Switch to to-do type"
msgstr "Skift mellem note- og opgave type"
#, fuzzy
msgid "Copy Markdown link"
msgstr "Markdown"
@@ -1507,6 +1537,10 @@ msgstr "Flyt %d noter til notesbogen \"%s\"?"
msgid "Press to set the decryption password."
msgstr "Klik for at gemme dekrypterings kodeord."
#, fuzzy
msgid "Clear alarm"
msgstr "Indstil alarm"
#, fuzzy
msgid "Save alarm"
msgstr "Indstil alarm"
@@ -1524,6 +1558,36 @@ msgstr "Afbryd synkronisering"
msgid "Decrypting items: %d/%d"
msgstr "Hentede emner: %d/%d."
#, fuzzy
msgid "Checking... Please wait."
msgstr "Annullerer... Vent venligst."
#, fuzzy
msgid "Success! Synchronisation configuration appears to be correct."
msgstr "Check synkroniserings Indstillinger"
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
#, fuzzy
msgid "The application has been authorised!"
msgstr "Denne app er succesfuldt godkendt."
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
#, fuzzy, javascript-format
msgid "Decrypted items: %s / %s"
msgstr "Hentede emner: %d/%d."
msgid "New tags:"
msgstr "Nye tags:"

View File

@@ -13,13 +13,14 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.0.8\n"
"X-Generator: Poedit 2.1.1\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
msgid "To delete a tag, untag the associated notes."
msgstr ""
"Um ein Schlagwort zu löschen, entferne es bei allen damit verbundenen "
"Notizen."
"Um ein Tag zu löschen, entferne es bei allen damit verbundenen Notizen."
msgid "Please select the note or notebook to be deleted first."
msgstr ""
@@ -197,8 +198,8 @@ msgid ""
"Exports Joplin data to the given path. By default, it will export the "
"complete database including notebooks, notes, tags and resources."
msgstr ""
"Exportiert Joplin Dateien in den angegebenen Pfad. Standardmäßig wird die "
"komplette Datenbank inklusive Notizbüchern, Notizen, Markierungen und "
"Exportiert Joplin-Dateien in den angegebenen Pfad. Standardmäßig wird die "
"komplette Datenbank inklusive Notizbüchern, Notizen, Schlagwörtern und "
"Anhängen exportiert."
#, javascript-format
@@ -306,7 +307,7 @@ msgstr "Anhänge: %d."
#, javascript-format
msgid "Tagged: %d."
msgstr "Markiert: %d."
msgstr "Verschlagwortet: %d."
msgid "Importing notes..."
msgstr "Importiere Notizen..."
@@ -475,16 +476,15 @@ msgstr "Starte Synchronisation..."
msgid "Cancelling... Please wait."
msgstr "Abbrechen… Bitte warten."
#, fuzzy
msgid ""
"<tag-command> can be \"add\", \"remove\" or \"list\" to assign or remove "
"[tag] from [note], or to list the notes associated with [tag]. The command "
"`tag list` can be used to list all the tags (use -l for long option)."
msgstr ""
"<tag-command> kann \"add\", \"remove\" or \"list\" sein, um ein [Schlagwort] "
"zu [Notiz] zuzuweisen oder zu entfernen, oder um mit [Schlagwort] markierte "
"Notizen anzuzeigen. Mit dem Befehl `tag list` können alle Schlagwörter "
"angezeigt werden."
"<tag-command> kann add“, „remove“ oder „list sein um [tag] zu [note] "
"hinzuzufügen oder zu entfernen, oder um die Notizen aufzulisten die [tag] "
"zugeordnet sind. Der Befehl `tag list` kann benutzt werden, um alle "
"Schlagwörter anzuzeigen (nutze -l für die lange Option)."
#, javascript-format
msgid "Invalid command: \"%s\""
@@ -644,6 +644,9 @@ msgstr "Ausschneiden"
msgid "Paste"
msgstr "Einfügen"
msgid "Select all"
msgstr "Alle auswählen"
msgid "Bold"
msgstr "Fett"
@@ -666,7 +669,7 @@ msgid "Toggle sidebar"
msgstr "Seitenleiste ein/aus"
msgid "Toggle editor layout"
msgstr "Editor Layout umschalten"
msgstr "Editor-Layout umschalten"
msgid "Tools"
msgstr "Werkzeuge"
@@ -675,7 +678,7 @@ msgid "Synchronisation status"
msgstr "Status der Synchronisation"
msgid "Web clipper options"
msgstr "Web Clipper Optionen"
msgstr "Web-Clipper Optionen"
msgid "Encryption options"
msgstr "Verschlüsselungsoptionen"
@@ -719,7 +722,7 @@ msgid "Current version is up-to-date."
msgstr "Die aktuelle Version ist up-to-date."
msgid "An update is available, do you want to download it now?"
msgstr "Es ist ein Update verfügbar! Soll es jetzt heruntergeladen werden?"
msgstr "Es ist ein Update verfügbar. Soll es jetzt heruntergeladen werden?"
msgid "Yes"
msgstr "Ja"
@@ -727,9 +730,12 @@ msgstr "Ja"
msgid "No"
msgstr "Nein"
msgid "Token has been copied to the clipboard!"
msgstr "Token wurde in die Zwischenablage kopiert."
msgid "The web clipper service is enabled and set to auto-start."
msgstr ""
"Der Webclipperservice ist bereits aktiviert und auf Autostart eingestellt."
"Der Web-Clipper-Service ist bereits aktiviert und auf Autostart eingestellt."
#, javascript-format
msgid "Status: Started on port %d"
@@ -740,26 +746,26 @@ msgid "Status: %s"
msgstr "Status: %s"
msgid "Disable Web Clipper Service"
msgstr "Web Clipper Service deaktivieren"
msgstr "Web-Clipper-Service deaktivieren"
msgid "The web clipper service is not enabled."
msgstr "Der Web Clipper Service ist nicht aktiviert."
msgstr "Der Web-Clipper-Service ist nicht aktiviert."
msgid "Enable Web Clipper Service"
msgstr "Web Clipper Service aktivieren"
msgstr "Web-Clipper-Service aktivieren"
msgid ""
"Joplin Web Clipper allows saving web pages and screenshots from your browser "
"to Joplin."
msgstr ""
"Joplin Web Clipper erlaubt im Browser das Speichern von Webseiten und "
"Joplin Web-Clipper erlaubt im Browser das Speichern von Webseiten und "
"Screenshots nach Joplin."
msgid "In order to use the web clipper, you need to do the following:"
msgstr "Um den Web Clipper zu benutzen, musst du folgendes machen:"
msgstr "Um den Web-Clipper zu benutzen, musst du folgendes machen:"
msgid "Step 1: Enable the clipper service"
msgstr "Schritt 1: Clipper Service aktivieren"
msgstr "Schritt 1: Clipper-Service aktivieren"
msgid ""
"This service allows the browser extension to communicate with Joplin. When "
@@ -768,7 +774,7 @@ msgid ""
msgstr ""
"Dieser Service erlaubt es der Browser-Erweiterung mit Joplin zu "
"kommunizieren. Beim Aktivieren kann deine Firewall dich nach der Erlaubnis "
"bitten, dass Joplin auf einen bestimmten Port mithört."
"bitten, dass Joplin auf einen bestimmten Port mithören darf."
msgid "Step 2: Install the extension"
msgstr "Schritt 2: Erweiterung installieren"
@@ -778,6 +784,22 @@ msgstr ""
"Lade die entsprechende Erweiterung für deinen Browser herunter und "
"installiere sie:"
msgid "Advanced options"
msgstr "Erweiterte Optionen"
msgid "Authorisation token:"
msgstr "Autorisierung-Token:"
msgid "Copy token"
msgstr "Token kopieren"
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
"Dieses Autorisierung-Token wird nur benötigt, um Drittanbieter-Anwendungen "
"Zugriff auf Joplin zu gewähren."
msgid "Check synchronisation configuration"
msgstr "Überprüfen der Synchronisationseinstellungen"
@@ -856,13 +878,14 @@ msgstr ""
"Notizbücher ursprünglich verschlüsselt wurden."
msgid "Missing Master Keys"
msgstr "Fehlender Master-Key"
msgstr "Fehlender Hauptschlüssel"
msgid ""
"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 "
"they will eventually be downloaded via synchronisation."
msgstr "Die Master-Keas dieser IDs werden für die Verschlüsselung einiger ..."
msgstr ""
"Die Hauptschlüssel dieser IDs werden für die Verschlüsselung einiger ..."
msgid ""
"For more information about End-To-End Encryption (E2EE) and advices on how "
@@ -894,10 +917,10 @@ msgid "Please create a notebook first"
msgstr "Bitte erstelle zuerst ein Notizbuch"
msgid "Notebook title:"
msgstr "Notizbuch Titel:"
msgstr "Notizbuch-Titel:"
msgid "Add or remove tags:"
msgstr "Füge hinzu oder entferne Schlagwörter:"
msgstr "Schlagwörter hinzufügen oder entfernen:"
msgid "Separate each tag by a comma."
msgstr "Trenne jedes Schlagwort mit einem Komma."
@@ -942,6 +965,12 @@ msgstr "%s kopieren"
msgid "Switch between note and to-do type"
msgstr "Zwischen Notiz und To-Do Typ wechseln"
msgid "Switch to note type"
msgstr "Zu Notiz-Typ wechseln"
msgid "Switch to to-do type"
msgstr "Zu To-Do-Typ wechseln"
msgid "Copy Markdown link"
msgstr "Markdown-Link kopieren"
@@ -953,8 +982,7 @@ msgstr "Notizen löschen?"
msgid "No notes in here. Create one by clicking on \"New note\"."
msgstr ""
"Hier sind noch keine Notizen. Erstelle eine, indem du auf \"Neue Notiz\" "
"drückst."
"Keine Notizen vorhanden. Erstelle eine, indem du auf \"Neue Notiz\" drückst."
msgid ""
"There is currently no notebook. Create one by clicking on \"New notebook\"."
@@ -963,10 +991,10 @@ msgstr ""
"\"Neues Notizbuch\" drückst."
msgid "Location"
msgstr ""
msgstr "Ablageort"
msgid "URL"
msgstr ""
msgstr "URL"
msgid "Open..."
msgstr "Öffne..."
@@ -976,7 +1004,7 @@ msgid "This file could not be opened: %s"
msgstr "Dieses Notizbuch konnte nicht geöffnet werden: %s"
msgid "Save as..."
msgstr "Sichern unter..."
msgstr "Speichern unter..."
msgid "Copy path to clipboard"
msgstr "Pfad in Zwischenablage kopieren"
@@ -1012,7 +1040,7 @@ msgid "Attach file"
msgstr "Datei anhängen"
msgid "Tags"
msgstr "Markierungen"
msgstr "Schlagwörter"
msgid "Set alarm"
msgstr "Alarm erstellen"
@@ -1022,7 +1050,7 @@ msgid "In: %s"
msgstr "In: %s"
msgid "Note properties"
msgstr ""
msgstr "Notiz-Eigenschaften"
msgid "Hyperlink"
msgstr "Weblink"
@@ -1048,9 +1076,8 @@ msgstr "Horizontale Linie"
msgid "Click to stop external editing"
msgstr "Klicken Sie hier, um die externe Bearbeitung anzuhalten"
#, fuzzy
msgid "Watching..."
msgstr "Ansehen…"
msgstr "Zuschauend…"
msgid "to-do"
msgstr "To-Do"
@@ -1069,22 +1096,22 @@ msgid "Clear"
msgstr "Leeren"
msgid "OneDrive Login"
msgstr "OneDrive Anmeldung"
msgstr "OneDrive-Anmeldung"
msgid "Dropbox Login"
msgstr "Dropbox Anmeldung"
msgstr "Dropbox-Anmeldung"
msgid "Options"
msgstr "Optionen"
msgid "Synchronisation Status"
msgstr "Synchronisations Status"
msgstr "Synchronisations-Status"
msgid "Encryption Options"
msgstr "Verschlüsselungsoptionen"
msgid "Clipper Options"
msgstr "Clipper Einstellungen"
msgstr "Clipper-Einstellungen"
msgid "Remove this tag from all the notes?"
msgstr "Dieses Schlagwort von allen Notizen entfernen?"
@@ -1103,7 +1130,7 @@ msgstr "Notizbücher"
msgid "Please select where the sync status should be exported to"
msgstr ""
"Bitte wähle aus, wohin der Synchronisations Status exportiert werden soll"
"Bitte wähle aus, wohin der Synchronisations-Status exportiert werden soll"
#, javascript-format
msgid "Usage: %s"
@@ -1133,11 +1160,11 @@ msgstr "WebDAV"
#, javascript-format
msgid "Unknown log level: %s"
msgstr "Unbekanntes Log Level: %s"
msgstr "Unbekanntes Log-Level: %s"
#, javascript-format
msgid "Unknown level ID: %s"
msgstr "Unbekannte Level ID: %s"
msgstr "Unbekannte Level-ID: %s"
msgid ""
"Cannot refresh token: authentication data is missing. Starting the "
@@ -1159,7 +1186,7 @@ msgstr ""
"Dieser Fehler kommt oft vor, wenn OneDrive Business benutzt wird, das leider "
"nicht unterstützt wird.\n"
"\n"
"Bitte benutze stattdessen einen normalen OneDrive Account."
"Bitte benutze stattdessen einen normalen OneDrive-Account."
#, javascript-format
msgid "Cannot access %s"
@@ -1175,11 +1202,11 @@ msgstr "Lokale Objekte aktualisiert: %d."
#, javascript-format
msgid "Created remote items: %d."
msgstr "Remote Objekte erstellt: %d."
msgstr "Remote-Objekte erstellt: %d."
#, javascript-format
msgid "Updated remote items: %d."
msgstr "Remote Objekte aktualisiert: %d."
msgstr "Remote-Objekte aktualisiert: %d."
#, javascript-format
msgid "Deleted local items: %d."
@@ -1187,7 +1214,7 @@ msgstr "Lokale Objekte gelöscht: %d."
#, javascript-format
msgid "Deleted remote items: %d."
msgstr "Remote Objekte gelöscht: %d."
msgstr "Remote-Objekte gelöscht: %d."
#, javascript-format
msgid "Fetched items: %d/%d."
@@ -1198,7 +1225,7 @@ msgid "State: %s."
msgstr "Status: %s."
msgid "Cancelling..."
msgstr "Abbrechen…"
msgstr "Breche ab…"
#, javascript-format
msgid "Completed: %s"
@@ -1233,7 +1260,7 @@ msgstr "Kann Notizbuch nicht an diesen Ort verschieben"
#, javascript-format
msgid "Notebooks cannot be named \"%s\", which is a reserved title."
msgstr ""
"Notizbuch kann nicht \"%s\" genannt werden. Dies ist ein reservierter Titel."
"Notizbuch kann nicht \"%s\" genannt werden. Dieser Name ist reserviert.."
msgid "title"
msgstr "Titel"
@@ -1280,7 +1307,7 @@ msgid "Uncompleted to-dos on top"
msgstr "Zeige unvollständige To-Dos an oberster Stelle"
msgid "Show completed to-dos"
msgstr "Abgeschlossene ToDos anzeigen"
msgstr "Abgeschlossene To-Dos anzeigen"
msgid "Sort notes by"
msgstr "Sortiere Notizen nach"
@@ -1304,13 +1331,13 @@ msgid "When creating a new note:"
msgstr "Wenn eine neue Notiz erstellt wird:"
msgid "Show tray icon"
msgstr "Zeige Tray Icon"
msgstr "Zeige Tray-Icon"
msgid "Note: Does not work in all desktop environments."
msgstr "Hinweis: Funktioniert nicht in allen Desktopumgebungen."
msgid "Start application minimised in the tray icon"
msgstr ""
msgstr "Starte die Anwendung minimiert im Tray"
msgid "Global zoom percentage"
msgstr "Zoomstufe der Benutzeroberfläche"
@@ -1369,7 +1396,7 @@ msgstr ""
"dokumentiert) bezeichnet werden."
msgid "Directory to synchronise with (absolute path)"
msgstr "Verzeichnis zum synchronisieren (absoluter Pfad)"
msgstr "Verzeichnis mit dem synchronisiert werden soll (absoluter Pfad)"
msgid ""
"The path to synchronise with when file system synchronisation is enabled. "
@@ -1379,7 +1406,7 @@ msgstr ""
"Synchronisation aktiviert ist. Siehe `sync.target`."
msgid "Nextcloud WebDAV URL"
msgstr "Nextcloud WebDAV URL"
msgstr "Nextcloud WebDAV-URL"
#, javascript-format
msgid ""
@@ -1387,21 +1414,24 @@ msgid ""
"to it before syncing, otherwise all files will be removed! See the FAQ for "
"more details: %s"
msgstr ""
"Achtung: Stelle sicher, dass Du vor der Synchronisation alle Inhalte an den "
"neuen Ablageort kopiert hast, sonst werden alle Dateien gelöscht! Lies auch "
"die FAQs hierzu: %s"
msgid "Nextcloud username"
msgstr "Nextcloud Benutzername"
msgstr "Nextcloud-Benutzername"
msgid "Nextcloud password"
msgstr "Nextcloud Passwort"
msgstr "Nextcloud-Passwort"
msgid "WebDAV URL"
msgstr "WebDAV URL"
msgstr "WebDAV-URL"
msgid "WebDAV username"
msgstr "WebDAV Benutzername"
msgstr "WebDAV-Benutzername"
msgid "WebDAV password"
msgstr "WebDAV Passwort"
msgstr "WebDAV-Passwort"
msgid "Custom TLS certificates"
msgstr "Benutzerdefinierte TLS-Zertifikate"
@@ -1413,9 +1443,9 @@ msgid ""
"changes before clicking on \"Check synchronisation configuration\"."
msgstr ""
"Kommagetrennte Liste von Pfaden zu Verzeichnissen, aus denen die Zertifikate "
"geladen werden, oder Pfad zu einzelnen Zertifikatsdateien. Zum Beispiel: / "
"my / cert_dir, /other/custom.pem. Wenn Sie Änderungen an den TLS-"
"Einstellungen vornehmen, müssen Sie Ihre Änderungen speichern, bevor Sie auf "
"geladen werden, oder Pfad zu einzelnen Zertifikatsdateien. Zum Beispiel: /my/"
"cert_dir, /other/custom.pem. Wenn Sie Änderungen an den TLS-Einstellungen "
"vornehmen, müssen Sie Ihre Änderungen speichern, bevor Sie auf "
"\"Synchronisierungskonfiguration prüfen\" klicken."
msgid "Ignore TLS certificate errors"
@@ -1428,6 +1458,7 @@ msgstr "Ungültiger Optionswert: \"%s\". Mögliche Werte sind: %s."
#, javascript-format
msgid "The tag \"%s\" already exists. Please choose a different name."
msgstr ""
"Das Schlagwort \"%s\" existiert bereits. Bitte wähle einen anderen Namen."
msgid "Joplin Export File"
msgstr "Joplin Export Datei"
@@ -1546,6 +1577,9 @@ msgstr "%d Notizen in das Notizbuch \"%s\" verschieben?"
msgid "Press to set the decryption password."
msgstr "Tippe hier, um das Entschlüsselungspasswort festzulegen."
msgid "Clear alarm"
msgstr "Alarm löschen"
msgid "Save alarm"
msgstr "Alarm speichern"
@@ -1562,6 +1596,41 @@ msgstr "Synchronisation abbrechen"
msgid "Decrypting items: %d/%d"
msgstr "Entschlüsselte Objekte: %d/%d"
msgid "Checking... Please wait."
msgstr "Überprüfe… Bitte warten."
msgid "Success! Synchronisation configuration appears to be correct."
msgstr ""
"Erfolgreich. Die Synchronisation-Konfiguration scheint korrekt zu sein."
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
"Fehler. Bitte überprüfe, ob die URL, der Benutzername, das Passwort. usw. "
"korrekt sind und das das Synchronisierungsziel erreichbar ist. Fehlermeldung:"
msgid "The application has been authorised!"
msgstr "Das Programm wurde erfolgreich autorisiert."
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
"Konnte Applikation nicht autorisieren:\n"
"\n"
"%s\n"
"\n"
"Bitte versuche es erneut."
#, javascript-format
msgid "Decrypted items: %s / %s"
msgstr "Entschlüsselte Objekte: %s / %s"
msgid "New tags:"
msgstr "Neue Schlagwörter:"
@@ -1691,8 +1760,8 @@ msgstr ""
msgid "You currently have no notebook. Create one by clicking on (+) button."
msgstr ""
"Du hast noch kein Notizbuch. Erstelle eines, indem du auf den (+) Knopf "
"drückst."
"Du hast noch kein Notizbuch angelegt. Erstelle eines, indem du auf den (+) "
"Knopf drückst."
msgid "Welcome"
msgstr "Willkommen"

View File

@@ -552,6 +552,9 @@ msgstr ""
msgid "Paste"
msgstr ""
msgid "Select all"
msgstr ""
msgid "Bold"
msgstr ""
@@ -635,6 +638,9 @@ msgstr ""
msgid "No"
msgstr ""
msgid "Token has been copied to the clipboard!"
msgstr ""
msgid "The web clipper service is enabled and set to auto-start."
msgstr ""
@@ -678,6 +684,20 @@ msgstr ""
msgid "Download and install the relevant extension for your browser:"
msgstr ""
msgid "Advanced options"
msgstr ""
msgid "Authorisation token:"
msgstr ""
msgid "Copy token"
msgstr ""
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
msgid "Check synchronisation configuration"
msgstr ""
@@ -826,6 +846,12 @@ msgstr ""
msgid "Switch between note and to-do type"
msgstr ""
msgid "Switch to note type"
msgstr ""
msgid "Switch to to-do type"
msgstr ""
msgid "Copy Markdown link"
msgstr ""
@@ -1390,6 +1416,9 @@ msgstr ""
msgid "Press to set the decryption password."
msgstr ""
msgid "Clear alarm"
msgstr ""
msgid "Save alarm"
msgstr ""
@@ -1406,6 +1435,33 @@ msgstr ""
msgid "Decrypting items: %d/%d"
msgstr ""
msgid "Checking... Please wait."
msgstr ""
msgid "Success! Synchronisation configuration appears to be correct."
msgstr ""
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
msgid "The application has been authorised!"
msgstr ""
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
#, javascript-format
msgid "Decrypted items: %s / %s"
msgstr ""
msgid "New tags:"
msgstr ""

View File

@@ -629,6 +629,10 @@ msgstr "Cortar"
msgid "Paste"
msgstr "Pegar"
#, fuzzy
msgid "Select all"
msgstr "Seleccione fecha"
msgid "Bold"
msgstr "Negrita"
@@ -712,6 +716,9 @@ msgstr "Sí"
msgid "No"
msgstr "No"
msgid "Token has been copied to the clipboard!"
msgstr ""
msgid "The web clipper service is enabled and set to auto-start."
msgstr ""
"El servicio de recorte web está habilitado y configurado para que inicie "
@@ -762,6 +769,21 @@ msgstr "Paso 2: Instalar la extensión"
msgid "Download and install the relevant extension for your browser:"
msgstr "Descargar e instalar para su navegador:"
#, fuzzy
msgid "Advanced options"
msgstr "Mostrar opciones avanzadas"
msgid "Authorisation token:"
msgstr ""
msgid "Copy token"
msgstr ""
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
msgid "Check synchronisation configuration"
msgstr "Comprobar sincronización"
@@ -925,6 +947,14 @@ msgstr "%s - Copiar"
msgid "Switch between note and to-do type"
msgstr "Cambiar entre nota y lista de tareas"
#, fuzzy
msgid "Switch to note type"
msgstr "Cambiar entre nota y lista de tareas"
#, fuzzy
msgid "Switch to to-do type"
msgstr "Cambiar entre nota y lista de tareas"
msgid "Copy Markdown link"
msgstr "Copiar el enlace de Markdown"
@@ -1521,6 +1551,10 @@ msgstr "¿Desea mover %d notas a libreta «%s»?"
msgid "Press to set the decryption password."
msgstr "Presione para establecer la contraseña de descifrado."
#, fuzzy
msgid "Clear alarm"
msgstr "Establecer alarma"
msgid "Save alarm"
msgstr "Establecer alarma"
@@ -1537,6 +1571,36 @@ msgstr "Cancelar sincronización"
msgid "Decrypting items: %d/%d"
msgstr "Descifrando elementos: %d/%d."
#, fuzzy
msgid "Checking... Please wait."
msgstr "Cancelando... Por favor espere."
#, fuzzy
msgid "Success! Synchronisation configuration appears to be correct."
msgstr "Comprobar sincronización"
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
#, fuzzy
msgid "The application has been authorised!"
msgstr "La aplicacion ha sido autorizada éxitosamente."
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
#, fuzzy, javascript-format
msgid "Decrypted items: %s / %s"
msgstr "Descifrando elementos: %d/%d."
msgid "New tags:"
msgstr "Nuevas etiquetas:"

View File

@@ -628,6 +628,10 @@ msgstr "Moztu"
msgid "Paste"
msgstr "Itsatsi"
#, fuzzy
msgid "Select all"
msgstr "Data aukeratu"
msgid "Bold"
msgstr ""
@@ -713,6 +717,9 @@ msgstr ""
msgid "No"
msgstr "E"
msgid "Token has been copied to the clipboard!"
msgstr ""
msgid "The web clipper service is enabled and set to auto-start."
msgstr ""
@@ -756,6 +763,21 @@ msgstr ""
msgid "Download and install the relevant extension for your browser:"
msgstr ""
#, fuzzy
msgid "Advanced options"
msgstr "Erakutsi aukera aurreratuak"
msgid "Authorisation token:"
msgstr ""
msgid "Copy token"
msgstr ""
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
#, fuzzy
msgid "Check synchronisation configuration"
msgstr "Sinkronizazioa utzi"
@@ -921,6 +943,14 @@ msgstr "Kopiatu"
msgid "Switch between note and to-do type"
msgstr "Aldatu oharra eta zeregin eren artean."
#, fuzzy
msgid "Switch to note type"
msgstr "Aldatu oharra eta zeregin eren artean."
#, fuzzy
msgid "Switch to to-do type"
msgstr "Aldatu oharra eta zeregin eren artean."
msgid "Copy Markdown link"
msgstr ""
@@ -1528,6 +1558,10 @@ msgstr "Mugitu %d oharrak \"%s\" koadernora?"
msgid "Press to set the decryption password."
msgstr "Sakatu deszifratze pasahitza ezartzeko."
#, fuzzy
msgid "Clear alarm"
msgstr "Ezarri alarma"
#, fuzzy
msgid "Save alarm"
msgstr "Ezarri alarma"
@@ -1545,6 +1579,36 @@ msgstr "Sinkronizazioa utzi"
msgid "Decrypting items: %d/%d"
msgstr "Itemak eskuratuta: %d%d."
#, fuzzy
msgid "Checking... Please wait."
msgstr "Bertan behera uzten... itxaron, mesedez."
#, fuzzy
msgid "Success! Synchronisation configuration appears to be correct."
msgstr "Sinkronizazioa utzi"
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
#, fuzzy
msgid "The application has been authorised!"
msgstr "Aplikazioak baimena hartu du."
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
#, fuzzy, javascript-format
msgid "Decrypted items: %s / %s"
msgstr "Itemak eskuratuta: %d%d."
msgid "New tags:"
msgstr ""

View File

@@ -461,7 +461,6 @@ msgstr "Commencement de la synchronisation..."
msgid "Cancelling... Please wait."
msgstr "Annulation... Veuillez attendre."
#, fuzzy
msgid ""
"<tag-command> can be \"add\", \"remove\" or \"list\" to assign or remove "
"[tag] from [note], or to list the notes associated with [tag]. The command "
@@ -470,7 +469,7 @@ msgstr ""
"<tag-command> peut être \"add\", \"remove\" ou \"list\" pour assigner ou "
"enlever l'étiquette [tag] de la [note], our pour lister les notes associées "
"avec l'étiquette [tag]. La commande `tag list` peut être utilisée pour "
"lister les étiquettes."
"lister les étiquettes (utilisez l'option -l pour les options complètes)."
#, javascript-format
msgid "Invalid command: \"%s\""
@@ -628,6 +627,9 @@ msgstr "Couper"
msgid "Paste"
msgstr "Coller"
msgid "Select all"
msgstr "Sélectionner tout"
msgid "Bold"
msgstr "Gras"
@@ -712,6 +714,9 @@ msgstr "Oui"
msgid "No"
msgstr "Non"
msgid "Token has been copied to the clipboard!"
msgstr "Le code d'authentification a été copié dans le presse-papiers !"
msgid "The web clipper service is enabled and set to auto-start."
msgstr "Le service du Web Clipper est activé et démarrera automatiquement."
@@ -762,6 +767,22 @@ msgstr ""
"Téléchargez et installez le module complémentaire correspondant à votre "
"navigateur :"
msgid "Advanced options"
msgstr "Options avancées"
msgid "Authorisation token:"
msgstr "Code d'authentification :"
msgid "Copy token"
msgstr "Copier le code"
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
"Ce code d'authentification est nécessaire uniquement pour permettre aux "
"logiciels tiers d'accéder aux données de Joplin."
msgid "Check synchronisation configuration"
msgstr "Vérifier config synchronisation"
@@ -929,6 +950,12 @@ msgstr "%s - Copie"
msgid "Switch between note and to-do type"
msgstr "Alterner entre note et tâche"
msgid "Switch to note type"
msgstr "Convertir en note"
msgid "Switch to to-do type"
msgstr "Convertir en tâche"
msgid "Copy Markdown link"
msgstr "Copier lien Markdown"
@@ -949,10 +976,10 @@ msgstr ""
"carnet\"."
msgid "Location"
msgstr ""
msgstr "Lieu"
msgid "URL"
msgstr ""
msgstr "URL"
msgid "Open..."
msgstr "Ouvrir..."
@@ -1008,7 +1035,7 @@ msgid "In: %s"
msgstr "Dans : %s"
msgid "Note properties"
msgstr ""
msgstr "Propriétés de la note"
msgid "Hyperlink"
msgstr "Lien"
@@ -1294,7 +1321,7 @@ msgid "Note: Does not work in all desktop environments."
msgstr "Note : Ne fonctionne pas dans tous les environnements de bureau."
msgid "Start application minimised in the tray icon"
msgstr ""
msgstr "Démarrer minimisé dans la zone de notification"
msgid "Global zoom percentage"
msgstr "Niveau de zoom"
@@ -1371,6 +1398,9 @@ msgid ""
"to it before syncing, otherwise all files will be removed! See the FAQ for "
"more details: %s"
msgstr ""
"Attention : si vous changez cet emplacement, copiez-y tout le contenu avant "
"de synchroniser, sinon tous les fichiers seront supprimés ! Consulter la FAQ "
"pour plus de détails : %s"
msgid "Nextcloud username"
msgstr "Nextcloud : Nom utilisateur"
@@ -1411,7 +1441,7 @@ msgstr "Option invalide: \"%s\". Les valeurs possibles sont : %s."
#, javascript-format
msgid "The tag \"%s\" already exists. Please choose a different name."
msgstr ""
msgstr "L'étiquette \"%s\" existe déjà. Veuillez choisir un autre nom."
msgid "Joplin Export File"
msgstr "Fichier d'export Joplin"
@@ -1529,6 +1559,9 @@ msgstr "Déplacer %d notes vers carnet \"%s\" ?"
msgid "Press to set the decryption password."
msgstr "Définir mot de passe de synchronisation."
msgid "Clear alarm"
msgstr "Enlever l'alarme"
msgid "Save alarm"
msgstr "Enregistrer alarme"
@@ -1545,6 +1578,40 @@ msgstr "Annuler synchronisation"
msgid "Decrypting items: %d/%d"
msgstr "Déchiffrement des objets : %d/%d"
msgid "Checking... Please wait."
msgstr "Vérification... Veuillez attendre."
msgid "Success! Synchronisation configuration appears to be correct."
msgstr "La configuration de la synchronisation semble correcte."
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
"Erreur. Veuillez vérifier que l'URL, le nom, le mot de passe, etc. sont "
"corrects et que la destination est accessible. L'erreur reportée est :"
msgid "The application has been authorised!"
msgstr "Le logiciel a été autorisé !"
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
"Impossible d'autoriser le logiciel :\n"
"\n"
"%s\n"
"\n"
"Veuillez réessayer."
#, javascript-format
msgid "Decrypted items: %s / %s"
msgstr "Déchiffrement : %s / %s"
msgid "New tags:"
msgstr "Nouvelles étiquettes :"

View File

@@ -620,6 +620,10 @@ msgstr "Cortar"
msgid "Paste"
msgstr "Pegar"
#, fuzzy
msgid "Select all"
msgstr "Seleccionar data"
msgid "Bold"
msgstr ""
@@ -703,6 +707,9 @@ msgstr "Si"
msgid "No"
msgstr "Non"
msgid "Token has been copied to the clipboard!"
msgstr ""
msgid "The web clipper service is enabled and set to auto-start."
msgstr ""
@@ -746,6 +753,21 @@ msgstr ""
msgid "Download and install the relevant extension for your browser:"
msgstr ""
#, fuzzy
msgid "Advanced options"
msgstr "Mostrar opcións avanzadas"
msgid "Authorisation token:"
msgstr ""
msgid "Copy token"
msgstr ""
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
msgid "Check synchronisation configuration"
msgstr "Comprobar a configuración da sincronización"
@@ -908,6 +930,14 @@ msgstr "Copiar"
msgid "Switch between note and to-do type"
msgstr "Cambiar entre notas e tarefas"
#, fuzzy
msgid "Switch to note type"
msgstr "Cambiar entre notas e tarefas"
#, fuzzy
msgid "Switch to to-do type"
msgstr "Cambiar entre notas e tarefas"
#, fuzzy
msgid "Copy Markdown link"
msgstr "Markdown"
@@ -1506,6 +1536,10 @@ msgstr "Mover %d notas para o caderno \"%s\"?"
msgid "Press to set the decryption password."
msgstr "Prema para estabelecer o contrasinal de descifrado."
#, fuzzy
msgid "Clear alarm"
msgstr "Estabelecer alarma"
#, fuzzy
msgid "Save alarm"
msgstr "Estabelecer alarma"
@@ -1523,6 +1557,36 @@ msgstr "Cancelar sincronización"
msgid "Decrypting items: %d/%d"
msgstr "Elementos obtidos: %d/%d."
#, fuzzy
msgid "Checking... Please wait."
msgstr "Cancelando... Agarde."
#, fuzzy
msgid "Success! Synchronisation configuration appears to be correct."
msgstr "Comprobar a configuración da sincronización"
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
#, fuzzy
msgid "The application has been authorised!"
msgstr "O aplicativo foi autorizado correctamente."
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
#, fuzzy, javascript-format
msgid "Decrypted items: %s / %s"
msgstr "Elementos obtidos: %d/%d."
msgid "New tags:"
msgstr "Etiquetas novas:"

View File

@@ -625,6 +625,10 @@ msgstr "Izreži"
msgid "Paste"
msgstr "Zalijepi"
#, fuzzy
msgid "Select all"
msgstr "Odaberi datum"
msgid "Bold"
msgstr ""
@@ -711,6 +715,9 @@ msgstr ""
msgid "No"
msgstr "N"
msgid "Token has been copied to the clipboard!"
msgstr ""
msgid "The web clipper service is enabled and set to auto-start."
msgstr ""
@@ -754,6 +761,21 @@ msgstr ""
msgid "Download and install the relevant extension for your browser:"
msgstr ""
#, fuzzy
msgid "Advanced options"
msgstr "Prikaži napredne opcije"
msgid "Authorisation token:"
msgstr ""
msgid "Copy token"
msgstr ""
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
#, fuzzy
msgid "Check synchronisation configuration"
msgstr "Prekini sinkronizaciju"
@@ -908,6 +930,14 @@ msgstr "Kopiraj"
msgid "Switch between note and to-do type"
msgstr "Zamijeni bilješku i zadatak"
#, fuzzy
msgid "Switch to note type"
msgstr "Zamijeni bilješku i zadatak"
#, fuzzy
msgid "Switch to to-do type"
msgstr "Zamijeni bilješku i zadatak"
msgid "Copy Markdown link"
msgstr ""
@@ -1503,6 +1533,10 @@ msgstr "Premjesti %d bilješke u bilježnicu \"%s\"?"
msgid "Press to set the decryption password."
msgstr ""
#, fuzzy
msgid "Clear alarm"
msgstr "Postavi upozorenje"
#, fuzzy
msgid "Save alarm"
msgstr "Postavi upozorenje"
@@ -1520,6 +1554,36 @@ msgstr "Prekini sinkronizaciju"
msgid "Decrypting items: %d/%d"
msgstr "Stvorene lokalne stavke: %d."
#, fuzzy
msgid "Checking... Please wait."
msgstr "Prekidam... Pričekaj."
#, fuzzy
msgid "Success! Synchronisation configuration appears to be correct."
msgstr "Prekini sinkronizaciju"
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
#, fuzzy
msgid "The application has been authorised!"
msgstr "Aplikacija je uspješno autorizirana."
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
#, fuzzy, javascript-format
msgid "Decrypted items: %s / %s"
msgstr "Stvorene lokalne stavke: %d."
msgid "New tags:"
msgstr ""

View File

@@ -626,6 +626,10 @@ msgstr "Taglia"
msgid "Paste"
msgstr "Incolla"
#, fuzzy
msgid "Select all"
msgstr "Seleziona la data"
msgid "Bold"
msgstr "Grasseto"
@@ -709,6 +713,9 @@ msgstr "Sì"
msgid "No"
msgstr "No"
msgid "Token has been copied to the clipboard!"
msgstr ""
msgid "The web clipper service is enabled and set to auto-start."
msgstr ""
"Il servizio Web clipper è abilitato e impostato per l'avvio automatico."
@@ -758,6 +765,21 @@ msgstr "Passaggio 2: installare l'estensione"
msgid "Download and install the relevant extension for your browser:"
msgstr "Scarica e installa l'estensione adatta per il tuo browser:"
#, fuzzy
msgid "Advanced options"
msgstr "Mostra opzioni avanzate"
msgid "Authorisation token:"
msgstr ""
msgid "Copy token"
msgstr ""
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
msgid "Check synchronisation configuration"
msgstr "Controlla la configurazione della sincronizzazione"
@@ -923,6 +945,14 @@ msgstr "%s - Copia"
msgid "Switch between note and to-do type"
msgstr "Converti nota in \"Cose-da-fare\" e viceversa"
#, fuzzy
msgid "Switch to note type"
msgstr "Converti nota in \"Cose-da-fare\" e viceversa"
#, fuzzy
msgid "Switch to to-do type"
msgstr "Converti nota in \"Cose-da-fare\" e viceversa"
msgid "Copy Markdown link"
msgstr "Copia il link Markdown"
@@ -1520,6 +1550,10 @@ msgstr "Spostare le note %d sul Taccuino \"%s\"?"
msgid "Press to set the decryption password."
msgstr "Premere per impostare la password di decrittografia."
#, fuzzy
msgid "Clear alarm"
msgstr "Imposta Allarme"
msgid "Save alarm"
msgstr "Salva Allarme"
@@ -1536,6 +1570,36 @@ msgstr "Cancella la sincronizzazione"
msgid "Decrypting items: %d/%d"
msgstr "Decrittografia Elementi: %d/%d"
#, fuzzy
msgid "Checking... Please wait."
msgstr "Cancellazione... Attendere per favore."
#, fuzzy
msgid "Success! Synchronisation configuration appears to be correct."
msgstr "Controlla la configurazione della sincronizzazione"
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
#, fuzzy
msgid "The application has been authorised!"
msgstr "L'applicazione è stata autorizzata con successo."
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
#, fuzzy, javascript-format
msgid "Decrypted items: %s / %s"
msgstr "Decrittografia Elementi: %d/%d"
msgid "New tags:"
msgstr "Nuovi tag:"

File diff suppressed because it is too large Load Diff

View File

@@ -552,6 +552,9 @@ msgstr ""
msgid "Paste"
msgstr ""
msgid "Select all"
msgstr ""
msgid "Bold"
msgstr ""
@@ -635,6 +638,9 @@ msgstr ""
msgid "No"
msgstr ""
msgid "Token has been copied to the clipboard!"
msgstr ""
msgid "The web clipper service is enabled and set to auto-start."
msgstr ""
@@ -678,6 +684,20 @@ msgstr ""
msgid "Download and install the relevant extension for your browser:"
msgstr ""
msgid "Advanced options"
msgstr ""
msgid "Authorisation token:"
msgstr ""
msgid "Copy token"
msgstr ""
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
msgid "Check synchronisation configuration"
msgstr ""
@@ -826,6 +846,12 @@ msgstr ""
msgid "Switch between note and to-do type"
msgstr ""
msgid "Switch to note type"
msgstr ""
msgid "Switch to to-do type"
msgstr ""
msgid "Copy Markdown link"
msgstr ""
@@ -1390,6 +1416,9 @@ msgstr ""
msgid "Press to set the decryption password."
msgstr ""
msgid "Clear alarm"
msgstr ""
msgid "Save alarm"
msgstr ""
@@ -1406,6 +1435,33 @@ msgstr ""
msgid "Decrypting items: %d/%d"
msgstr ""
msgid "Checking... Please wait."
msgstr ""
msgid "Success! Synchronisation configuration appears to be correct."
msgstr ""
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
msgid "The application has been authorised!"
msgstr ""
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
#, javascript-format
msgid "Decrypted items: %s / %s"
msgstr ""
msgid "New tags:"
msgstr ""

View File

@@ -612,6 +612,10 @@ msgstr "잘라내기"
msgid "Paste"
msgstr "붙여넣기"
#, fuzzy
msgid "Select all"
msgstr "날짜 선택"
msgid "Bold"
msgstr "굵게"
@@ -695,6 +699,9 @@ msgstr "예"
msgid "No"
msgstr "아니오"
msgid "Token has been copied to the clipboard!"
msgstr ""
msgid "The web clipper service is enabled and set to auto-start."
msgstr "웹 수집기 서비스가 활성화되었고 자동으로 시작하도록 설정되었습니다."
@@ -742,6 +749,21 @@ msgstr "2단계: 확장 기능 설치"
msgid "Download and install the relevant extension for your browser:"
msgstr "브라우저에 적절한 확장 기능을 다운로드 및 설치하세요:"
#, fuzzy
msgid "Advanced options"
msgstr "고급 옵션"
msgid "Authorisation token:"
msgstr ""
msgid "Copy token"
msgstr ""
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
msgid "Check synchronisation configuration"
msgstr "동기화 설정 확인"
@@ -903,6 +925,14 @@ msgstr "%s - 복사"
msgid "Switch between note and to-do type"
msgstr "'노트' 또는'할 일' 형식으로 전환합니다."
#, fuzzy
msgid "Switch to note type"
msgstr "'노트' 또는'할 일' 형식으로 전환합니다."
#, fuzzy
msgid "Switch to to-do type"
msgstr "'노트' 또는'할 일' 형식으로 전환합니다."
msgid "Copy Markdown link"
msgstr "마크다운 링크 복사"
@@ -1495,6 +1525,10 @@ msgstr "%d 노트를 \"%s\" 노트북으로 옮길까요?"
msgid "Press to set the decryption password."
msgstr "복호화 암호를 설정하려면 누르세요."
#, fuzzy
msgid "Clear alarm"
msgstr "알람 설정"
msgid "Save alarm"
msgstr "알람 저장"
@@ -1511,6 +1545,36 @@ msgstr "동기화 취소"
msgid "Decrypting items: %d/%d"
msgstr "복호화 항목: %d/%d"
#, fuzzy
msgid "Checking... Please wait."
msgstr "취소하는 중입니다... 잠시만 기다리세요."
#, fuzzy
msgid "Success! Synchronisation configuration appears to be correct."
msgstr "동기화 설정 확인"
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
#, fuzzy
msgid "The application has been authorised!"
msgstr "애플리케이션이 성공적으로 허가되었습니다."
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
#, fuzzy, javascript-format
msgid "Decrypted items: %s / %s"
msgstr "복호화 항목: %d/%d"
msgid "New tags:"
msgstr "새 태그:"

View File

@@ -630,6 +630,10 @@ msgstr "Knip"
msgid "Paste"
msgstr "Plak"
#, fuzzy
msgid "Select all"
msgstr "Selecteer datum"
msgid "Bold"
msgstr ""
@@ -715,6 +719,9 @@ msgstr ""
msgid "No"
msgstr "N"
msgid "Token has been copied to the clipboard!"
msgstr ""
msgid "The web clipper service is enabled and set to auto-start."
msgstr ""
@@ -758,6 +765,21 @@ msgstr ""
msgid "Download and install the relevant extension for your browser:"
msgstr ""
#, fuzzy
msgid "Advanced options"
msgstr "Toon geavanceerde opties"
msgid "Authorisation token:"
msgstr ""
msgid "Copy token"
msgstr ""
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
#, fuzzy
msgid "Check synchronisation configuration"
msgstr "Annuleer synchronisatie"
@@ -923,6 +945,14 @@ msgstr "Kopieer"
msgid "Switch between note and to-do type"
msgstr "Wissel tussen notitie en to-do type"
#, fuzzy
msgid "Switch to note type"
msgstr "Wissel tussen notitie en to-do type"
#, fuzzy
msgid "Switch to to-do type"
msgstr "Wissel tussen notitie en to-do type"
msgid "Copy Markdown link"
msgstr ""
@@ -1530,6 +1560,10 @@ msgstr "Verplaats %d notities naar notitieboek \"%s\"?"
msgid "Press to set the decryption password."
msgstr "Klik om het decryptie wachtwoord in te stellen"
#, fuzzy
msgid "Clear alarm"
msgstr "Zet melding"
#, fuzzy
msgid "Save alarm"
msgstr "Zet melding"
@@ -1547,6 +1581,36 @@ msgstr "Annuleer synchronisatie"
msgid "Decrypting items: %d/%d"
msgstr "Opgehaalde items: %d/%d."
#, fuzzy
msgid "Checking... Please wait."
msgstr "Annuleren.. Even geduld."
#, fuzzy
msgid "Success! Synchronisation configuration appears to be correct."
msgstr "Annuleer synchronisatie"
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
#, fuzzy
msgid "The application has been authorised!"
msgstr "De applicatie is succesvol geauthenticeerd."
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
#, fuzzy, javascript-format
msgid "Decrypted items: %s / %s"
msgstr "Opgehaalde items: %d/%d."
msgid "New tags:"
msgstr ""

View File

@@ -629,6 +629,10 @@ msgstr "Knippen"
msgid "Paste"
msgstr "Plakken"
#, fuzzy
msgid "Select all"
msgstr "Datum kiezen"
msgid "Bold"
msgstr "Vetgedrukt"
@@ -712,6 +716,9 @@ msgstr "Ja"
msgid "No"
msgstr "Nee"
msgid "Token has been copied to the clipboard!"
msgstr ""
msgid "The web clipper service is enabled and set to auto-start."
msgstr "De webclipper-dienst is ingeschakeld en wordt automatisch opgestart."
@@ -761,6 +768,21 @@ msgstr "Stap 2: Installeer de extensie"
msgid "Download and install the relevant extension for your browser:"
msgstr "Download en installeer de bijbehorende extensie in je browser:"
#, fuzzy
msgid "Advanced options"
msgstr "Geavanceerde opties tonen"
msgid "Authorisation token:"
msgstr ""
msgid "Copy token"
msgstr ""
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
msgid "Check synchronisation configuration"
msgstr "Synchronisatieconfiguratie controleren"
@@ -927,6 +949,14 @@ msgstr "%s - kopiëren"
msgid "Switch between note and to-do type"
msgstr "Schakelen tussen notitie en taak"
#, fuzzy
msgid "Switch to note type"
msgstr "Schakelen tussen notitie en taak"
#, fuzzy
msgid "Switch to to-do type"
msgstr "Schakelen tussen notitie en taak"
msgid "Copy Markdown link"
msgstr "Markdownlink kopiëren"
@@ -1530,6 +1560,10 @@ msgstr "%d notities verplaatsen naar notitieboek \"%s\"?"
msgid "Press to set the decryption password."
msgstr "Druk om het ontsleutelwachtwoord in te stellen."
#, fuzzy
msgid "Clear alarm"
msgstr "Alarm instellen"
msgid "Save alarm"
msgstr "Alarm opslaam"
@@ -1546,6 +1580,36 @@ msgstr "Synchronisatie annuleren"
msgid "Decrypting items: %d/%d"
msgstr "Bezig met ontsleutelen van items: %d/%d"
#, fuzzy
msgid "Checking... Please wait."
msgstr "Bezig met annuleren... Even geduld."
#, fuzzy
msgid "Success! Synchronisation configuration appears to be correct."
msgstr "Synchronisatieconfiguratie controleren"
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
#, fuzzy
msgid "The application has been authorised!"
msgstr "De applicatie is geautoriseerd."
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
#, fuzzy, javascript-format
msgid "Decrypted items: %s / %s"
msgstr "Bezig met ontsleutelen van items: %d/%d"
msgid "New tags:"
msgstr "Nieuwe labels:"

View File

@@ -623,6 +623,10 @@ msgstr "Klipp ut"
msgid "Paste"
msgstr "Lim inn"
#, fuzzy
msgid "Select all"
msgstr "Velg dato"
msgid "Bold"
msgstr ""
@@ -706,6 +710,9 @@ msgstr "Ja"
msgid "No"
msgstr "Nei"
msgid "Token has been copied to the clipboard!"
msgstr ""
msgid "The web clipper service is enabled and set to auto-start."
msgstr ""
@@ -749,6 +756,21 @@ msgstr ""
msgid "Download and install the relevant extension for your browser:"
msgstr ""
#, fuzzy
msgid "Advanced options"
msgstr "Vis avanserte valg"
msgid "Authorisation token:"
msgstr ""
msgid "Copy token"
msgstr ""
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
msgid "Check synchronisation configuration"
msgstr "Sjekk synkroniseringskonfigurasjonen"
@@ -915,6 +937,14 @@ msgstr "Kopier"
msgid "Switch between note and to-do type"
msgstr "Bytt mellom notat og gjøremål"
#, fuzzy
msgid "Switch to note type"
msgstr "Bytt mellom notat og gjøremål"
#, fuzzy
msgid "Switch to to-do type"
msgstr "Bytt mellom notat og gjøremål"
msgid "Copy Markdown link"
msgstr "Kopier Link"
@@ -1512,6 +1542,10 @@ msgstr "Flytt %d notater til notisblokk \"%s\"?"
msgid "Press to set the decryption password."
msgstr "Trykk for å sette krypteringspassordet"
#, fuzzy
msgid "Clear alarm"
msgstr "Angi Alarm"
#, fuzzy
msgid "Save alarm"
msgstr "Angi Alarm"
@@ -1529,6 +1563,36 @@ msgstr "Stopp Synkronisering"
msgid "Decrypting items: %d/%d"
msgstr "Hentede elementer: %d/%d."
#, fuzzy
msgid "Checking... Please wait."
msgstr "Stopper… Vennligst vent."
#, fuzzy
msgid "Success! Synchronisation configuration appears to be correct."
msgstr "Sjekk synkroniseringskonfigurasjonen"
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
#, fuzzy
msgid "The application has been authorised!"
msgstr "Applikasjonen har blitt godkjent."
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
#, fuzzy, javascript-format
msgid "Decrypted items: %s / %s"
msgstr "Hentede elementer: %d/%d."
msgid "New tags:"
msgstr "Nye merkelapper:"

View File

@@ -625,6 +625,10 @@ msgstr "Cortar"
msgid "Paste"
msgstr "Colar"
#, fuzzy
msgid "Select all"
msgstr "Selecionar data"
msgid "Bold"
msgstr "Negrito"
@@ -708,6 +712,9 @@ msgstr "Sim"
msgid "No"
msgstr "Não"
msgid "Token has been copied to the clipboard!"
msgstr ""
msgid "The web clipper service is enabled and set to auto-start."
msgstr ""
"O serviço de web clipper está habilitado e configurado para auto-start."
@@ -757,6 +764,21 @@ msgstr "Passo 2: Instalar a extensão"
msgid "Download and install the relevant extension for your browser:"
msgstr "Baixe e instale a extensão relevante para seu browser:"
#, fuzzy
msgid "Advanced options"
msgstr "Mostrar opções avançadas"
msgid "Authorisation token:"
msgstr ""
msgid "Copy token"
msgstr ""
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
msgid "Check synchronisation configuration"
msgstr "Verificar a configuração da sincronização"
@@ -921,6 +943,14 @@ msgstr "%s - Copiar"
msgid "Switch between note and to-do type"
msgstr "Alternar entre os tipos Nota e Tarefa"
#, fuzzy
msgid "Switch to note type"
msgstr "Alternar entre os tipos Nota e Tarefa"
#, fuzzy
msgid "Switch to to-do type"
msgstr "Alternar entre os tipos Nota e Tarefa"
msgid "Copy Markdown link"
msgstr "Copiar link de Markdown"
@@ -1518,6 +1548,10 @@ msgstr "Mover %d notas para o caderno \"%s\"?"
msgid "Press to set the decryption password."
msgstr "Pressione para configurar a senha de decriptação."
#, fuzzy
msgid "Clear alarm"
msgstr "Definir alarme"
msgid "Save alarm"
msgstr "Salvar alarme"
@@ -1534,6 +1568,36 @@ msgstr "Cancelar sincronização"
msgid "Decrypting items: %d/%d"
msgstr "Decriptando itens: %d/%d."
#, fuzzy
msgid "Checking... Please wait."
msgstr "Cancelando... Aguarde."
#, fuzzy
msgid "Success! Synchronisation configuration appears to be correct."
msgstr "Verificar a configuração da sincronização"
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
#, fuzzy
msgid "The application has been authorised!"
msgstr "O aplicativo foi autorizado com sucesso."
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
#, fuzzy, javascript-format
msgid "Decrypted items: %s / %s"
msgstr "Decriptando itens: %d/%d."
msgid "New tags:"
msgstr "Novas tags:"

View File

@@ -565,6 +565,10 @@ msgstr "Tăiați"
msgid "Paste"
msgstr "Lipește"
#, fuzzy
msgid "Select all"
msgstr "Selectați data"
msgid "Bold"
msgstr ""
@@ -648,6 +652,9 @@ msgstr "Da"
msgid "No"
msgstr "Nu"
msgid "Token has been copied to the clipboard!"
msgstr ""
msgid "The web clipper service is enabled and set to auto-start."
msgstr ""
@@ -691,6 +698,21 @@ msgstr "Pasul 2: Instalați extensia"
msgid "Download and install the relevant extension for your browser:"
msgstr ""
#, fuzzy
msgid "Advanced options"
msgstr "Afișați opțiunile avansate"
msgid "Authorisation token:"
msgstr ""
msgid "Copy token"
msgstr ""
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
msgid "Check synchronisation configuration"
msgstr ""
@@ -839,6 +861,14 @@ msgstr "%s - Copiați"
msgid "Switch between note and to-do type"
msgstr "Schimbați între notiță și sarcină"
#, fuzzy
msgid "Switch to note type"
msgstr "Schimbați între notiță și sarcină"
#, fuzzy
msgid "Switch to to-do type"
msgstr "Schimbați între notiță și sarcină"
msgid "Copy Markdown link"
msgstr "Copiați link-ul Markdown"
@@ -1403,6 +1433,10 @@ msgstr "Mutați %d notițe în caietul de notițe \"%s\"?"
msgid "Press to set the decryption password."
msgstr ""
#, fuzzy
msgid "Clear alarm"
msgstr "Setați alarma"
msgid "Save alarm"
msgstr "Salvați alarma"
@@ -1419,6 +1453,34 @@ msgstr "Amânați sincronizarea"
msgid "Decrypting items: %d/%d"
msgstr "Se decriptează itemi: %d/%d"
#, fuzzy
msgid "Checking... Please wait."
msgstr "Se anulează... Vă rugăm să așteptați."
msgid "Success! Synchronisation configuration appears to be correct."
msgstr ""
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
msgid "The application has been authorised!"
msgstr ""
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
#, fuzzy, javascript-format
msgid "Decrypted items: %s / %s"
msgstr "Se decriptează itemi: %d/%d"
msgid "New tags:"
msgstr "Etichete noi:"

View File

@@ -627,6 +627,10 @@ msgstr "Вырезать"
msgid "Paste"
msgstr "Вставить"
#, fuzzy
msgid "Select all"
msgstr "Выбрать дату"
msgid "Bold"
msgstr ""
@@ -710,6 +714,9 @@ msgstr "Да"
msgid "No"
msgstr "Нет"
msgid "Token has been copied to the clipboard!"
msgstr ""
msgid "The web clipper service is enabled and set to auto-start."
msgstr ""
@@ -753,6 +760,21 @@ msgstr ""
msgid "Download and install the relevant extension for your browser:"
msgstr ""
#, fuzzy
msgid "Advanced options"
msgstr "Показывать расширенные настройки"
msgid "Authorisation token:"
msgstr ""
msgid "Copy token"
msgstr ""
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
msgid "Check synchronisation configuration"
msgstr "Проверить настройки синхронизации"
@@ -918,6 +940,14 @@ msgstr "Копировать"
msgid "Switch between note and to-do type"
msgstr "Переключить тип между заметкой и задачей"
#, fuzzy
msgid "Switch to note type"
msgstr "Переключить тип между заметкой и задачей"
#, fuzzy
msgid "Switch to to-do type"
msgstr "Переключить тип между заметкой и задачей"
#, fuzzy
msgid "Copy Markdown link"
msgstr "Markdown"
@@ -1517,6 +1547,10 @@ msgstr "Переместить %d заметок в блокнот «%s»?"
msgid "Press to set the decryption password."
msgstr "Нажмите, чтобы установить пароль для расшифровки."
#, fuzzy
msgid "Clear alarm"
msgstr "Установить напоминание"
#, fuzzy
msgid "Save alarm"
msgstr "Установить напоминание"
@@ -1534,6 +1568,36 @@ msgstr "Отменить синхронизацию"
msgid "Decrypting items: %d/%d"
msgstr "Получено элементов: %d/%d."
#, fuzzy
msgid "Checking... Please wait."
msgstr "Отмена... Пожалуйста, ожидайте."
#, fuzzy
msgid "Success! Synchronisation configuration appears to be correct."
msgstr "Проверить настройки синхронизации"
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
#, fuzzy
msgid "The application has been authorised!"
msgstr "Приложение успешно авторизовано."
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
#, fuzzy, javascript-format
msgid "Decrypted items: %s / %s"
msgstr "Получено элементов: %d/%d."
msgid "New tags:"
msgstr ""

View File

@@ -626,6 +626,10 @@ msgstr "Izreži"
msgid "Paste"
msgstr "Prilepi"
#, fuzzy
msgid "Select all"
msgstr "Izberi datum"
msgid "Bold"
msgstr ""
@@ -709,6 +713,9 @@ msgstr "Da"
msgid "No"
msgstr "Ne"
msgid "Token has been copied to the clipboard!"
msgstr ""
msgid "The web clipper service is enabled and set to auto-start."
msgstr ""
@@ -752,6 +759,21 @@ msgstr ""
msgid "Download and install the relevant extension for your browser:"
msgstr ""
#, fuzzy
msgid "Advanced options"
msgstr "Pokaži napredne možnosti"
msgid "Authorisation token:"
msgstr ""
msgid "Copy token"
msgstr ""
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
msgid "Check synchronisation configuration"
msgstr "Preveri nastavitve sinhronizacije"
@@ -918,6 +940,14 @@ msgstr "Kopiraj"
msgid "Switch between note and to-do type"
msgstr "Menjaj med zabeležko in seznamom opravil"
#, fuzzy
msgid "Switch to note type"
msgstr "Menjaj med zabeležko in seznamom opravil"
#, fuzzy
msgid "Switch to to-do type"
msgstr "Menjaj med zabeležko in seznamom opravil"
#, fuzzy
msgid "Copy Markdown link"
msgstr "Sistem označevanja"
@@ -1520,6 +1550,10 @@ msgstr "Premakni %d zabeležk v beležnico \"%s\"?"
msgid "Press to set the decryption password."
msgstr "Klikni za nastavitev dekripcijskega gesla."
#, fuzzy
msgid "Clear alarm"
msgstr "Nastavi alarm"
#, fuzzy
msgid "Save alarm"
msgstr "Nastavi alarm"
@@ -1537,6 +1571,36 @@ msgstr "Prekliči sinhronizacijo"
msgid "Decrypting items: %d/%d"
msgstr "Preneseni predmeti: %d/%d."
#, fuzzy
msgid "Checking... Please wait."
msgstr "V preklicu...Prosim počakajte."
#, fuzzy
msgid "Success! Synchronisation configuration appears to be correct."
msgstr "Preveri nastavitve sinhronizacije"
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
#, fuzzy
msgid "The application has been authorised!"
msgstr "Aplikacija je bila uspešno avtorizirana."
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
#, fuzzy, javascript-format
msgid "Decrypted items: %s / %s"
msgstr "Preneseni predmeti: %d/%d."
msgid "New tags:"
msgstr ""

View File

@@ -633,6 +633,10 @@ msgstr "Klipp ut"
msgid "Paste"
msgstr "Klistra in"
#, fuzzy
msgid "Select all"
msgstr "Välj datum"
msgid "Bold"
msgstr "Fet"
@@ -716,6 +720,9 @@ msgstr "Ja"
msgid "No"
msgstr "Nej"
msgid "Token has been copied to the clipboard!"
msgstr ""
msgid "The web clipper service is enabled and set to auto-start."
msgstr "Web clipper-tjänsten är aktiverad och inställd för automatisk start."
@@ -764,6 +771,21 @@ msgstr "Steg 2: Installera tillägget"
msgid "Download and install the relevant extension for your browser:"
msgstr "Hämta och installera det relevanta tillägget för din webbläsare:"
#, fuzzy
msgid "Advanced options"
msgstr "Visa avancerade inställningar"
msgid "Authorisation token:"
msgstr ""
msgid "Copy token"
msgstr ""
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
msgid "Check synchronisation configuration"
msgstr "Kontrollera synkroniseringskonfigurationen"
@@ -930,6 +952,14 @@ msgstr "%s - Kopiera"
msgid "Switch between note and to-do type"
msgstr "Växla mellan antecknings- och att-göra-typ"
#, fuzzy
msgid "Switch to note type"
msgstr "Växla mellan antecknings- och att-göra-typ"
#, fuzzy
msgid "Switch to to-do type"
msgstr "Växla mellan antecknings- och att-göra-typ"
msgid "Copy Markdown link"
msgstr "Kopiera Markdown-länk"
@@ -1529,6 +1559,10 @@ msgstr "Flytta %d anteckningar till anteckningsboken \"%s\"?"
msgid "Press to set the decryption password."
msgstr "Tryck för att ställa in dekrypteringslösenordet."
#, fuzzy
msgid "Clear alarm"
msgstr "Sätt alarm"
msgid "Save alarm"
msgstr "Spara alarm"
@@ -1545,6 +1579,36 @@ msgstr "Avbryt synkronisering"
msgid "Decrypting items: %d/%d"
msgstr "Dekrypterar objekt: %d/%d"
#, fuzzy
msgid "Checking... Please wait."
msgstr "Avbryter... vänta."
#, fuzzy
msgid "Success! Synchronisation configuration appears to be correct."
msgstr "Kontrollera synkroniseringskonfigurationen"
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
#, fuzzy
msgid "The application has been authorised!"
msgstr "Programmet har godkänts."
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
#, fuzzy, javascript-format
msgid "Decrypted items: %s / %s"
msgstr "Dekrypterar objekt: %d/%d"
msgid "New tags:"
msgstr "Nya taggar:"

View File

@@ -591,6 +591,10 @@ msgstr "剪切"
msgid "Paste"
msgstr "粘贴"
#, fuzzy
msgid "Select all"
msgstr "选择日期"
msgid "Bold"
msgstr "粗体"
@@ -675,6 +679,9 @@ msgstr "是"
msgid "No"
msgstr "否"
msgid "Token has been copied to the clipboard!"
msgstr ""
msgid "The web clipper service is enabled and set to auto-start."
msgstr "网页剪辑服务已启用并设置为自动启动。"
@@ -720,6 +727,21 @@ msgstr "步骤二:安装扩展"
msgid "Download and install the relevant extension for your browser:"
msgstr "为您的浏览器下载并安装相关的扩展:"
#, fuzzy
msgid "Advanced options"
msgstr "显示高级选项"
msgid "Authorisation token:"
msgstr ""
msgid "Copy token"
msgstr ""
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
msgid "Check synchronisation configuration"
msgstr "检查同步配置"
@@ -877,6 +899,14 @@ msgstr "%s - 副本"
msgid "Switch between note and to-do type"
msgstr "在笔记和待办事项类型之间切换"
#, fuzzy
msgid "Switch to note type"
msgstr "在笔记和待办事项类型之间切换"
#, fuzzy
msgid "Switch to to-do type"
msgstr "在笔记和待办事项类型之间切换"
msgid "Copy Markdown link"
msgstr "复制 Markdown 链接"
@@ -1460,6 +1490,10 @@ msgstr "移动%d条笔记至笔记本\"%s\"?"
msgid "Press to set the decryption password."
msgstr "按键将设置解密密码。"
#, fuzzy
msgid "Clear alarm"
msgstr "设置提醒"
msgid "Save alarm"
msgstr "保存提醒"
@@ -1476,6 +1510,36 @@ msgstr "取消同步"
msgid "Decrypting items: %d/%d"
msgstr "解密项目:%d/%d"
#, fuzzy
msgid "Checking... Please wait."
msgstr "正在取消... 请稍后。"
#, fuzzy
msgid "Success! Synchronisation configuration appears to be correct."
msgstr "检查同步配置"
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
#, fuzzy
msgid "The application has been authorised!"
msgstr "此程序已被成功授权。"
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
#, fuzzy, javascript-format
msgid "Decrypted items: %s / %s"
msgstr "解密项目:%d/%d"
msgid "New tags:"
msgstr "新标签:"

View File

@@ -592,6 +592,10 @@ msgstr "剪下"
msgid "Paste"
msgstr "貼上"
#, fuzzy
msgid "Select all"
msgstr "選擇日期"
msgid "Bold"
msgstr "粗體"
@@ -675,6 +679,9 @@ msgstr "是"
msgid "No"
msgstr "否"
msgid "Token has been copied to the clipboard!"
msgstr ""
msgid "The web clipper service is enabled and set to auto-start."
msgstr "Web clipper 服務已啟用,並設置為自動啟動。"
@@ -720,6 +727,21 @@ msgstr "步驟 2: 安裝插件"
msgid "Download and install the relevant extension for your browser:"
msgstr "下載並安裝瀏覽器插件:"
#, fuzzy
msgid "Advanced options"
msgstr "顯示進階選項"
msgid "Authorisation token:"
msgstr ""
msgid "Copy token"
msgstr ""
msgid ""
"This authorisation token is only needed to allow third-party applications to "
"access Joplin."
msgstr ""
msgid "Check synchronisation configuration"
msgstr "檢測同步設置"
@@ -877,6 +899,14 @@ msgstr "%s - 複本"
msgid "Switch between note and to-do type"
msgstr "切換到記事 / 待辦事項"
#, fuzzy
msgid "Switch to note type"
msgstr "切換到記事 / 待辦事項"
#, fuzzy
msgid "Switch to to-do type"
msgstr "切換到記事 / 待辦事項"
msgid "Copy Markdown link"
msgstr "複製 Markdown 連結"
@@ -1457,6 +1487,10 @@ msgstr "移動 %d 記事到記事本 \"%s\"?"
msgid "Press to set the decryption password."
msgstr "按下以設置解密密碼。"
#, fuzzy
msgid "Clear alarm"
msgstr "設置提醒"
msgid "Save alarm"
msgstr "儲存提醒事項"
@@ -1473,6 +1507,36 @@ msgstr "取消同步"
msgid "Decrypting items: %d/%d"
msgstr "正在解密項目: %d/%d 項"
#, fuzzy
msgid "Checking... Please wait."
msgstr "正在取消中...請稍候。"
#, fuzzy
msgid "Success! Synchronisation configuration appears to be correct."
msgstr "檢測同步設置"
msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
#, fuzzy
msgid "The application has been authorised!"
msgstr "應用程式已成功取得權限。"
#, javascript-format
msgid ""
"Could not authorise application:\n"
"\n"
"%s\n"
"\n"
"Please try again."
msgstr ""
#, fuzzy, javascript-format
msgid "Decrypted items: %s / %s"
msgstr "正在解密項目: %d/%d 項"
msgid "New tags:"
msgstr "新增標籤:"

File diff suppressed because it is too large Load Diff

View File

@@ -19,7 +19,7 @@
],
"owner": "Laurent Cozic"
},
"version": "1.0.114",
"version": "1.0.116",
"bin": {
"joplin": "./main.js"
},
@@ -37,12 +37,14 @@
"fs-extra": "^5.0.0",
"html-entities": "^1.2.1",
"html-minifier": "^3.5.15",
"image-data-uri": "^2.0.0",
"image-type": "^3.0.0",
"joplin-turndown": "^4.0.8",
"joplin-turndown": "^4.0.9",
"joplin-turndown-plugin-gfm": "^1.0.7",
"jssha": "^2.3.0",
"levenshtein": "^1.0.5",
"lodash": "^4.17.4",
"markdown-it": "^8.4.2",
"md5": "^2.2.1",
"mime": "^2.0.3",
"moment": "^2.18.1",
@@ -56,7 +58,7 @@
"redux": "^3.7.2",
"sax": "^1.2.2",
"server-destroy": "^1.0.1",
"sharp": "^0.18.4",
"sharp": "^0.20.8",
"sprintf-js": "^1.1.1",
"sqlite3": "^4.0.1",
"string-padding": "^1.0.2",
@@ -66,6 +68,7 @@
"tar": "^4.4.0",
"tcp-port-used": "^0.1.2",
"tkwidgets": "^0.5.26",
"unidecode": "^0.1.8",
"url-parse": "^1.2.0",
"uuid": "^3.0.1",
"valid-url": "^1.0.9",

View File

@@ -22,7 +22,6 @@ trap finish EXIT
cd "$ROOT_DIR"
npm test tests-build/ArrayUtils.js
npm test tests-build/encryption.js
npm test tests-build/EnexToMd.js
npm test tests-build/HtmlToMd.js
npm test tests-build/markdownUtils.js
@@ -32,5 +31,7 @@ npm test tests-build/models_Tag.js
npm test tests-build/models_Setting.js
npm test tests-build/services_InteropService.js
npm test tests-build/services_ResourceService.js
npm test tests-build/synchronizer.js
npm test tests-build/urlUtils.js
npm test tests-build/urlUtils.js
npm test tests-build/encryption.js
npm test tests-build/services_rest_Api.js
npm test tests-build/synchronizer.js

View File

@@ -36,7 +36,7 @@ describe('HtmlToMd', function() {
const htmlPath = basePath + '/' + htmlFilename;
const mdPath = basePath + '/' + filename(htmlFilename) + '.md';
// if (htmlFilename !== 'code_1.html') continue;
// if (htmlFilename !== 'anchor_with_url_with_spaces.html') continue;
const html = await shim.fsDriver().readFile(htmlPath);
const expectedMd = await shim.fsDriver().readFile(mdPath);

View File

@@ -0,0 +1,5 @@
<ul>
<li><div>This note has an unordered list</div></li>
<li><div>List item</div></li>
<li><div>List item</div></li>
</ul>

View File

@@ -0,0 +1,3 @@
- This note has an unordered list
- List item
- List item

View File

@@ -0,0 +1 @@
<a href="http://example.com/That is not right"/>Testing</a>

View File

@@ -0,0 +1 @@
[Testing](http://example.com/That%20is%20not%20right)

View File

@@ -0,0 +1,29 @@
Some pictures:
<picture>
<!--[if IE 9]><video style="display: none;"><![endif]-->
<source media="(min-width: 768px) and (-webkit-min-device-pixel-ratio: 1.25), (min-width: px) and (min-resolution: 120dpi)" sizes="588px" data-srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=588&amp;h=900&amp;fit=crop&amp;dpr=1.5 882w" srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=588&amp;h=900&amp;fit=crop&amp;dpr=1.5 882w">
<source media="(min-width: 768px)" sizes="588px" data-srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=588&amp;h=900&amp;fit=crop 588w" srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=588&amp;h=900&amp;fit=crop 588w">
<source media="(min-width: 481px) and (-webkit-min-device-pixel-ratio: 1.25), (min-width: px) and (min-resolution: 120dpi)" sizes="588px" data-srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=588&amp;h=900&amp;fit=crop&amp;dpr=1.5 882w" srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=588&amp;h=900&amp;fit=crop&amp;dpr=1.5 882w">
<source media="(min-width: 481px)" sizes="588px" data-srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=588&amp;h=900&amp;fit=crop 588w" srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=588&amp;h=900&amp;fit=crop 588w">
<source media="(min-width: 321px) and (-webkit-min-device-pixel-ratio: 1.25), (min-width: px) and (min-resolution: 120dpi)" sizes="450px" data-srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=450&amp;h=688&amp;fit=crop&amp;dpr=1.5 675w" srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=450&amp;h=688&amp;fit=crop&amp;dpr=1.5 675w">
<source media="(min-width: 321px)" sizes="450px" data-srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=450&amp;h=688&amp;fit=crop 450w" srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=450&amp;h=688&amp;fit=crop 450w">
<source media="(min-width: 0px) and (-webkit-min-device-pixel-ratio: 1.25), (min-width: px) and (min-resolution: 120dpi)" sizes="320px" data-srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=320&amp;h=489&amp;fit=crop&amp;dpr=1.5 480w" srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=320&amp;h=489&amp;fit=crop&amp;dpr=1.5 480w">
<source media="(min-width: 0px)" sizes="320px" data-srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=320&amp;h=489&amp;fit=crop 320w" srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=320&amp;h=489&amp;fit=crop 320w">
<!--[if IE 9]></video><![endif]-->
<img class=" lazyloaded" title="" alt="" id="img-id-0">
</picture>
<picture>
<!--[if IE 9]><video style="display: none;"><![endif]-->
<source media="(min-width: 768px) and (-webkit-min-device-pixel-ratio: 1.25), (min-width: px) and (min-resolution: 120dpi)" sizes="588px" data-srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=588&amp;h=900&amp;fit=crop&amp;dpr=1.5 882w" srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=588&amp;h=900&amp;fit=crop&amp;dpr=1.5 882w">
<source media="(min-width: 768px)" sizes="588px" data-srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=588&amp;h=900&amp;fit=crop 588w" srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=588&amp;h=900&amp;fit=crop 588w">
<source media="(min-width: 481px) and (-webkit-min-device-pixel-ratio: 1.25), (min-width: px) and (min-resolution: 120dpi)" sizes="588px" data-srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=588&amp;h=900&amp;fit=crop&amp;dpr=1.5 882w" srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=588&amp;h=900&amp;fit=crop&amp;dpr=1.5 882w">
<source media="(min-width: 481px)" sizes="588px" data-srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=588&amp;h=900&amp;fit=crop 588w" srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=588&amp;h=900&amp;fit=crop 588w">
<source media="(min-width: 321px) and (-webkit-min-device-pixel-ratio: 1.25), (min-width: px) and (min-resolution: 120dpi)" sizes="450px" data-srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=450&amp;h=688&amp;fit=crop&amp;dpr=1.5 675w" srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=450&amp;h=688&amp;fit=crop&amp;dpr=1.5 675w">
<source media="(min-width: 321px)" sizes="450px" data-srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=450&amp;h=688&amp;fit=crop 450w" srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=450&amp;h=688&amp;fit=crop 450w">
<source media="(min-width: 0px) and (-webkit-min-device-pixel-ratio: 1.25), (min-width: px) and (min-resolution: 120dpi)" sizes="320px" data-srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=320&amp;h=489&amp;fit=crop&amp;dpr=1.5 480w" srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=320&amp;h=489&amp;fit=crop&amp;dpr=1.5 480w">
<source media="(min-width: 0px)" sizes="320px" data-srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=320&amp;h=489&amp;fit=crop 320w" srcset="https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&amp;w=320&amp;h=489&amp;fit=crop 320w">
<!--[if IE 9]></video><![endif]-->
<img class=" lazyloaded" title="" alt="" id="img-id-0" src="http://example.com/test.gif">
</picture>

View File

@@ -0,0 +1 @@
Some pictures: ![](https://static2.cbrimages.com/wp-content/uploads/2018/09/Die-01-cvrA.jpg?q=35&w=588&h=900&fit=crop&dpr=1.5) ![](http://example.com/test.gif)

View File

@@ -39,6 +39,7 @@ describe('markdownUtils', function() {
['![something](http://test.com/img.png)', ['http://test.com/img.png']],
['![something](http://test.com/img.png) ![something2](http://test.com/img2.png)', ['http://test.com/img.png', 'http://test.com/img2.png']],
['![something](http://test.com/img.png "Some description")', ['http://test.com/img.png']],
['![something](https://test.com/ohoh_(123).png)', ['https://test.com/ohoh_(123).png']],
];
for (let i = 0; i < testCases.length; i++) {

View File

@@ -34,6 +34,33 @@ describe('models_Note', function() {
expect(items.length).toBe(2);
expect(items[0].type_).toBe(BaseModel.TYPE_NOTE);
expect(items[1].type_).toBe(BaseModel.TYPE_RESOURCE);
const resource = items[1];
note2.body += '<img alt="bla" src=":/' + resource.id + '"/>';
note2.body += '<img src=\':/' + resource.id + '\' />';
items = await Note.linkedItems(note2.body);
expect(items.length).toBe(4);
}));
it('should change the type of notes', asyncTest(async () => {
let folder1 = await Folder.save({ title: "folder1" });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
note1 = await Note.load(note1.id);
let changedNote = Note.changeNoteType(note1, 'todo');
expect(changedNote === note1).toBe(false);
expect(!!changedNote.is_todo).toBe(true);
await Note.save(changedNote);
note1 = await Note.load(note1.id);
changedNote = Note.changeNoteType(note1, 'todo');
expect(changedNote === note1).toBe(true);
expect(!!changedNote.is_todo).toBe(true);
note1 = await Note.load(note1.id);
changedNote = Note.changeNoteType(note1, 'note');
expect(changedNote === note1).toBe(false);
expect(!!changedNote.is_todo).toBe(false);
}));
});

View File

@@ -0,0 +1,31 @@
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 Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const Resource = require('lib/models/Resource.js');
const BaseModel = require('lib/BaseModel.js');
const { shim } = require('lib/shim');
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
describe('models_Resource', function() {
beforeEach(async (done) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
});
it('should have a "done" fetch_status when created locally', asyncTest(async () => {
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];
console.info(resource1);
}));
});

View File

@@ -79,7 +79,6 @@ describe('services_ResourceService', function() {
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();
@@ -106,4 +105,23 @@ describe('services_ResourceService', function() {
expect((await Resource.all()).length).toBe(1);
}));
it('should not delete resource if it is used in an IMG tag', 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];
await service.indexNoteResources();
await Note.save({ id: note1.id, body: 'This is HTML: <img src=":/' + resource1.id + '"/>' });
await service.indexNoteResources();
await service.deleteOrphanResources(0);
expect(!!(await Resource.load(resource1.id))).toBe(true);
}));
});

View File

@@ -0,0 +1,275 @@
require('app-module-path').addPath(__dirname);
const { time } = require('lib/time-utils.js');
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
const markdownUtils = require('lib/markdownUtils.js');
const Api = require('lib/services/rest/Api');
const Folder = require('lib/models/Folder');
const Note = require('lib/models/Note');
const Tag = require('lib/models/Tag');
const Resource = require('lib/models/Resource');
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
let api = null;
describe('services_rest_Api', function() {
beforeEach(async (done) => {
api = new Api();
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
});
it('should ping', async (done) => {
const response = await api.route('GET', 'ping');
expect(response).toBe('JoplinClipperServer');
done();
});
it('should handle Not Found errors', async (done) => {
const hasThrown = await checkThrowAsync(async () => await api.route('GET', 'pong'));
expect(hasThrown).toBe(true);
done();
});
it('should get folders', async (done) => {
let f1 = await Folder.save({ title: "mon carnet" });
const response = await api.route('GET', 'folders');
expect(response.length).toBe(1);
expect(response[0].title).toBe('mon carnet');
done();
});
it('should update folders', async (done) => {
let f1 = await Folder.save({ title: "mon carnet" });
const response = await api.route('PUT', 'folders/' + f1.id, null, JSON.stringify({
title: 'modifié',
}));
let f1b = await Folder.load(f1.id);
expect(f1b.title).toBe('modifié');
done();
});
it('should delete folders', async (done) => {
let f1 = await Folder.save({ title: "mon carnet" });
await api.route('DELETE', 'folders/' + f1.id);
let f1b = await Folder.load(f1.id);
expect(!f1b).toBe(true);
done();
});
it('should create folders', async (done) => {
const response = await api.route('POST', 'folders', null, JSON.stringify({
title: 'from api',
}));
expect(!!response.id).toBe(true);
let f = await Folder.all();
expect(f.length).toBe(1);
expect(f[0].title).toBe('from api');
done();
});
it('should get one folder', async (done) => {
let f1 = await Folder.save({ title: "mon carnet" });
const response = await api.route('GET', 'folders/' + f1.id);
expect(response.id).toBe(f1.id);
const hasThrown = await checkThrowAsync(async () => await api.route('GET', 'folders/doesntexist'));
expect(hasThrown).toBe(true);
done();
});
it('should get the folder notes', async (done) => {
let f1 = await Folder.save({ title: "mon carnet" });
const response2 = await api.route('GET', 'folders/' + f1.id + '/notes');
expect(response2.length).toBe(0);
const n1 = await Note.save({ title: 'un', parent_id: f1.id });
const n2 = await Note.save({ title: 'deux', parent_id: f1.id });
const response = await api.route('GET', 'folders/' + f1.id + '/notes');
expect(response.length).toBe(2);
done();
});
it('should fail on invalid paths', async (done) => {
const hasThrown = await checkThrowAsync(async () => await api.route('GET', 'schtroumpf'));
expect(hasThrown).toBe(true);
done();
});
it('should get notes', async (done) => {
let response = null;
const f1 = await Folder.save({ title: "mon carnet" });
const f2 = await Folder.save({ title: "mon deuxième carnet" });
const n1 = await Note.save({ title: 'un', parent_id: f1.id });
const n2 = await Note.save({ title: 'deux', parent_id: f1.id });
const n3 = await Note.save({ title: 'trois', parent_id: f2.id });
response = await api.route('GET', 'notes');
expect(response.length).toBe(3);
response = await api.route('GET', 'notes/' + n1.id);
expect(response.id).toBe(n1.id);
response = await api.route('GET', 'notes/' + n3.id, { fields: 'id,title' });
expect(Object.getOwnPropertyNames(response).length).toBe(3);
expect(response.id).toBe(n3.id);
expect(response.title).toBe('trois');
done();
});
it('should create notes', async (done) => {
let response = null;
const f = await Folder.save({ title: "mon carnet" });
response = await api.route('POST', 'notes', null, JSON.stringify({
title: 'testing',
parent_id: f.id,
}));
expect(response.title).toBe('testing');
expect(!!response.id).toBe(true);
response = await api.route('POST', 'notes', null, JSON.stringify({
title: 'testing',
parent_id: f.id,
}));
expect(response.title).toBe('testing');
expect(!!response.id).toBe(true);
done();
});
it('should create notes with images', async (done) => {
let response = null;
const f = await Folder.save({ title: "mon carnet" });
response = await api.route('POST', 'notes', null, JSON.stringify({
title: 'testing image',
parent_id: f.id,
image_data_url: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAANZJREFUeNoAyAA3/wFwtO3K6gUB/vz2+Prw9fj/+/r+/wBZKAAExOgF4/MC9ff+MRH6Ui4E+/0Bqc/zutj6AgT+/Pz7+vv7++nu82c4DlMqCvLs8goA/gL8/fz09fb59vXa6vzZ6vjT5fbn6voD/fwC8vX4UiT9Zi//APHyAP8ACgUBAPv5APz7BPj2+DIaC2o3E+3o6ywaC5fT6gD6/QD9/QEVf9kD+/dcLQgJA/7v8vqfwOf18wA1IAIEVycAyt//v9XvAPv7APz8LhoIAPz9Ri4OAgwARgx4W/6fVeEAAAAASUVORK5CYII="
}));
const resources = await Resource.all();
expect(resources.length).toBe(1);
const resource = resources[0];
expect(response.body.indexOf(resource.id) >= 0).toBe(true);
done();
});
it('should create notes from HTML', async (done) => {
let response = null;
const f = await Folder.save({ title: "mon carnet" });
response = await api.route('POST', 'notes', null, JSON.stringify({
title: 'testing HTML',
parent_id: f.id,
body_html: '<b>Bold text</b>',
}));
expect(response.body).toBe('**Bold text**');
done();
});
it('should filter fields', async (done) => {
let f = api.fields_({ query: { fields: 'one,two' } }, []);
expect(f.length).toBe(2);
expect(f[0]).toBe('one');
expect(f[1]).toBe('two');
f = api.fields_({ query: { fields: 'one ,, two ' } }, []);
expect(f.length).toBe(2);
expect(f[0]).toBe('one');
expect(f[1]).toBe('two');
f = api.fields_({ query: { fields: ' ' } }, ['def']);
expect(f.length).toBe(1);
expect(f[0]).toBe('def');
done();
});
it('should handle tokens', async (done) => {
api = new Api('mytoken');
let hasThrown = await checkThrowAsync(async () => await api.route('GET', 'notes'));
expect(hasThrown).toBe(true);
const response = await api.route('GET', 'notes', { token: 'mytoken' })
expect(response.length).toBe(0);
hasThrown = await checkThrowAsync(async () => await api.route('POST', 'notes', null, JSON.stringify({title:'testing'})));
expect(hasThrown).toBe(true);
done();
});
it('should add tags to notes', async (done) => {
const tag = await Tag.save({ title: "mon étiquette" });
const note = await Note.save({ title: "ma note" });
const response = await api.route('POST', 'tags/' + tag.id + '/notes', null, JSON.stringify({
id: note.id,
}));
const noteIds = await Tag.noteIds(tag.id);
expect(noteIds[0]).toBe(note.id);
done();
});
it('should remove tags from notes', async (done) => {
const tag = await Tag.save({ title: "mon étiquette" });
const note = await Note.save({ title: "ma note" });
await Tag.addNote(tag.id, note.id);
const response = await api.route('DELETE', 'tags/' + tag.id + '/notes/' + note.id);
const noteIds = await Tag.noteIds(tag.id);
expect(noteIds.length).toBe(0);
done();
});
it('should list all tag notes', async (done) => {
const tag = await Tag.save({ title: "mon étiquette" });
const tag2 = await Tag.save({ title: "mon étiquette 2" });
const note1 = await Note.save({ title: "ma note un" });
const note2 = await Note.save({ title: "ma note deux" });
await Tag.addNote(tag.id, note1.id);
await Tag.addNote(tag.id, note2.id);
const response = await api.route('GET', 'tags/' + tag.id + '/notes');
expect(response.length).toBe(2);
expect('id' in response[0]).toBe(true);
expect('title' in response[0]).toBe(true);
const response2 = await api.route('GET', 'notes/' + note1.id + '/tags');
expect(response2.length).toBe(1);
await Tag.addNote(tag2.id, note1.id);
const response3 = await api.route('GET', 'notes/' + note1.id + '/tags');
expect(response3.length).toBe(2);
done();
});
});

View File

@@ -7,6 +7,7 @@ const fs = require('fs-extra');
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const Resource = require('lib/models/Resource.js');
const ResourceFetcher = require('lib/services/ResourceFetcher');
const Tag = require('lib/models/Tag.js');
const { Database } = require('lib/database.js');
const Setting = require('lib/models/Setting.js');
@@ -872,12 +873,46 @@ describe('Synchronizer', function() {
let allResources = await Resource.all();
expect(allResources.length).toBe(1);
let resource1_2 = allResources[0];
let resourcePath1_2 = Resource.fullPath(resource1_2);
expect(resource1_2.id).toBe(resource1.id);
expect(resource1_2.fetch_status).toBe(Resource.FETCH_STATUS_IDLE);
const fetcher = new ResourceFetcher(() => { return synchronizer().api() });
fetcher.queueDownload(resource1_2.id);
await fetcher.waitForAllFinished();
resource1_2 = await Resource.load(resource1.id);
expect(resource1_2.fetch_status).toBe(Resource.FETCH_STATUS_DONE);
let resourcePath1_2 = Resource.fullPath(resource1_2);
expect(fileContentEqual(resourcePath1, resourcePath1_2)).toBe(true);
}));
it('should handle resource download errors', 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();
const fetcher = new ResourceFetcher(() => { return {
// Simulate a failed download
get: () => { return new Promise((resolve, reject) => { reject(new Error('did not work')) }); }
} });
fetcher.queueDownload(resource1.id);
await fetcher.waitForAllFinished();
resource1 = await Resource.load(resource1.id);
expect(resource1.fetch_status).toBe(Resource.FETCH_STATUS_ERROR);
expect(resource1.fetch_error).toBe('did not work');
}));
it('should delete resources', asyncTest(async () => {
while (insideBeforeEach) await time.msleep(500);
@@ -926,6 +961,10 @@ describe('Synchronizer', function() {
Setting.setObjectKey('encryption.passwordCache', masterKey.id, '123456');
await encryptionService().loadMasterKeysFromSettings();
const fetcher = new ResourceFetcher(() => { return synchronizer().api() });
fetcher.queueDownload(resource1.id);
await fetcher.waitForAllFinished();
let resource1_2 = (await Resource.all())[0];
resource1_2 = await Resource.decrypt(resource1_2);
let resourcePath1_2 = Resource.fullPath(resource1_2);

View File

@@ -48,7 +48,9 @@ EncryptionService.fsDriver_ = fsDriver;
FileApiDriverLocal.fsDriver_ = fsDriver;
const logDir = __dirname + '/../tests/logs';
const tempDir = __dirname + '/../tests/tmp';
fs.mkdirpSync(logDir, 0o755);
fs.mkdirpSync(tempDir, 0o755);
SyncTargetRegistry.addClass(SyncTargetMemory);
SyncTargetRegistry.addClass(SyncTargetFilesystem);
@@ -80,6 +82,7 @@ BaseItem.loadClass('MasterKey', MasterKey);
Setting.setConstant('appId', 'net.cozic.joplin-cli');
Setting.setConstant('appType', 'cli');
Setting.setConstant('tempDir', tempDir);
BaseService.logger_ = logger;

0
CliClientDemo/publish.sh Normal file → Executable file
View File

View File

@@ -80,8 +80,9 @@
title: title,
html: html,
base_url: baseUrl(),
url: location.origin + location.pathname,
url: location.origin + location.pathname + location.search,
parent_id: command.parent_id,
tags: command.tags || '',
};
}
@@ -137,6 +138,7 @@
messageComp.style.maxWidth = messageCompWidth + 'px'
messageComp.style.border = '1px solid black'
messageComp.style.background = 'white'
messageComp.style.color = 'black';
messageComp.style.top = '10px'
messageComp.style.textAlign = 'center';
messageComp.style.padding = '10px'
@@ -214,6 +216,7 @@
crop_rect: selectionArea,
url: location.origin + location.pathname,
parent_id: command.parent_id,
tags: command.tags,
};
browser_.runtime.sendMessage({

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 2,
"name": "Joplin Web Clipper [DEV]",
"version": "1.0.6",
"version": "1.0.7",
"description": "Capture and save web pages and screenshots from your browser to Joplin.",
"homepage_url": "https://joplin.cozic.net",
"icons": {

View File

@@ -1,5 +1,7 @@
<!DOCTYPE html>
<!-- NOTE: I think this is not used at all -->
<html>
<head>
<meta charset="utf-8">

View File

@@ -100,21 +100,34 @@
flex: 0;
}
.App .Folders {
.App .Folders,
.App .Tags {
display: flex;
flex-direction: row;
align-items: center;
align-items: top;
padding: 5px 0;
}
.App .Folders label {
.App .Folders label,
.App .Tags label {
flex: 0;
white-space: nowrap;
margin-right: .5em;
}
.App .Folders select {
flex: 1;
margin-left: 10px;
}
.App .Tags input {
display: inline-block;
flex: 1;
margin-bottom: .5em;
}
.App .ClearTagButton {
margin-left: .5em;
text-decoration: none;
}
.App .StatusBar {

View File

@@ -14,6 +14,7 @@ class AppComponent extends Component {
this.state = ({
contentScriptLoaded: false,
selectedTags: [],
});
this.confirm_click = () => {
@@ -31,6 +32,7 @@ class AppComponent extends Component {
bridge().sendCommandToActiveTab({
name: 'simplifiedPageHtml',
parent_id: this.props.selectedFolderId,
tags: this.state.selectedTags.join(','),
});
}
@@ -38,6 +40,7 @@ class AppComponent extends Component {
bridge().sendCommandToActiveTab({
name: 'completePageHtml',
parent_id: this.props.selectedFolderId,
tags: this.state.selectedTags.join(','),
});
}
@@ -45,6 +48,7 @@ class AppComponent extends Component {
bridge().sendCommandToActiveTab({
name: 'selectedHtml',
parent_id: this.props.selectedFolderId,
tags: this.state.selectedTags.join(','),
});
}
@@ -56,6 +60,7 @@ class AppComponent extends Component {
name: 'screenshot',
api_base_url: baseUrl,
parent_id: this.props.selectedFolderId,
tags: this.state.selectedTags.join(','),
});
window.close();
@@ -74,6 +79,41 @@ class AppComponent extends Component {
id: event.target.value,
});
}
this.tagCompChanged = this.tagCompChanged.bind(this);
this.onAddTagClick = this.onAddTagClick.bind(this);
this.onClearTagButtonClick = this.onClearTagButtonClick.bind(this);
}
onAddTagClick(event) {
const newTags = this.state.selectedTags.slice();
newTags.push('');
this.setState({ selectedTags: newTags });
this.focusNewTagInput_ = true;
}
onClearTagButtonClick(event) {
const index = event.target.getAttribute('data-index');
const newTags = this.state.selectedTags.slice();
newTags.splice(index, 1);
this.setState({ selectedTags: newTags });
}
tagCompChanged(event) {
const index = Number(event.target.getAttribute('data-index'));
const value = event.target.value;
if (this.state.selectedTags.length <= index) {
const newTags = this.state.selectedTags.slice();
newTags.push(value);
this.setState({ selectedTags: newTags });
} else {
if (this.state.selectedTags[index] !== value) {
const newTags = this.state.selectedTags.slice();
newTags[index] = value;
this.setState({ selectedTags: newTags });
}
}
}
async loadContentScripts() {
@@ -87,6 +127,39 @@ class AppComponent extends Component {
this.setState({
contentScriptLoaded: true,
});
let foundSelectedFolderId = false;
const searchSelectedFolder = (folders) => {
for (let i = 0; i < folders.length; i++) {
const folder = folders[i];
if (folder.id === this.props.selectedFolderId) foundSelectedFolderId = true;
if (folder.children) searchSelectedFolder(folder.children);
}
}
searchSelectedFolder(this.props.folders);
if (!foundSelectedFolderId) {
const newFolderId = this.props.folders.length ? this.props.folders[0].id : null;
this.props.dispatch({
type: 'SELECTED_FOLDER_SET',
id: newFolderId,
});
}
}
componentDidUpdate() {
if (this.focusNewTagInput_) {
this.focusNewTagInput_ = false;
let lastRef = null;
for (let i = 0; i < 100; i++) {
const ref = this.refs['tagSelector' + i];
if (!ref) break;
lastRef = ref;
}
if (lastRef) lastRef.focus();
}
}
render() {
@@ -119,24 +192,17 @@ class AppComponent extends Component {
<p className="Info">{ msg }</p>
</div>
);
} else {
if (hasContent) {
previewComponent = (
<div className="Preview">
<input className={"Title"} value={content.title} onChange={this.contentTitle_change}/>
<div className={"BodyWrapper"}>
<div className={"Body"} dangerouslySetInnerHTML={{__html: content.body_html}}></div>
</div>
<a className={"Confirm Button"} onClick={this.confirm_click}>Confirm</a>
} else if (hasContent) {
previewComponent = (
<div className="Preview">
<h2>Preview:</h2>
<input className={"Title"} value={content.title} onChange={this.contentTitle_change}/>
<div className={"BodyWrapper"}>
<div className={"Body"} dangerouslySetInnerHTML={{__html: content.body_html}}></div>
</div>
);
} else {
previewComponent = (
<div className="Preview">
<p className="Info">(No preview yet)</p>
</div>
);
}
<a className={"Confirm Button"} onClick={this.confirm_click}>Confirm</a>
</div>
);
}
const clipperStatusComp = () => {
@@ -166,8 +232,6 @@ class AppComponent extends Component {
return <div className="StatusBar"><img alt={foundState} className="Led" src={led}/><span className="ServerStatus">{ msg }{ helpLink }</span></div>
}
console.info(this.props.selectedFolderId);
const foldersComp = () => {
const optionComps = [];
@@ -196,6 +260,37 @@ class AppComponent extends Component {
);
}
const tagsComp = () => {
const comps = [];
for (let i = 0; i < this.state.selectedTags.length; i++) {
comps.push(<div>
<input
ref={'tagSelector' + i}
data-index={i}
key={i}
type="text"
list="tags"
value={this.state.selectedTags[i]}
onChange={this.tagCompChanged}
onInput={this.tagCompChanged}
/>
<a data-index={i} href="#" className="ClearTagButton" onClick={this.onClearTagButtonClick}>[x]</a>
</div>);
}
return (
<div>
{comps}
<a className="AddTagButton" href="#" onClick={this.onAddTagClick}>Add tag</a>
</div>
);
}
const tagDataListOptions = [];
for (let i = 0; i < this.props.tags.length; i++) {
const tag = this.props.tags[i];
tagDataListOptions.push(<option key={tag.id}>{tag.title}</option>);
}
return (
<div className="App">
<div className="Controls">
@@ -207,8 +302,14 @@ class AppComponent extends Component {
</ul>
</div>
{ foldersComp() }
<div className="Tags">
<label>Tags:</label>
{tagsComp()}
<datalist id="tags">
{tagDataListOptions}
</datalist>
</div>
{ warningComponent }
<h2>Preview:</h2>
{ previewComponent }
{ clipperStatusComp() }
</div>
@@ -224,6 +325,7 @@ const mapStateToProps = (state) => {
contentUploadOperation: state.contentUploadOperation,
clipperServer: state.clipperServer,
folders: state.folders,
tags: state.tags,
selectedFolderId: state.selectedFolderId,
};
};

View File

@@ -2,7 +2,7 @@ const randomClipperPort = require('./randomClipperPort');
class Bridge {
init(browser, browserSupportsPromises, dispatch) {
async init(browser, browserSupportsPromises, dispatch) {
console.info('Popup: Init bridge');
this.browser_ = browser;
@@ -28,6 +28,7 @@ class Bridge {
base_url: command.base_url,
source_url: command.url,
parent_id: command.parent_id,
tags: command.tags || '',
};
this.dispatch({ type: 'CLIPPED_CONTENT_SET', content: content });
@@ -36,7 +37,7 @@ class Bridge {
this.browser_.runtime.onMessage.addListener(this.browser_notify);
const backgroundPage = this.browser_.extension.getBackgroundPage();
const backgroundPage = await this.backgroundPage(this.browser_);
// Not sure why the getBackgroundPage() sometimes returns null, so
// in that case default to "prod" environment, which means the live
@@ -53,6 +54,17 @@ class Bridge {
this.findClipperServerPort();
}
async backgroundPage(browser) {
const bgp = browser.extension.getBackgroundPage();
if (bgp) return bgp;
return new Promise((resolve, reject) => {
browser.runtime.getBackgroundPage((bgp) => {
resolve(bgp);
})
});
}
env() {
return this.env_;
}
@@ -111,6 +123,11 @@ class Bridge {
const folders = await this.folderTree();
this.dispatch({ type: 'FOLDERS_SET', folders: folders });
const tags = await this.clipperApiExec('GET', 'tags');
this.dispatch({ type: 'TAGS_SET', tags: tags });
bridge().restoreState();
return;
}
} catch (error) {

View File

@@ -16,6 +16,7 @@ const defaultState = {
port: null,
},
folders: [],
tags: [],
selectedFolderId: null,
env: 'prod',
};
@@ -65,6 +66,11 @@ function reducer(state = defaultState, action) {
newState.selectedFolderId = action.folders[0].id;
}
} else if (action.type === 'TAGS_SET') {
newState = Object.assign({}, state);
newState.tags = action.tags;
} else if (action.type === 'SELECTED_FOLDER_SET') {
newState = Object.assign({}, state);
@@ -88,11 +94,18 @@ function reducer(state = defaultState, action) {
return newState;
}
const store = createStore(reducer, applyMiddleware(reduxMiddleware));
async function main() {
const store = createStore(reducer, applyMiddleware(reduxMiddleware));
bridge().init(window.browser ? window.browser : window.chrome, !!window.browser, store.dispatch);
bridge().restoreState();
console.info('Popup: Init bridge and restore state...');
console.info('Popup: Creating React app...');
await bridge().init(window.browser ? window.browser : window.chrome, !!window.browser, store.dispatch);
ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));
console.info('Popup: Creating React app...');
ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));
}
main().catch((error) => {
console.error('Fatal error on initialisation:', error);
});

View File

@@ -1,20 +1,20 @@
function randomClipperPort(state, env) {
const startPorts = {
prod: 41184,
dev: 27583,
};
const startPorts = {
prod: 41184,
dev: 27583,
};
const startPort = env === 'prod' ? startPorts.prod : startPorts.dev;
const startPort = env === 'prod' ? startPorts.prod : startPorts.dev;
if (!state) {
state = { offset: 0 };
} else {
state.offset++;
}
if (!state) {
state = { offset: 0 };
} else {
state.offset++;
}
state.port = startPort + state.offset;
state.port = startPort + state.offset;
return state;
return state;
}
module.exports = randomClipperPort;

View File

@@ -49,7 +49,6 @@ class Application extends BaseApplication {
constructor() {
super();
this.lastMenuScreen_ = null;
this.powerSaveBlockerId_ = null;
}
hasGui() {
@@ -166,8 +165,10 @@ class Application extends BaseApplication {
case 'NOTE_FILE_WATCHER_CLEAR':
newState = Object.assign({}, state);
newState.watchedNoteFiles = [];
if (state.watchedNoteFiles.length) {
newState = Object.assign({}, state);
newState.watchedNoteFiles = [];
}
break;
}
@@ -219,17 +220,6 @@ class Application extends BaseApplication {
Setting.setValue('sidebarVisibility', newState.sidebarVisibility);
}
if (action.type === 'SYNC_STARTED') {
if (!this.powerSaveBlockerId_) this.powerSaveBlockerId_ = bridge().powerSaveBlockerStart('prevent-app-suspension');
}
if (action.type === 'SYNC_COMPLETED') {
if (this.powerSaveBlockerId_) {
bridge().powerSaveBlockerStop(this.powerSaveBlockerId_);
this.powerSaveBlockerId_ = null;
}
}
return result;
}
@@ -418,6 +408,10 @@ class Application extends BaseApplication {
label: _('Paste'),
role: 'paste',
accelerator: 'CommandOrControl+V',
}, {
label: _('Select all'),
role: 'selectall',
accelerator: 'CommandOrControl+A',
}, {
type: 'separator',
screens: ['Main'],
@@ -735,8 +729,6 @@ class Application extends BaseApplication {
ids: Setting.value('collapsedFolderIds'),
});
if (shim.isLinux()) bridge().setAllowPowerSaveBlockerToggle(true);
// Note: Auto-update currently doesn't work in Linux: it downloads the update
// but then doesn't install it on exit.
if (shim.isWindows() || shim.isMac()) {

View File

@@ -1,7 +1,6 @@
const { _, setLocale } = require('lib/locale.js');
const { dirname } = require('lib/path-utils.js');
const { Logger } = require('lib/logger.js');
const { powerSaveBlocker } = require('electron');
class Bridge {
@@ -9,7 +8,6 @@ class Bridge {
this.electronWrapper_ = electronWrapper;
this.autoUpdateLogger_ = null;
this.lastSelectedPath_ = null;
this.allowPowerSaveBlockerToggle_ = false;
}
electronApp() {
@@ -24,10 +22,6 @@ class Bridge {
return this.electronWrapper_.window();
}
setAllowPowerSaveBlockerToggle(v) {
this.allowPowerSaveBlockerToggle_ = v;
}
windowContentSize() {
if (!this.window()) return { width: 0, height: 0 };
const s = this.window().getContentSize();
@@ -126,19 +120,7 @@ class Bridge {
const { checkForUpdates } = require('./checkForUpdates.js');
checkForUpdates(inBackground, window, logFilePath);
}
powerSaveBlockerStart(type) {
if (!this.allowPowerSaveBlockerToggle_) return null;
console.info('Enable powerSaveBlockerStart: ' + type);
return powerSaveBlocker.start(type);
}
powerSaveBlockerStop(id) {
if (!this.allowPowerSaveBlockerToggle_) return null;
console.info('Disable powerSaveBlocker: ' + id);
return powerSaveBlocker.stop(id);
}
}
let bridge_ = null;

File diff suppressed because one or more lines are too long

View File

@@ -97,13 +97,33 @@ class NoteListComponent extends React.Component {
}
}}));
menu.append(new MenuItem({label: _('Switch between note and to-do type'), click: async () => {
for (let i = 0; i < noteIds.length; i++) {
const note = await Note.load(noteIds[i]);
await Note.save(Note.toggleIsTodo(note), { userSideValidation: true });
eventManager.emit('noteTypeToggle', { noteId: note.id });
if (noteIds.length <= 1) {
menu.append(new MenuItem({label: _('Switch between note and to-do type'), click: async () => {
for (let i = 0; i < noteIds.length; i++) {
const note = await Note.load(noteIds[i]);
await Note.save(Note.toggleIsTodo(note), { userSideValidation: true });
eventManager.emit('noteTypeToggle', { noteId: note.id });
}
}}));
} else {
const switchNoteType = async (noteIds, type) => {
for (let i = 0; i < noteIds.length; i++) {
const note = await Note.load(noteIds[i]);
const newNote = Note.changeNoteType(note, type);
if (newNote === note) continue;
await Note.save(newNote, { userSideValidation: true });
eventManager.emit('noteTypeToggle', { noteId: note.id });
}
}
}}));
menu.append(new MenuItem({label: _('Switch to note type'), click: async () => {
await switchNoteType(noteIds, 'note');
}}));
menu.append(new MenuItem({label: _('Switch to to-do type'), click: async () => {
await switchNoteType(noteIds, 'todo');
}}));
}
menu.append(new MenuItem({label: _('Copy Markdown link'), click: async () => {
const { clipboard } = require('electron');

View File

@@ -6,6 +6,7 @@ const Search = require('lib/models/Search.js');
const { time } = require('lib/time-utils.js');
const Setting = require('lib/models/Setting.js');
const { IconButton } = require('./IconButton.min.js');
const { urlDecode } = require('lib/string-utils');
const Toolbar = require('./Toolbar.min.js');
const { connect } = require('react-redux');
const { _ } = require('lib/locale.js');
@@ -27,6 +28,7 @@ const urlUtils = require('lib/urlUtils');
const dialogs = require('./dialogs');
const markdownUtils = require('lib/markdownUtils');
const ExternalEditWatcher = require('lib/services/ExternalEditWatcher');
const ResourceFetcher = require('lib/services/ResourceFetcher');
const { toSystemSlashes, safeFilename } = require('lib/path-utils');
const { clipboard } = require('electron');
@@ -142,7 +144,20 @@ class NoteTextComponent extends React.Component {
}
this.onDrop_ = async (event) => {
const files = event.dataTransfer.files;
const dt = event.dataTransfer;
if (dt.types.indexOf("text/x-jop-note-ids") >= 0) {
const noteIds = JSON.parse(dt.getData("text/x-jop-note-ids"));
const linkText = [];
for (let i = 0; i < noteIds.length; i++) {
const note = await Note.load(noteIds[i]);
linkText.push(Note.markdownTag(note));
}
this.wrapSelectionWithStrings("", "", '', linkText.join('\n'));
}
const files = dt.files;
if (!files || !files.length) return;
const filesToAttach = [];
@@ -157,6 +172,10 @@ class NoteTextComponent extends React.Component {
}
const updateSelectionRange = () => {
if (!this.rawEditor()) {
this.selectionRange_ = null;
return;
}
const ranges = this.rawEditor().getSelection().getAllRanges();
if (!ranges || !ranges.length || !this.state.note) {
@@ -180,6 +199,16 @@ class NoteTextComponent extends React.Component {
this.reloadNote(this.props);
}
}
this.resourceFetcher_downloadComplete = async (resource) => {
if (!this.state.note || !this.state.note.body) return;
const resourceIds = await Note.linkedResourceIds(this.state.note.body);
if (resourceIds.indexOf(resource.id) >= 0) {
this.mdToHtml().clearCache();
this.lastSetHtml_ = '';
this.updateHtml(this.state.note.body);
}
}
}
// Note:
@@ -269,6 +298,8 @@ class NoteTextComponent extends React.Component {
eventManager.on('alarmChange', this.onAlarmChange_);
eventManager.on('noteTypeToggle', this.onNoteTypeToggle_);
eventManager.on('todoToggle', this.onTodoToggle_);
ResourceFetcher.instance().on('downloadComplete', this.resourceFetcher_downloadComplete);
}
componentWillUnmount() {
@@ -281,6 +312,8 @@ class NoteTextComponent extends React.Component {
eventManager.removeListener('noteTypeToggle', this.onNoteTypeToggle_);
eventManager.removeListener('todoToggle', this.onTodoToggle_);
ResourceFetcher.instance().off('downloadComplete', this.resourceFetcher_downloadComplete);
this.destroyExternalEditWatcher();
}
@@ -534,6 +567,10 @@ class NoteTextComponent extends React.Component {
if (!item) throw new Error('No item with ID ' + itemId);
if (item.type_ === BaseModel.TYPE_RESOURCE) {
if (item.fetch_status !== Resource.FETCH_STATUS_DONE || !!item.encryption_blob_encrypted) {
bridge().showErrorMessageBox(_('This attachment is not downloaded or not decrypted yet.'));
return;
}
const filePath = Resource.fullPath(item);
bridge().openItem(filePath);
} else if (item.type_ === BaseModel.TYPE_NOTE) {
@@ -552,7 +589,14 @@ class NoteTextComponent extends React.Component {
throw new Error('Unsupported item type: ' + item.type_);
}
} else if (urlUtils.urlProtocol(msg)) {
require('electron').shell.openExternal(msg);
if (msg.indexOf('file://') === 0) {
// When using the file:// protocol, openExternal doesn't work (does nothing) with URL-encoded paths
require('electron').shell.openExternal(urlDecode(msg));
} else {
require('electron').shell.openExternal(msg);
}
} else if (msg.indexOf('#') === 0) {
// This is an internal anchor, which is handled by the WebView so skip this case
} else {
bridge().showErrorMessageBox(_('Unsupported link or message: %s', msg));
}
@@ -748,7 +792,7 @@ class NoteTextComponent extends React.Component {
this.forceUpdate();
}, 100);
},
postMessageSyntax: 'ipcRenderer.sendToHost',
postMessageSyntax: 'ipcProxySendToHost',
};
const theme = themeStyle(this.props.theme);
@@ -1025,6 +1069,9 @@ class NoteTextComponent extends React.Component {
end: { row: p.row, column: p.column + middleText.length },
};
// BUG!! If replacementText contains newline characters, the logic
// to select the new text will not work.
this.updateEditorWithDelay((editor) => {
if (middleText && newRange) {
const range = this.selectionRange_;
@@ -1350,7 +1397,11 @@ class NoteTextComponent extends React.Component {
}
if (visiblePanes.indexOf('editor') < 0) {
editorStyle.display = 'none';
// Note: Ideally we'd set the display to "none" to take the editor out
// of the DOM but if we do that, certain things won't work, in particular
// things related to scroll, which are based on the editor. See
// editorScrollTop_, restoreScrollTop_, etc.
editorStyle.width = 0;
viewerStyle.width = innerWidth;
}
@@ -1404,6 +1455,7 @@ class NoteTextComponent extends React.Component {
style={viewerStyle}
preload="gui/note-viewer/preload.js"
src="gui/note-viewer/index.html"
webpreferences="contextIsolation"
ref={(elem) => { this.webview_ref(elem); } }
/>

View File

@@ -12,6 +12,7 @@ const { bridge } = require("electron").remote.require("./bridge");
const Menu = bridge().Menu;
const MenuItem = bridge().MenuItem;
const InteropServiceHelper = require("../InteropServiceHelper.js");
const { shim } = require('lib/shim');
class SideBarComponent extends React.Component {
@@ -178,6 +179,35 @@ class SideBarComponent extends React.Component {
return style;
}
clearForceUpdateDuringSync() {
if (this.forceUpdateDuringSyncIID_) {
clearInterval(this.forceUpdateDuringSyncIID_);
this.forceUpdateDuringSyncIID_ = null;
}
}
componentDidUpdate(prevProps) {
if (shim.isLinux()) {
// For some reason, the UI seems to sleep in some Linux distro during
// sync. Cannot find the reason for it and cannot replicate, so here
// as a test force the update at regular intervals.
// https://github.com/laurent22/joplin/issues/312#issuecomment-429472193
if (!prevProps.syncStarted && this.props.syncStarted) {
this.clearForceUpdateDuringSync();
this.forceUpdateDuringSyncIID_ = setInterval(() => {
this.forceUpdate();
}, 2000);
}
if (prevProps.syncStarted && !this.props.syncStarted) this.clearForceUpdateDuringSync();
}
}
componentWillUnmount() {
this.clearForceUpdateDuringSync();
}
itemContextMenu(event) {
const itemId = event.target.getAttribute("data-id");
if (itemId === Folder.conflictFolderId()) return;
@@ -470,7 +500,13 @@ class SideBarComponent extends React.Component {
);
}
let decryptionReportText = '';
if (this.props.decryptionWorker && this.props.decryptionWorker.state !== 'idle' && this.props.decryptionWorker.itemCount) {
decryptionReportText = _('Decrypting items: %d/%d', this.props.decryptionWorker.itemIndex + 1, this.props.decryptionWorker.itemCount);
}
let lines = Synchronizer.reportToLines(this.props.syncReport);
if (decryptionReportText) lines.push(decryptionReportText);
const syncReportText = [];
for (let i = 0; i < lines.length; i++) {
syncReportText.push(
@@ -510,6 +546,7 @@ const mapStateToProps = state => {
locale: state.settings.locale,
theme: state.settings.theme,
collapsedFolderIds: state.collapsedFolderIds,
decryptionWorker: state.decryptionWorker,
};
};

View File

@@ -36,6 +36,22 @@
<script>
const contentElement = document.getElementById('content');
const ipc = {};
window.addEventListener('message', (event) => {
// Here we only deal with messages that are sent from the main Electro process to the webview.
if (!event.data || event.data.target !== 'webview') return;
const callName = event.data.name;
const callData = event.data.data;
if (!ipc[callName]) {
console.warn('Missing IPC function:', event.data);
} else {
ipc[callName](callData);
}
});
// ----------------------------------------------------------------------
// Handle dynamically loading HLJS when a code element is present
// ----------------------------------------------------------------------
@@ -119,7 +135,9 @@
setPercentScroll(percentScroll_);
}
ipcRenderer.on('setHtml', (event, html) => {
ipc.setHtml = (event) => {
const html = event.html;
updateBodyHeight();
contentElement.innerHTML = html;
@@ -158,10 +176,12 @@
}
}, 1);
}
});
}
let ignoreNextScrollEvent = false;
ipcRenderer.on('setPercentScroll', (event, percent) => {
ipc.setPercentScroll = (event) => {
const percent = event.percent;
if (checkScrollIID_) {
clearInterval(checkScrollIID_);
checkScrollIID_ = null;
@@ -169,7 +189,7 @@
ignoreNextScrollEvent = true;
setPercentScroll(percent);
});
}
let mark_ = null;
function setMarkers(keywords) {
@@ -184,7 +204,9 @@
}
let markLoaded_ = false;
ipcRenderer.on('setMarkers', (event, keywords) => {
ipc.setMarkers = (event) => {
const keywords = event.keywords;
if (!keywords.length && !markLoaded_) return;
if (!markLoaded_) {
@@ -199,7 +221,7 @@
} else {
setMarkers(keywords);
}
});
}
function maxScrollTop() {
return Math.max(0, contentElement.scrollHeight - contentElement.clientHeight);
@@ -210,6 +232,10 @@
document.getElementById('body').style.height = window.innerHeight + 'px';
}
const ipcProxySendToHost = (methodName, arg) => {
window.postMessage({ target: 'main', name: methodName, args: [ arg ] }, '*');
}
contentElement.addEventListener('scroll', function(e) {
if (ignoreNextScrollEvent) {
ignoreNextScrollEvent = false;
@@ -218,7 +244,8 @@
const m = maxScrollTop();
const percent = m ? contentElement.scrollTop / m : 0;
setPercentScroll(percent);
ipcRenderer.sendToHost('percentScroll', percent);
ipcProxySendToHost('percentScroll', percent);
});
document.addEventListener('contextmenu', function(event) {
@@ -228,7 +255,7 @@
if (element && !element.getAttribute('data-resource-id')) element = element.parentElement;
if (element && element.getAttribute('data-resource-id')) {
ipcRenderer.sendToHost('contextMenu', {
ipcProxySendToHost('contextMenu', {
type: element.getAttribute('src') ? 'image' : 'resource',
resourceId: element.getAttribute('data-resource-id'),
});
@@ -236,12 +263,12 @@
const selectedText = window.getSelection().toString();
if (selectedText) {
ipcRenderer.sendToHost('contextMenu', {
ipcProxySendToHost('contextMenu', {
type: 'text',
textToCopy: selectedText,
});
} else if (event.target.getAttribute('href')) {
ipcRenderer.sendToHost('contextMenu', {
ipcProxySendToHost('contextMenu', {
type: 'link',
textToCopy: event.target.getAttribute('href'),
});
@@ -249,6 +276,27 @@
}
});
document.addEventListener('click', function(event) {
const t = event.target;
// Prevent URLs added via <a> tags from being opened within the application itself
if (t && t.nodeName === 'A' && !t.hasAttribute('data-from-md')) {
event.preventDefault();
ipcProxySendToHost(t.getAttribute('href'));
return;
}
// IF this is an internal link, jump to the anchor directly
if (t && t.nodeName === 'A' && t.hasAttribute('data-from-md')) {
const href = t.getAttribute('href');
if (href.indexOf('#') === 0) {
event.preventDefault();
location.hash = href;
return;
}
}
});
// Disable drag and drop otherwise it's possible to drop a URL
// on it and it will open in the view as a website.
document.addEventListener('drop', function(e) {

View File

@@ -1,4 +1,36 @@
// Define here Electron objects that need to be accessed from the WebView
// https://github.com/electron/electron/blob/master/docs/tutorial/security.md#2-disable-nodejs-integration-for-remote-content
// In order to give access to the webview to certain functions of the main process, we need
// this bridge which listens from the main process and sends to the webview and the other
// way around. This is necessary after having enabled the "contextIsolation" option, which
// prevents the webview from accessing low-level methods in the main process.
window.ipcRenderer = require('electron').ipcRenderer;
const ipcRenderer = require('electron').ipcRenderer;
ipcRenderer.on('setHtml', (event, html) => {
window.postMessage({ target: 'webview', name: 'setHtml', data: { html: html } }, '*');
});
ipcRenderer.on('setPercentScroll', (event, percent) => {
window.postMessage({ target: 'webview', name: 'setPercentScroll', data: { percent: percent } }, '*');
});
ipcRenderer.on('setMarkers', (event, keywords) => {
window.postMessage({ target: 'webview', name: 'setMarkers', data: { keywords: keywords } }, '*');
});
window.addEventListener('message', (event) => {
// Here we only deal with messages that are sent from the webview to the main Electron process
if (!event.data || event.data.target !== 'main') return;
const callName = event.data.name;
const args = event.data.args;
if (args.length === 0) {
ipcRenderer.sendToHost(callName);
} else if (args.length === 1) {
ipcRenderer.sendToHost(callName, args[0]);
} else if (args.length === 2) {
ipcRenderer.sendToHost(callName, args[1]);
} else {
throw new Error('Unsupported number of args');
}
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
{
"name": "Joplin",
"version": "1.0.107",
"version": "1.0.113",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -40,18 +40,31 @@
"integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w=="
},
"acorn": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.2.tgz",
"integrity": "sha512-cJrKCNcr2kv8dlDnbw+JPUGjHZzo4myaxOLmpOX8a+rgX94YeTcTMv/LFJUSByRpc+i4GgVnnhLxvMu/2Y+rqw=="
"version": "5.7.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
"integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw=="
},
"acorn-globals": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.1.0.tgz",
"integrity": "sha512-KjZwU26uG3u6eZcfGbTULzFcsoz6pegNKtHPksZPOUsiKo5bUmiBPa38FuHZ/Eun+XYh/JCCkS9AS3Lu4McQOQ==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.0.tgz",
"integrity": "sha512-hMtHj3s5RnuhvHPowpBYvJVj3rAar82JiDQHvGs1zO0l10ocX/xEdBShNHTJaboucJUsScghp74pH3s7EnHHQw==",
"requires": {
"acorn": "^5.0.0"
"acorn": "^6.0.1",
"acorn-walk": "^6.0.1"
},
"dependencies": {
"acorn": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.2.tgz",
"integrity": "sha512-GXmKIvbrN3TV7aVqAzVFaMW8F8wzVX7voEBRO3bDA64+EX37YSayggRJP5Xig6HYHBkWKpFg9W5gg6orklubhg=="
}
}
},
"acorn-walk": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.0.tgz",
"integrity": "sha512-ugTb7Lq7u4GfWSqqpwE0bGyoBZNMTok/zDBXxfEG0QM50jNlGhIWjRC1pPN7bvV1anhF+bs+/gNcRw+o55Evbg=="
},
"ajv": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.0.tgz",
@@ -914,9 +927,9 @@
}
},
"browser-process-hrtime": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz",
"integrity": "sha1-Ql1opY00R/AqBKqJQYf86K+Le44="
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz",
"integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw=="
},
"buffer-from": {
"version": "1.1.0",
@@ -1783,6 +1796,11 @@
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"detect-indent": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz",
@@ -3519,6 +3537,18 @@
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz",
"integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8="
},
"http-errors": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.1.tgz",
"integrity": "sha512-jWEUgtZWGSMba9I1N3gc1HmvpBUaNC9vDdA46yScAdp+C5rdEuKWUBLWTQpW9FwSWSbYYs++b6SDCxf9UEJzfw==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.0",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
}
},
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
@@ -3909,9 +3939,9 @@
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
},
"joplin-turndown": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/joplin-turndown/-/joplin-turndown-4.0.8.tgz",
"integrity": "sha512-RPZJSZEplVPL3UiJNkaKsFAG8bCGofsKIiH24s8/4qcy1xYnEufvg++rHm7rxi/0VCtpSkRBlWHSs1/srJZvoA==",
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/joplin-turndown/-/joplin-turndown-4.0.9.tgz",
"integrity": "sha512-8MOxX4t5Ai22muHhXPMGNoKc/AB7gSo0eUvNh6dyd6b3vcSiMIRZE8UHpMjS9ruJQ+8e+8TtJXc0nfbexeHwrA==",
"requires": {
"jsdom": "^11.9.0"
}
@@ -4483,6 +4513,32 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"multiparty": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/multiparty/-/multiparty-4.2.1.tgz",
"integrity": "sha512-AvESCnNoQlZiOfP9R4mxN8M9csy2L16EIbWIkt3l4FuGti9kXBS8QVzlfyg4HEnarJhrzZilgNFlZtqmoiAIIA==",
"requires": {
"fd-slicer": "1.1.0",
"http-errors": "~1.7.0",
"safe-buffer": "5.1.2",
"uid-safe": "2.1.5"
},
"dependencies": {
"fd-slicer": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
"integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
"requires": {
"pend": "~1.2.0"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
"nan": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz",
@@ -4888,8 +4944,7 @@
"pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
"integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=",
"dev": true
"integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
},
"performance-now": {
"version": "2.1.0",
@@ -5059,6 +5114,11 @@
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.0.0.tgz",
"integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw=="
},
"random-bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
"integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs="
},
"randomatic": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.0.0.tgz",
@@ -5539,6 +5599,11 @@
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
},
"setprototypeof": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
},
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
@@ -6538,6 +6603,11 @@
}
}
},
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
},
"stealthy-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
@@ -6816,6 +6886,11 @@
"repeat-string": "^1.6.1"
}
},
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
},
"tough-cookie": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz",
@@ -6898,6 +6973,19 @@
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.5.tgz",
"integrity": "sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg=="
},
"uid-safe": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
"requires": {
"random-bytes": "~1.0.0"
}
},
"unidecode": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/unidecode/-/unidecode-0.1.8.tgz",
"integrity": "sha1-77swFTi8RSRqmsjFWdcvAVMFBT4="
},
"union-value": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
@@ -7182,9 +7270,9 @@
"integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng=="
},
"whatwg-mimetype": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz",
"integrity": "sha512-FKxhYLytBQiUKjkYteN71fAUA3g6KpNXoho1isLiLSB3N1G4F35Q5vUxWfKFhBwi5IWF27VE6WxhrnnC+m0Mew=="
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.2.0.tgz",
"integrity": "sha512-5YSO1nMd5D1hY3WzAQV3PzZL83W3YeyR1yW9PcH26Weh1t+Vzh9B6XkDh7aXm83HBZ4nSMvkjvN2H2ySWIvBgw=="
},
"whatwg-url": {
"version": "6.5.0",

View File

@@ -1,6 +1,6 @@
{
"name": "Joplin",
"version": "1.0.107",
"version": "1.0.113",
"description": "Joplin for Desktop",
"main": "main.js",
"scripts": {
@@ -92,7 +92,7 @@
"highlight.js": "^9.12.0",
"html-entities": "^1.2.1",
"image-type": "^3.0.0",
"joplin-turndown": "^4.0.8",
"joplin-turndown": "^4.0.9",
"joplin-turndown-plugin-gfm": "^1.0.7",
"jssha": "^2.3.1",
"katex": "^0.10.0-rc.1",
@@ -105,6 +105,7 @@
"mermaid": "^8.0.0-rc.8",
"mime": "^2.3.1",
"moment": "^2.22.2",
"multiparty": "^4.2.1",
"node-fetch": "^1.7.3",
"node-notifier": "^5.2.1",
"promise": "^8.0.1",
@@ -126,6 +127,7 @@
"syswide-cas": "^5.1.0",
"tar": "^4.4.4",
"tcp-port-used": "^0.1.2",
"unidecode": "^0.1.8",
"url-parse": "^1.4.1",
"uuid": "^3.2.1",
"valid-url": "^1.0.9",

View File

@@ -1,7 +1,9 @@
const Setting = require('lib/models/Setting.js');
const zoomRatio = Setting.value('style.zoom') / 100;
const globalStyle = {
fontSize: 12 * Setting.value('style.zoom') / 100,
fontSize: Math.round(12 * zoomRatio),
fontFamily: 'sans-serif',
margin: 15, // No text and no interactive component should be within this margin
itemMarginTop: 10,
@@ -46,7 +48,7 @@ globalStyle.htmlColor ='black'; // Note: CSS in WebView component only supports
globalStyle.htmlBackgroundColor ='white';
globalStyle.htmlDividerColor = 'rgb(150,150,150)';
globalStyle.htmlLinkColor ='blue';
globalStyle.htmlLineHeight ='20px';
globalStyle.htmlLineHeight = Math.round(20 * zoomRatio) + 'px';
globalStyle.marginRight = globalStyle.margin;
globalStyle.marginLeft = globalStyle.margin;

0
ElectronClient/run-prod.sh Normal file → Executable file
View File

70
Joplin_install_and_update.sh Executable file
View File

@@ -0,0 +1,70 @@
#!/bin/bash
set -e
# Title
echo " _ _ _ _ _ _ "
echo " | | (_) (_) | | | | | "
echo " | | ___ _ __ _ _ __ _ _ __ ___| |_ __ _| | | ___ _ __ "
echo " _ | |/ _ \\\| '_ \| | '_ \\ | | '_ \\\/ __| __/ _\` | | |/ _ \ '__|"
echo " | |__| | (_) | |_) | | | | | | | | | \__ \ || (_| | | | __/ | "
echo " \____/ \___/| .__/|_|_| |_| |_|_| |_|___/\__\__,_|_|_|\___|_| "
echo " | | "
echo " |_| "
echo ""
#-----------------------------------------------------
# Download Joplin
#-----------------------------------------------------
# Get the latest version to download
version=$(curl --silent "https://api.github.com/repos/laurent22/joplin/releases/latest" | grep -Po '"tag_name": "v\K.*?(?=")')
# Check if it's in the latest version
touch VERSION
if [[ $(< ~/.joplin/VERSION) != "$version" ]]; then
# Delete previous version
rm -f ~/.joplin/*.AppImage ~/.local/share/applications/joplin.desktop ~/.joplin/VERSION
# Creates the folder where the binary will be stored
mkdir -p ~/.joplin/
# Download the latest version
wget -O ~/.joplin/Joplin.AppImage https://github.com/laurent22/joplin/releases/download/v$version/Joplin-$version-x86_64.AppImage
# Gives execution privileges
chmod +x ~/.joplin/Joplin.AppImage
#-----------------------------------------------------
# Icon
#-----------------------------------------------------
# Download icon
wget -O ~/.joplin/Icon512.png https://joplin.cozic.net/images/Icon512.png
# Detect desktop environment
if [ "$XDG_CURRENT_DESKTOP" = "" ]
then
desktop=$(echo "$XDG_DATA_DIRS" | sed 's/.*\(xfce\|kde\|gnome\).*/\1/')
else
desktop=$XDG_CURRENT_DESKTOP
fi
desktop=${desktop,,} # convert to lower case
# Create icon for Gnome
if [[ $desktop =~ .*gnome.* ]]
then
echo -e "[Desktop Entry]\nEncoding=UTF-8\nName=Joplin\nExec=/home/$USER/.joplin/Joplin-$version-x86_64.AppImage\nIcon=/home/$USER/.joplin/Icon512.png\nType=Application\nCategories=Application;" >> ~/.local/share/applications/joplin.desktop
fi
#-----------------------------------------------------
# Finish
#-----------------------------------------------------
# Informs the user that it has been installed and cleans variables
echo 'Joplin installed in the version' $version
# Add version
echo $version > ~/.joplin/VERSION
else
echo 'You are now in the latest version.'
fi
unset version

View File

@@ -2,6 +2,20 @@
[![Donate](https://joplin.cozic.net/images/badges/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) [![Become a patron](https://joplin.cozic.net/images/badges/Patreon-Badge.svg)](https://www.patreon.com/joplin) [![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 and Hacktobertfest 2018 :jack_o_lantern:**
The [Hacktobertfest event](https://hacktoberfest.digitalocean.com/) has started - it allows you to contribute to Joplin and, at the end of the month, after having done 5 PR, you'll earn a limited edition T-shirt.
To participate, go on [https://hacktoberfest.digitalocean.com/ ](https://hacktoberfest.digitalocean.com/) log in (with you github account) and you are ready to get in.
Next, go dive into the Joplin issues list labelled ["Hacktoberfest"](https://github.com/laurent22/joplin/labels/Hacktoberfest%20%3Ajack_o_lantern%3A)
We hope you will enjoy that event by contributing to the project which is a nice moment of sharing good vibe :jack_o_lantern: :tada:
_PS: the 5 Pull Request don't have to be done __only__ on Joplin project, those can be done on any FOSS projects._
* * *
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.
@@ -20,23 +34,23 @@ Three types of applications are available: for the **desktop** (Windows, macOS a
Operating System | Download | Alternative
-----------------|--------|-------------------
Windows (32 and 64-bit) | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.105/Joplin-Setup-1.0.105.exe'><img alt='Get it on Windows' height="40px" src='https://joplin.cozic.net/images/BadgeWindows.png'/></a> | or Get the <a href='https://github.com/laurent22/joplin/releases/download/v1.0.105/JoplinPortable.exe'>Portable version</a><br>(to run from a USB key, etc.)
macOS | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.105/Joplin-1.0.105.dmg'><img alt='Get it on macOS' height="40px" src='https://joplin.cozic.net/images/BadgeMacOS.png'/></a> |
Linux | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.105/Joplin-1.0.105-x86_64.AppImage'><img alt='Get it on Linux' height="40px" src='https://joplin.cozic.net/images/BadgeLinux.png'/></a> | An Arch Linux package<br>[is also available](#terminal-application).
Windows (32 and 64-bit) | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.111/Joplin-Setup-1.0.111.exe'><img alt='Get it on Windows' height="40px" src='https://joplin.cozic.net/images/BadgeWindows.png'/></a> | or Get the <a href='https://github.com/laurent22/joplin/releases/download/v1.0.111/JoplinPortable.exe'>Portable version</a><br>(to run from a USB key, etc.)
macOS | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.111/Joplin-1.0.111.dmg'><img alt='Get it on macOS' height="40px" src='https://joplin.cozic.net/images/BadgeMacOS.png'/></a> |
Linux | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.111/Joplin-1.0.111-x86_64.AppImage'><img alt='Get it on Linux' height="40px" src='https://joplin.cozic.net/images/BadgeLinux.png'/></a> | An Arch Linux package<br>[is also available](#terminal-application).
The [portable application](https://en.wikipedia.org/wiki/Portable_application) allows installing the software on a portable device such as a USB key. Simply copy the file JoplinPortable.exe in any directory on that USB key ; the application will then create a directory called "JoplinProfile" next to the executable file.
### Install and Update Ubuntu/Debian (Gnome Shell)
On Linux, if it works with your distribution (it has been tested on Ubuntu, Fedora, Gnome and Mint), the recommended way is to use this script as it will handle the desktop icon too:
``` sh
wget -O - https://raw.githubusercontent.com/laurent22/joplin/master/install_ubuntu.sh | bash
wget -O - https://raw.githubusercontent.com/laurent22/joplin/master/Joplin_install_and_update.sh | bash
```
## Mobile applications
Operating System | Download | Alt. Download
-----------------|----------|----------------
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://joplin.cozic.net/images/BadgeAndroid.png'/></a> | or [Download APK File](https://github.com/laurent22/joplin-android/releases/download/android-v1.0.132/joplin-v1.0.132.apk)
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://joplin.cozic.net/images/BadgeAndroid.png'/></a> | or [Download APK File](https://github.com/laurent22/joplin-android/releases/download/android-v1.0.174/joplin-v1.0.174.apk)
iOS | <a href='https://itunes.apple.com/us/app/joplin/id1315599797'><img alt='Get it on the App Store' height="40px" src='https://joplin.cozic.net/images/BadgeIOS.png'/></a> | -
## Terminal application
@@ -71,6 +85,7 @@ The Web Clipper is a browser extension that allows you to save web pages and scr
- [How to enable end-to-end encryption](https://github.com/laurent22/joplin/blob/master/readme/e2ee.md)
- [End-to-end encryption spec](https://github.com/laurent22/joplin/blob/master/readme/spec.md)
- [How to enable debug mode](https://github.com/laurent22/joplin/blob/master/readme/debugging.md)
- [API documentation](https://github.com/laurent22/joplin/blob/master/readme/api.md)
- [FAQ](https://github.com/laurent22/joplin/blob/master/readme/faq.md)
- About
@@ -158,7 +173,7 @@ If synchronisation does not work, please consult the logs in the app profile dir
## Dropbox synchronisation
When syncing with Dropbox, Joplin creates a sub-directory in Dropbox, in /Apps/Joplin and read/write the notes and notebooks from it. The application does not have access to anything outside this directory.
When syncing with Dropbox, Joplin creates a sub-directory in Dropbox, in `/Apps/Joplin` and read/write the notes and notebooks from it. The application does not have access to anything outside this directory.
On the **desktop application** or **mobile application**, select "Dropbox" as the synchronisation target in the config screen (it is selected by default). Then, to initiate the synchronisation process, click on the "Synchronise" button in the sidebar and follow the instructions.
@@ -177,6 +192,7 @@ WebDAV-compatible services that are known to work with Joplin:
- [Fastmail](https://www.fastmail.com/)
- [HiDrive](https://www.strato.fr/stockage-en-ligne/) from Strato. [Setup help](https://github.com/laurent22/joplin/issues/309)
- [Nginx WebDAV Module](https://nginx.org/en/docs/http/ngx_http_dav_module.html)
- [NextCloud](https://nextcloud.com/)
- [OwnCloud](https://owncloud.org/)
- [Seafile](https://www.seafile.com/)
- [Stack](https://www.transip.nl/stack/)
@@ -244,7 +260,7 @@ Since getting the ID of a note is not straightforward, each app provides a way t
## Math notation
Math expressions can be added using the [Katex notation](https://khan.github.io/KaTeX/). To add an inline equation, wrap the expression in `$EXPRESSION$`, eg. `$\sqrt{3x-1}+(1+x)^2$`. To create an expression block, wrap it as follow:
Math expressions can be added using the [KaTeX notation](https://khan.github.io/KaTeX/). To add an inline equation, wrap the expression in `$EXPRESSION$`, eg. `$\sqrt{3x-1}+(1+x)^2$`. To create an expression block, wrap it as follow:
$$
EXPRESSION
@@ -274,7 +290,9 @@ The checkboxes can then be ticked in the mobile and desktop applications.
## HTML support
Only the `<br>` tag is supported - it can be used to force a new line, which is convenient to insert new lines inside table cells. For security reasons, other HTML tags are not supported.
It is generally recommended to enter the notes as Markdown as it makes the notes easier to edit. However for cases where certain features aren't supported (such as strikethrough or to highlight text), you can also use HTML code directly. For example this would be a valid note:
This is <s>strikethrough text</s> mixed with regular **Markdown**.
# Donations
@@ -311,29 +329,29 @@ Current translations:
<!-- LOCALE-TABLE-AUTO-GENERATED -->
&nbsp; | Language | Po File | Last translator | Percent done
---|---|---|---|---
![](https://joplin.cozic.net/images/flags/es/basque_country.png) | Basque | [eu](https://github.com/laurent22/joplin/blob/master/CliClient/locales/eu.po) | juan.abasolo@ehu.eus | 64%
![](https://joplin.cozic.net/images/flags/es/catalonia.png) | Catalan | [ca](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ca.po) | jmontane, 2018 | 91%
![](https://joplin.cozic.net/images/flags/country-4x3/hr.png) | Croatian | [hr_HR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/hr_HR.po) | Hrvoje Mandić (trbuhom@net.hr) | 52%
![](https://joplin.cozic.net/images/flags/country-4x3/cz.png) | Czech | [cs_CZ](https://github.com/laurent22/joplin/blob/master/CliClient/locales/cs_CZ.po) | Lukas Helebrandt (lukas@aiya.cz) | 81%
![](https://joplin.cozic.net/images/flags/country-4x3/dk.png) | Dansk | [da_DK](https://github.com/laurent22/joplin/blob/master/CliClient/locales/da_DK.po) | Morten Juhl-Johansen Zölde-Fejér (mjjzf@syntaktisk. | 83%
![](https://joplin.cozic.net/images/flags/country-4x3/de.png) | Deutsch | [de_DE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/de_DE.po) | Michael Sonntag (ms@editorei.de) | 98%
![](https://joplin.cozic.net/images/flags/es/basque_country.png) | Basque | [eu](https://github.com/laurent22/joplin/blob/master/CliClient/locales/eu.po) | juan.abasolo@ehu.eus | 62%
![](https://joplin.cozic.net/images/flags/es/catalonia.png) | Catalan | [ca](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ca.po) | jmontane, 2018 | 88%
![](https://joplin.cozic.net/images/flags/country-4x3/hr.png) | Croatian | [hr_HR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/hr_HR.po) | Hrvoje Mandić (trbuhom@net.hr) | 50%
![](https://joplin.cozic.net/images/flags/country-4x3/cz.png) | Czech | [cs_CZ](https://github.com/laurent22/joplin/blob/master/CliClient/locales/cs_CZ.po) | Lukas Helebrandt (lukas@aiya.cz) | 78%
![](https://joplin.cozic.net/images/flags/country-4x3/dk.png) | Dansk | [da_DK](https://github.com/laurent22/joplin/blob/master/CliClient/locales/da_DK.po) | Morten Juhl-Johansen Zölde-Fejér (mjjzf@syntaktisk. | 80%
![](https://joplin.cozic.net/images/flags/country-4x3/de.png) | Deutsch | [de_DE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/de_DE.po) | Michael Sonntag (ms@editorei.de) | 100%
![](https://joplin.cozic.net/images/flags/country-4x3/gb.png) | English | [en_GB](https://github.com/laurent22/joplin/blob/master/CliClient/locales/en_GB.po) | | 100%
![](https://joplin.cozic.net/images/flags/country-4x3/es.png) | Español | [es_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/es_ES.po) | Fernando Martín (f@mrtn.es) | 98%
![](https://joplin.cozic.net/images/flags/country-4x3/fr.png) | Français | [fr_FR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/fr_FR.po) | Laurent Cozic | 98%
![](https://joplin.cozic.net/images/flags/es/galicia.png) | Galician | [gl_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/gl_ES.po) | Marcos Lans (marcoslansgarza@gmail.com) | 81%
![](https://joplin.cozic.net/images/flags/country-4x3/it.png) | Italiano | [it_IT](https://github.com/laurent22/joplin/blob/master/CliClient/locales/it_IT.po) | | 97%
![](https://joplin.cozic.net/images/flags/country-4x3/nl.png) | Nederlands | [nl_NL](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nl_NL.po) | Heimen Stoffels (vistausss@outlook.com) | 98%
![](https://joplin.cozic.net/images/flags/country-4x3/be.png) | Nederlands | [nl_BE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nl_BE.po) | | 64%
![](https://joplin.cozic.net/images/flags/country-4x3/no.png) | Norwegian | [no](https://github.com/laurent22/joplin/blob/master/CliClient/locales/no.po) | | 87%
![](https://joplin.cozic.net/images/flags/country-4x3/br.png) | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/pt_BR.po) | Renato Nunes Bastos (rnbastos@gmail.com) | 98%
![](https://joplin.cozic.net/images/flags/country-4x3/ro.png) | Română | [ro](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ro.po) | | 64%
![](https://joplin.cozic.net/images/flags/country-4x3/si.png) | Slovenian | [sl_SI](https://github.com/laurent22/joplin/blob/master/CliClient/locales/sl_SI.po) | | 80%
![](https://joplin.cozic.net/images/flags/country-4x3/se.png) | Svenska | [sv](https://github.com/laurent22/joplin/blob/master/CliClient/locales/sv.po) | Jonatan Nyberg (jonatan@autistici.org) | 97%
![](https://joplin.cozic.net/images/flags/country-4x3/ru.png) | Русский | [ru_RU](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ru_RU.po) | Artyom Karlov (artyom.karlov@gmail.com) | 80%
![](https://joplin.cozic.net/images/flags/country-4x3/cn.png) | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_CN.po) | | 98%
![](https://joplin.cozic.net/images/flags/country-4x3/tw.png) | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_TW.po) | penguinsam (samliu@gmail.com) | 98%
![](https://joplin.cozic.net/images/flags/country-4x3/jp.png) | 日本語 | [ja_JP](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ja_JP.po) | | 52%
![](https://joplin.cozic.net/images/flags/country-4x3/kr.png) | 한국말 | [ko](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ko.po) | | 98%
![](https://joplin.cozic.net/images/flags/country-4x3/es.png) | Español | [es_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/es_ES.po) | Fernando Martín (f@mrtn.es) | 95%
![](https://joplin.cozic.net/images/flags/country-4x3/fr.png) | Français | [fr_FR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/fr_FR.po) | Laurent Cozic | 100%
![](https://joplin.cozic.net/images/flags/es/galicia.png) | Galician | [gl_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/gl_ES.po) | Marcos Lans (marcoslansgarza@gmail.com) | 79%
![](https://joplin.cozic.net/images/flags/country-4x3/it.png) | Italiano | [it_IT](https://github.com/laurent22/joplin/blob/master/CliClient/locales/it_IT.po) | | 93%
![](https://joplin.cozic.net/images/flags/country-4x3/nl.png) | Nederlands | [nl_NL](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nl_NL.po) | Heimen Stoffels (vistausss@outlook.com) | 95%
![](https://joplin.cozic.net/images/flags/country-4x3/be.png) | Nederlands | [nl_BE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nl_BE.po) | | 62%
![](https://joplin.cozic.net/images/flags/country-4x3/no.png) | Norwegian | [no](https://github.com/laurent22/joplin/blob/master/CliClient/locales/no.po) | | 84%
![](https://joplin.cozic.net/images/flags/country-4x3/br.png) | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/pt_BR.po) | Renato Nunes Bastos (rnbastos@gmail.com) | 95%
![](https://joplin.cozic.net/images/flags/country-4x3/ro.png) | Română | [ro](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ro.po) | | 62%
![](https://joplin.cozic.net/images/flags/country-4x3/si.png) | Slovenian | [sl_SI](https://github.com/laurent22/joplin/blob/master/CliClient/locales/sl_SI.po) | | 78%
![](https://joplin.cozic.net/images/flags/country-4x3/se.png) | Svenska | [sv](https://github.com/laurent22/joplin/blob/master/CliClient/locales/sv.po) | Jonatan Nyberg (jonatan@autistici.org) | 94%
![](https://joplin.cozic.net/images/flags/country-4x3/ru.png) | Русский | [ru_RU](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ru_RU.po) | Artyom Karlov (artyom.karlov@gmail.com) | 78%
![](https://joplin.cozic.net/images/flags/country-4x3/cn.png) | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_CN.po) | | 94%
![](https://joplin.cozic.net/images/flags/country-4x3/tw.png) | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_TW.po) | penguinsam (samliu@gmail.com) | 95%
![](https://joplin.cozic.net/images/flags/country-4x3/jp.png) | 日本語 | [ja_JP](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ja_JP.po) | AWASHIRO Ikuya (ikunya@gmail.com) | 95%
![](https://joplin.cozic.net/images/flags/country-4x3/kr.png) | 한국말 | [ko](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ko.po) | | 95%
<!-- LOCALE-TABLE-AUTO-GENERATED -->
# Known bugs

View File

@@ -1,3 +1,3 @@
{
"presets": ["react-native"]
"presets": ["module:metro-react-native-babel-preset"]
}

49
ReactNativeClient/App.js Normal file
View File

@@ -0,0 +1,49 @@
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
* @flow
*/
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';
const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
android:
'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
});
type Props = {};
export default class App extends Component<Props> {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to React Native!</Text>
<Text style={styles.instructions}>To get started, edit App.js</Text>
<Text style={styles.instructions}>{instructions}</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,
},
});

View File

@@ -83,15 +83,15 @@ def enableSeparateBuildPerCPUArchitecture = false
def enableProguardInReleaseBuilds = false
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
applicationId "net.cozic.joplin"
minSdkVersion 16
targetSdkVersion 26
versionCode 2097310
versionName "1.0.132"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 2097410
versionName "1.0.174"
ndk {
abiFilters "armeabi-v7a", "x86"
}
@@ -137,6 +137,7 @@ android {
}
dependencies {
compile project(':react-native-camera')
compile project(':react-native-file-viewer')
compile project(':react-native-securerandom')
compile project(':react-native-push-notification')
@@ -144,15 +145,33 @@ dependencies {
compile project(':react-native-image-picker')
compile project(':react-native-vector-icons')
compile project(':react-native-fs')
compile fileTree(dir: "libs", include: ["*.jar"])
compile "com.android.support:appcompat-v7:23.0.1"
compile "com.facebook.react:react-native:+" // From node_modules
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
implementation "com.facebook.react:react-native:+" // From node_modules
compile project(':react-native-sqlite-storage')
compile project(':react-native-fetch-blob')
compile project(':rn-fetch-blob')
compile project(':react-native-document-picker')
compile project(':react-native-image-resizer')
compile project(':react-native-share-extension')
compile "com.facebook.react:react-native:+"
// To fix the error below, which happened after adding react-native-camera.
// Doesn't make any sense since rn-camera neither defines v26 nor 27 but
// v25.0.2 in build.gradle, but anyway now it works ¯\_(ツ)_/¯
// --------------------------------------------------------------------------------------
// Fatal error
// { Error: Command failed: ./gradlew assembleRelease -PbuildDir=build --console plain
//
// FAILURE: Build failed with an exception.
//
// * What went wrong:
// Execution failed for task ':app:preReleaseBuild'.
// > Android dependency 'com.android.support:support-v4' has different version for the compile (26.1.0) and runtime (27.1.1) classpath. You should manually set the same version via DependencyResolution
// --------------------------------------------------------------------------------------
// https://github.com/react-native-community/react-native-camera/issues/1532#issuecomment-386434771
compile ("com.android.support:support-v4:26.0.1") {
force = true //<-- force dependency resolution to 26.0.1 in my case
}
}
// Run this once to be able to run the application with BUCK

View File

@@ -15,56 +15,3 @@
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Disabling obfuscation is useful if you collect stack traces from production crashes
# (unless you are using a system that supports de-obfuscate the stack traces).
-dontobfuscate
# React Native
# Keep our interfaces so they can be used by other ProGuard rules.
# See http://sourceforge.net/p/proguard/bugs/466/
-keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip
-keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters
-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
# Do not strip any method/class that is annotated with @DoNotStrip
-keep @com.facebook.proguard.annotations.DoNotStrip class *
-keep @com.facebook.common.internal.DoNotStrip class *
-keepclassmembers class * {
@com.facebook.proguard.annotations.DoNotStrip *;
@com.facebook.common.internal.DoNotStrip *;
}
-keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * {
void set*(***);
*** get*();
}
-keep class * extends com.facebook.react.bridge.JavaScriptModule { *; }
-keep class * extends com.facebook.react.bridge.NativeModule { *; }
-keepclassmembers,includedescriptorclasses class * { native <methods>; }
-keepclassmembers class * { @com.facebook.react.uimanager.UIProp <fields>; }
-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp <methods>; }
-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup <methods>; }
-dontwarn com.facebook.react.**
# TextLayoutBuilder uses a non-public Android constructor within StaticLayout.
# See libs/proxy/src/main/java/com/facebook/fbui/textlayoutbuilder/proxy for details.
-dontwarn android.text.StaticLayout
# okhttp
-keepattributes Signature
-keepattributes *Annotation*
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**
# okio
-keep class sun.misc.Unsafe { *; }
-dontwarn java.nio.file.*
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-dontwarn okio.**

View File

@@ -11,6 +11,11 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove"/>
<!-- Make these features optional to enable Chromebooks -->
<!-- https://github.com/laurent22/joplin/issues/37 -->
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<!-- ==================================== -->
<!-- START react-native-push-notification -->
<!-- ==================================== -->

View File

@@ -3,6 +3,7 @@ package net.cozic.joplin;
import android.app.Application;
import com.facebook.react.ReactApplication;
import org.reactnative.camera.RNCameraPackage;
import com.vinzscam.reactnativefileviewer.RNFileViewerPackage;
import net.rhogan.rnsecurerandom.RNSecureRandomPackage;
import com.dieam.reactnativepushnotification.ReactNativePushNotificationPackage;
@@ -37,6 +38,7 @@ public class MainApplication extends Application implements ReactApplication {
return Arrays.<ReactPackage>asList(
new ImageResizerPackage(),
new MainReactPackage(),
new RNCameraPackage(),
new RNFileViewerPackage(),
new RNSecureRandomPackage(),
new ReactNativePushNotificationPackage(),

View File

@@ -1,11 +1,19 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext {
buildToolsVersion = "27.0.3"
minSdkVersion = 16
compileSdkVersion = 27
targetSdkVersion = 26
supportLibVersion = "27.1.1"
}
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.3'
classpath 'com.android.tools.build:gradle:3.1.4'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -22,6 +30,25 @@ allprojects {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
jcenter()
jcenter() // Was added by me - still needed?
google()
}
}
subprojects {
afterEvaluate {project ->
if (project.hasProperty("android")) {
android {
compileSdkVersion 26
buildToolsVersion "27.0.3"
}
}
}
}
task wrapper(type: Wrapper) {
gradleVersion = '4.4'
distributionUrl = distributionUrl.replace("bin", "all")
}

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