Compare commits

...

251 commits
main ... 6.0.17

Author SHA1 Message Date
Abe Jellinek
47610d0faa Scaffold: Autocomplete translator IDs in setTranslator() calls 2022-11-01 16:38:01 -04:00
Abe Jellinek
42e042c6c6 Scaffold: Fix copying JSON test data
Previously only copied '[object Object]'.
2022-11-01 16:05:54 -04:00
Dan Stillman
b9a5fd9f78 Fix error copying from note if note Quick Copy format is "HTML"
In this case, Note Markdown wouldn't be preloaded, but it's always used
for copying from the note editor, and since the note editor uses
`noWait`, this would result in "Code promise is not resolved in noWait
mode".
2022-11-01 03:57:50 -04:00
Dan Stillman
cbb524d257 Fix Quick Copy drag after reiniting translators
Zotero.Translators.reinit() didn't reinit Quick Copy, so if an export
translator was selected it wouldn't be preloaded again and dragging
would fail with "Code promise is not resolved in noWait mode".
2022-11-01 03:57:50 -04:00
Dan Stillman
93ea818bf2 Update version 2022-11-01 03:57:50 -04:00
Dan Stillman
d2243cc339 Update translators 2022-10-31 19:11:56 -04:00
Dan Stillman
f06267e4cd Update styles and translators 2022-10-31 17:40:00 -04:00
Dan Stillman
624101832e Update locales from Transifex 2022-10-31 17:34:24 -04:00
Martynas Bagdonas
6bc1551f7f Increase wait time for pdf-reader iframe initialization in tests
This is necessary until a proper initialization promise is added to pdf-reader.
2022-10-31 18:03:42 +02:00
Dan Stillman
a8a0cabcc4 Fix HiDPI icon for webpages 2022-10-31 05:43:17 -04:00
Dan Stillman
9d80c54d5d Update repotime 2022-10-31 05:18:06 -04:00
Adomas Venčkauskas
1c8554d527 Make colored tagging via keyboard layout independent
Report https://forums.zotero.org/discussion/100717/tag-number-shortcut-with-mac-raccourci-clavier-pour-marqueurs-avec-un-mac
2022-10-31 09:45:56 +02:00
Dan Stillman
5cea183ae3 Avoid infinite/excessive loops in Find Available PDF
https://forums.zotero.org/discussion/100634/potential-infinite-loop-when-trying-to-find-available-pdf

Closes #2883
2022-10-30 04:53:09 -04:00
Dan Stillman
92434dbb78 Update Mac Word automation settings instructions for Ventura 2022-10-30 04:53:08 -04:00
Dan Stillman
2c5a91682e Fix comment for Zotero.Attachments.getPDFResolvers() 2022-10-29 04:43:23 -04:00
Dan Stillman
3cc54eaf95 Retry only twice on error in Find Available PDF
Not three times
2022-10-29 04:43:23 -04:00
Dan Stillman
7f91a0c1a8 Fix hang on 500 from site during Find Available PDF
Find Available PDF has its own domain-based retry logic that predated
automatic 5xx retries in Zotero.HTTP, so disable the latter and fix some
bugs in the former.

Fixes #2700
2022-10-29 04:43:23 -04:00
Adomas Venčkauskas
77fdc8e40c Truncate long citation text in citation dialog. Closes #2879 2022-10-28 15:09:35 +03:00
Dan Stillman
c5a769285b Update translators 2022-10-26 06:55:49 -04:00
Dan Stillman
fe09afa0a6 Restore cell.csl for style tests
Removed in 4e1754b960
2022-10-26 05:14:47 -04:00
Dan Stillman
ab2bec936e Reset accidental change from 7fcc1d35 2022-10-26 04:38:19 -04:00
Adomas Venčkauskas
af66712c77 Update SingleFile. Closes #2880 2022-10-25 15:57:54 +03:00
Adomas Venčkauskas
7db16843b3 Add an integration test for citation merging 2022-10-25 14:55:19 +03:00
Adomas Venčkauskas
a4eafd0307 Fix wrong given name disambiguation after editing author for citeproc-js
Closes #2870
2022-10-25 14:55:16 +03:00
Adomas Venčkauskas
4e1754b960 Change integration test citation style to APA and some refactoring 2022-10-25 14:55:16 +03:00
Dan Stillman
238c7ce410 Update locales from Transifex and merge new English strings 2022-10-24 21:13:24 -04:00
Dan Stillman
8a15e5abe0 Don't localize "Markdown" in Quick Copy settings 2022-10-24 19:53:42 -04:00
Dan Stillman
159c112257 Add Zotero.Utilities.Internal.stringWithColon() for localized colons
Generally just a colon, but, e.g., French adds a space before it. We'll
want to replace uses of `punctuation.colon` with this.
2022-10-24 19:49:10 -04:00
Dan Stillman
70f9462913 Merge pull request #2676 from mrtcode/note-html-export-links
Add "Include Zotero Links" option for note export and QuickCopy
2022-10-22 16:47:18 -04:00
Dan Stillman
4b5e2eae1b Update translators 2022-10-22 16:42:24 -04:00
Dan Stillman
c4876fd5ae Adjust layout of Quick Copy note format options
Also:

- Hide format options until translators are loaded (since otherwise
  the checkboxes don't have labels and the layout is messed up)
- Remove unnecessary "zotero-" prefix from the new elements
2022-10-22 16:41:48 -04:00
Dan Stillman
7fcc1d35a4 Reset note Quick Copy pref on upgrade if set to Markdown + Rich Text
The default JSON pref value used spaces, but changing the drop-down
resulted in a version without spaces, so simply changing the default to
include the new link options wouldn't change all existing installs and
the "Include App Links" checkbox wouldn't show as enabled for Markdown.
This adds a pref migration step to reset the pref to the new default if
it's set to Markdown + Rich Text, regardless of whitespace differences.
2022-10-22 16:41:48 -04:00
Martynas Bagdonas
d0d3ed731d Add "Include Zotero Links" option for note export and QuickCopy
Fixes #2349
2022-10-22 16:41:48 -04:00
Dan Stillman
f2bfaeb7a9 Fix error message for invalid field in Item.setField()
The error message was showing a field instead of an item type.

https://forums.zotero.org/discussion/100598/error-1533410729-impossible-to-correct-date-field-in-items-added-with-zotero-conector-ver-6-0-15
2022-10-22 05:28:02 -04:00
Adomas Ven
2fa67bae64
Adds support for merging adjacent citations (#2875) 2022-10-21 03:17:35 -04:00
Dan Stillman
a2db01c633 Merge new English strings 2022-10-18 00:00:46 -04:00
Dan Stillman
2b9e9c174f Don't match all attachments with annotations for "not" search conditions
Fixes #2867
2022-10-17 23:59:29 -04:00
Dan Stillman
aa0ef7012c PDF reader: Hide "Rotate Page 180°" menu option for now
Rotating will speed up with Zotero 7, so maybe we can do without this.
2022-10-17 23:59:07 -04:00
Dan Stillman
73306ee5fb PDF reader: Add "Page" to rotate menu options for clarity 2022-10-17 23:58:52 -04:00
Martynas Bagdonas
d8d6054927 Add rotation options to Edit menu to rotate the current PDF page 2022-10-17 12:26:47 +03:00
Dan Stillman
410ebc288b Update locales from Transifex 2022-10-14 17:17:48 -04:00
Martynas Bagdonas
55095ba354 Reduce loaded tabs limit if the system has 8 GB or less memory 2022-10-14 11:13:02 +03:00
Dan Stillman
3ba6343668 Merge new English strings 2022-10-12 05:04:49 -04:00
Dan Stillman
d37aa37629 Update note-editor and pdf-reader submodules 2022-10-12 05:03:09 -04:00
Dan Stillman
3582405f55 Default to UTF-8 when indexing text file without detected charset
https://forums.zotero.org/discussion/94637/indexing-of-markdown-plain-text-attachments-with-md-extension
2022-10-12 05:01:10 -04:00
Martynas Bagdonas
78eb857e73 Add annotation update test to pdf-reader tests 2022-10-11 18:07:03 +03:00
Abe Jellinek
618e71b5f1 Fix last editpane tab being misaligned vertically (#2864) 2022-10-11 10:49:30 -04:00
Martynas Bagdonas
7bada8ce0c Tweak pdf-reader annotation creation test 2022-10-10 17:09:30 +03:00
Martynas Bagdonas
af436d34a8 Add pdf-reader annotation creation test 2022-10-10 16:53:42 +03:00
Dan Stillman
e45becce2a Tweak text in LABD auto-relinking dialogs (#2374, #2092) 2022-10-08 02:29:39 -04:00
Abe Jellinek
fb4f7b7932 Automatically relink attachments from LABD (#2374)
Fixes #2092
2022-10-08 02:29:31 -04:00
Dan Stillman
fd8aa4433c Change connector server response to "Zotero is running"
People are confused by "Zotero Connector Server is Available". I'm not
sure if this will be any better, but not mentioning the connector might
help -- that seems to be part of the problem.

We could display a longer message with a link to
https://www.zotero.org/support/kb/connector_zotero_unavailable, but this
is the /ping page that gets hit constantly, so it seems like we should
keep it short (though probably it doesn't make a difference).
2022-10-07 01:31:59 -04:00
Martynas Bagdonas
29dd0cf5dc Don't allow to rotate or delete pages for read-only PDF 2022-10-06 18:00:32 +03:00
Martynas Bagdonas
28329aa785 Fix annotation image saving to cache for read-only annotations 2022-10-06 17:32:51 +03:00
Martynas Bagdonas
c5d418fc16 Fix annotation saving regression 2022-10-06 16:43:58 +03:00
Martynas Bagdonas
e9c6c76e79 Properly handle errors when trying to open too large (>2GB-1) PDF file 2022-10-06 10:57:04 +03:00
Martynas Bagdonas
f13966894f Update pdf-reader submodule 2022-10-06 09:37:01 +03:00
Martynas Bagdonas
b7cc1d549b Fix pref observer handler unregistering 2022-10-05 14:09:39 +03:00
Dan Stillman
9a3f8dbc94 Update locales from Transifex 2022-10-05 02:45:05 -04:00
Martynas Bagdonas
b594ad0b0a Allow importing annotations in group libraries 2022-10-04 14:22:54 +03:00
Martynas Bagdonas
4dd1851140 Don't allow pdf-reader to set annotation author name
Fixes the issue when your own created annotation becomes grayish and indicates that it was modified by another user.
2022-10-04 13:59:16 +03:00
Martynas Bagdonas
006dd20d5b Re-render annotation images when rotating a page
Fixes #2824
2022-10-03 17:03:32 +03:00
Adomas Venčkauskas
55afe7c08d Fix item list row text not read when using non-contiguous kb selection
Closes #2830
2022-10-03 15:20:38 +03:00
Dan Stillman
9fadfac7ee Add 15 and 16 as note font size options in menu
https://forums.zotero.org/discussion/100079/notebook-fonts
2022-10-03 06:43:28 -04:00
Martynas Bagdonas
38ae3a2600 Remove PDF view rotation options 2022-10-03 13:14:37 +03:00
Dan Stillman
473dd2ed64 Update pdf-reader submodule 2022-09-30 17:30:57 -04:00
Dan Stillman
e3b1cbd4eb Update version 2022-09-30 17:30:29 -04:00
Dan Stillman
3b93147052 Update locales from Transifex 2022-09-30 16:52:13 -04:00
Dan Stillman
b9a1e93518 Merge new English strings 2022-09-30 16:09:56 -04:00
Dan Stillman
99084084d4 Remove default OpenURL gateway
https://www.worldcat.org/registry/gateway is no longer supported -- see
https://forums.zotero.org/discussion/comment/418373/#Comment_418373 for
details

If no resolver is selected, show an error on "Library Lookup" with a
button to open the prefs.
2022-09-30 16:08:12 -04:00
Dan Stillman
64047f181a Remove stray "aaa" in "Split Horizontally" in PDF window
From 12b1897478
2022-09-30 15:33:00 -04:00
Dan Stillman
12b1897478 Swap "Split Vertically" and "Split Horizontally" in PDF reader 2022-09-30 04:22:48 -04:00
Dan Stillman
8fc9ebe4ab Update locales from Transifex and merge new English strings 2022-09-30 02:59:52 -04:00
Dan Stillman
5162845179 Update version 2022-09-30 02:52:48 -04:00
Martynas Bagdonas
2569ad6506 Update note-editor submodule 2022-09-30 09:45:41 +03:00
Martynas Bagdonas
4fdf43ed92 Prevent error when creating a note from annotations that have ink 2022-09-30 09:44:27 +03:00
Martynas Bagdonas
9691fcafc2 Import external annotations before trying to add a note from annotations 2022-09-30 09:40:35 +03:00
Martynas Bagdonas
6aad0cbb9c
Add split view for PDF reader (#2832) 2022-09-29 13:25:58 +03:00
Dan Stillman
a7e6fc330c Update locales from Transifex 2022-09-28 05:09:52 -04:00
Dan Stillman
677499865a Update translators and CSL locales 2022-09-28 04:50:28 -04:00
Dan Stillman
0e60389ee4 Update note-editor submodule 2022-09-28 04:28:56 -04:00
Dan Stillman
2271913e49 Add Number as items list column option
https://forums.zotero.org/discussion/99844/add-columns-to-desktop-app-report-number
2022-09-22 01:48:15 -04:00
Dan Stillman
ff5e09201d Update package-lock.json 2022-09-22 01:16:11 -04:00
Dan Stillman
0b89b15f66 Update locales from Transifex and merge new English strings 2022-09-22 01:14:26 -04:00
Dan Stillman
f6e11981e3 Update pdf-reader and pdf-worker submodules 2022-09-22 01:14:26 -04:00
Dan Stillman
230fc7f2df Merge pull request #2826 from tnajdek/999-keyboard-nav-creators
Make creator buttons accessible via keyboard
2022-09-21 23:18:54 -04:00
Dan Stillman
f179f0a54f Additional tweaks for creator button keyboard access (#2826)
- Open creator type menu on ArrowUp as well
- Fix outline on Windows/Linux
- Don't show focus ring on hover, since we already show a background
2022-09-21 23:16:53 -04:00
Tom Najdek
ea09aac616 Make creator buttons accessible via keyboard #999 2022-09-21 23:16:53 -04:00
Dan Stillman
4a49ffec0a Update ESLint config 2022-09-21 22:25:34 -04:00
Martynas Bagdonas
0d09618865 Fix focusContextPane error when pressing tab in standalone PDF window
Fixes #2823
2022-09-15 11:59:03 +03:00
Dan Stillman
b96fcd41b1 Update translate submodule 2022-09-15 01:06:47 -04:00
Martynas Bagdonas
c13d17b5e6 Fix PDF reader sometimes showing UTC time and sometimes local 2022-09-13 16:38:17 +03:00
Martynas Bagdonas
eb255c5868 Convert Mendeley colors to equivalent Zotero colors on PDF open
Fixes #2820
2022-09-13 14:35:16 +03:00
Tom Najdek
88217492f9
Map annotation colors colors on import #2819 (#2822) 2022-09-12 19:57:24 -04:00
Martynas Bagdonas
64897455f4 Adjust PDF reader and context pane UI based on font size pref
Fixes #2809
2022-09-09 18:03:20 +03:00
Abe Jellinek
5015b0e4cc Hide "Find Available PDF" for feed items (#2807) 2022-09-07 10:07:43 -04:00
Martynas Bagdonas
a2d378448a Add context menu options to copy/save image from pdf-reader/note-editor
Fixes #2664
2022-09-06 16:35:14 +03:00
Dan Stillman
32b64b3cf1 Merge new English strings 2022-09-05 15:47:06 +01:00
Dan Stillman
66fccec6b4 Add "Create Note from Annotations" option to items list context menu
To allow creation of a standalone note with annotations from all the
selected top-level items and/or attachments. Annotations will be sorted
by the order of the items in the items list.

"Add Note from Annotations" remains when a single regular item or one or
more attachments under a single regular item are selected.
2022-09-05 10:37:07 -04:00
Dan Stillman
894309d061 Add collectionID option to EditorInstance.createNoteFromAnnotations()
For adding a standalone note to the current collection
2022-09-05 10:37:07 -04:00
Dan Stillman
b24c7577ec Rename Zotero.Items::keepParents() to keepTopLevel()
So that it works for more than two levels of items

Also fix a bug where the parent item could be returned more than once if
multiple child items were selected.
2022-09-05 10:37:07 -04:00
Dan Stillman
ca4ced1e9f Add Zotero.Item::topLevelItem and Zotero.Items.getTopLevel(items) 2022-09-05 10:37:07 -04:00
Martynas Bagdonas
6646be66d0 EditorInstance.createNoteFromAnnotations(): Handle multiple attachments 2022-09-05 10:37:07 -04:00
Dan Stillman
ef0811bd4c Merge new English strings 2022-09-02 12:07:13 +02:00
Martynas Bagdonas
f893206dfd Allow inserting images over context menu
Fixes #2759
2022-09-01 11:49:30 +03:00
Martynas Bagdonas
ae5de2c2f5 Update pdf-reader submodule 2022-08-31 15:45:56 +03:00
Martynas Bagdonas
a3b45a74e9 Add missing noteEditor.mathBlock string 2022-08-31 14:08:42 +03:00
Dan Stillman
e474ca524b Update pdf-reader submodule 2022-08-30 11:38:44 +02:00
Dan Stillman
bc0f6203e1 Update note-editor submodule 2022-08-30 11:18:07 +02:00
Dan Stillman
91ae576074 Fix HTTP.request() 5xx handling with errorDelayMax=0
This was causing delay-less infinite loops for OPTIONS requests to Box,
which are returning 502 errors.
2022-08-29 23:44:55 +02:00
Abe Jellinek
c255104ada duplicateAndConvertSelectedItem: Don't copy abstracts (#2799) 2022-08-29 16:27:26 -04:00
Dan Stillman
75f5d6aca1 Merge new English strings 2022-08-25 17:34:41 -04:00
Martynas Bagdonas
d156bef42a Update note-editor submodule and strings for math and table insertion 2022-08-25 14:19:38 +03:00
Dan Stillman
79246196ac Use items-list sort order for report from collection or saved search
https://forums.zotero.org/discussion/99284/exporting-a-report-sorted-by-first-date

Regression from HTML tree
2022-08-25 00:47:57 -04:00
Adomas Venčkauskas
262e1964ae Fix SingleFile snapshot saving stalling in Firefox Connector
Update SingleFile submodule
2022-08-24 11:48:02 +03:00
Dan Stillman
f4dfea6849 Don't repeatedly upload relations
Fixes #2772
2022-08-22 01:12:31 -04:00
Dan Stillman
999f78b0f5 Update translators 2022-08-21 23:30:39 -04:00
Dan Stillman
2e6321a4bf Update version 2022-08-21 23:29:48 -04:00
Tom Najdek
35a1bb20cb Make citavi annotations importer more resilient 2022-08-19 11:20:24 -04:00
Tom Najdek
8c80ea431e Fix importing Citavi annotations in some cases 2022-08-19 11:20:24 -04:00
Dan Stillman
3abd720957 Update pdf-reader submodule 2022-08-19 02:50:22 -04:00
Dan Stillman
8379540719 Include proper test for b373291c02 for #2771 2022-08-19 02:47:24 -04:00
Dan Stillman
b373291c02 Fixed filed items with annotations appearing in Unfiled Items
Fixes #2771

Regression from 20c6fe67
2022-08-19 00:15:55 -04:00
Abe Jellinek
3391f3bc99
Empty Trash: Visually remove deleted items, refresh icon (#2606)
There were a few problems causing the incorrect behavior:
1. Rows were being removed only if they had no non-deleted children, which
   wasn't the right check. We want to remove all rows with no *deleted*
   children.
2. Children of the removed rows weren't being removed with them.
3. We weren't invalidating the tree (which _removeRows() doesn't do).

Also:

* Erase trashed annotation after getAnnotations() test

Because ItemTree#notify() doesn't yet correctly handle refresh events on
parent items that are themselves children (three-level nesting: item ->
attachment -> annotation), this test was causing a failure in
itemTreeTest.js.
2022-08-18 21:14:34 -04:00
Martynas Bagdonas
6ca854a018 Fix navigation to annotation when opening an unloaded PDF reader tab
Regression from 142e3b09
2022-08-18 10:21:37 +03:00
Martynas Bagdonas
fa32eb6790 Fix regression for broken "Snow in Page"
Fixes #2764
Regression from 7ec54fd
2022-08-18 10:21:37 +03:00
Abe Jellinek
96c4427d6c Scaffold: Non-async detectWeb and async scrape by default
We don't usually want an async detectWeb, since HTTP requests should only be
used there in very exceptional cases. We do usually want an async scrape (and we
were already - mistakenly - awaiting it).
2022-08-17 16:34:19 -04:00
Dan Stillman
5a8b5a1829 Update localized string for pdf-reader 2022-08-17 03:46:27 -04:00
Dan Stillman
f368a53311 Fix and disable item-tree test for emptying trash
20c6fe6737 caused this to start failing, but only because the test was
testing something too specific. The change in that commit caused more
rows to be left behind when emptying the trash (for reasons I should
probably look into), but the trash wasn't being emptied properly before,
which #2606 should fix. This test should be restored as part of that PR.
2022-08-17 01:53:21 -04:00
Dan Stillman
49020b02a2 Update pdf-reader submodule 2022-08-17 00:03:34 -04:00
Dan Stillman
20c6fe6737 Match parent attachments for annotation tags
Expose annotation tags in tag selector and match parent attachments when
filtering/searching

This also fixes searching for annotation text or comments when using
Everything quick search.

This is temporary until we display annotations in the items list
directly.
2022-08-16 22:56:39 -04:00
Dan Stillman
80351a496b Update version 2022-08-16 22:56:39 -04:00
Dan Stillman
2d2e787111 Update translators 2022-08-15 08:32:57 -04:00
Dan Stillman
eaba3ae099 Update locales from Transifex 2022-08-15 08:32:17 -04:00
Dan Stillman
4746300d33 Update pdf-reader submodule 2022-08-15 08:24:17 -04:00
Dan Stillman
85b0c9dbad Fix importing of standalone attachments without URLs
And allow `importSnapshotFromFile()` without `parentItemID` or
`libraryID`, which is more consistent with other attachment functions

Follow-up to 318e4852e9

https://forums.zotero.org/discussion/comment/414802/#Comment_414802
2022-08-14 05:06:27 -04:00
Dan Stillman
318e4852e9 Fix importing of standalone attachments
Maybe has been broken for years?

https://forums.zotero.org/discussion/99020/warning-data-loss-when-exporting-collections
2022-08-13 16:23:00 -04:00
Dan Stillman
3dc3359cff Don't relate book sections to each other when creating from book
Fixes #2757
2022-08-13 05:45:23 -04:00
Dan Stillman
b505c630d3 Fix copying of standalone attachments between libraries
Regression in f684984b07 (maybe a race condition)
2022-08-13 02:00:41 -04:00
Dan Stillman
21b7baee35 Update translators 2022-08-12 00:38:19 -04:00
Dan Stillman
df59cee94a Update global schema for locales 2022-08-12 00:34:21 -04:00
Dan Stillman
d1275f9964 Update locales from Transifex and merge new English strings 2022-08-12 00:33:08 -04:00
Abe Jellinek
d4a7de2e8d
Add Book -> Book Section and Book Section -> Book to context menu (#2755) 2022-08-11 16:40:27 -04:00
Adomas Venčkauskas
864fd03c87 Prevent singlefile config from overwriting the Zotero.SingleFile namespace 2022-08-11 13:44:48 +03:00
Adomas Venčkauskas
d3cb7c86d9 Update SingleFile config for current version
They got updated upstream and the backwards-compat code
does not run for the way we execute singlefile

See https://github.com/gildas-lormeau/SingleFile/issues/1021
Addresses https://github.com/zotero/zotero-connectors/issues/398
2022-08-11 13:43:39 +03:00
Martynas Bagdonas
e4e3c72036 Fix PDF reader selector popup that was never opening 2022-08-11 12:18:32 +03:00
Martynas Bagdonas
0857cefb0c Fix regression preventing opening PDF reader window when the tab is open 2022-08-11 12:18:32 +03:00
Dan Stillman
9b7b4fe134 Improve test for event-title substitution
And add comments

Follow-up to 20d0f103f
2022-08-10 01:34:57 -04:00
Dan Stillman
a9c3fc7790 Reset Zotero.locale to 'en-US' after cite test 2022-08-10 01:01:48 -04:00
Dan Stillman
20d0f103fd Automatically substitute event-title for event in styles
Stopgap until styles are updated in citation-style-language/styles#6151
2022-08-10 01:01:48 -04:00
Abe Jellinek
20eca3781d Add test for 87455d 2022-08-08 11:48:33 -04:00
Abe Jellinek
87455d28c3 Item box: Fix fieldMode resetting after editor hidden twice
hideEditor() called switchCreatorMode() too early, setting the fieldMode
attribute on the soon-to-be-discarded textbox instead of the label
replacing it. Then, showing the editor a second time would carry over an
empty fieldMode attribute from the label to the new textbox. Hiding that
editor would update the creator in the item to fieldMode = 0 and trigger
a save.

Moving the switchCreatorMode() call does the trick, and the flex
settings changes still work fine when made there.
2022-08-08 10:56:56 -04:00
Abe Jellinek
2048319541
Advanced Search: Make sure items are loaded (#2743) 2022-08-06 15:00:33 -04:00
Dan Stillman
d03b0a716d Don't preselect Act for multiple sources in classic citation dialog
"Multiple Sources…" wasn't properly handled by 629892b0b8

https://forums.zotero.org/discussion/98877/act-instead-of-page
https://twitter.com/drezvera99/status/1554521940684984320
2022-08-06 02:29:26 -04:00
Abe Jellinek
bb2b37a73a Item box: Prevent tabbing to hidden fields
This one's probably been around for a while! Reproduce by creating an
item with a fieldMode = 1 creator, tabbing past the creator, and then
shift-tabbing back to it. Your cursor will end up in the invisible first
name field and further shift-tabs can't move it past.

Cherry-picked from fx102: 080ada78ee
2022-08-05 16:31:09 -04:00
Martynas Bagdonas
7ec54fdaa9 Select existing unloaded tab instead of opening a new one
Fixes #2740
2022-08-05 18:22:30 +03:00
Martynas Bagdonas
5a659b070d Allow to scroll tabs with mouse wheel
Fixes #2724
2022-07-29 13:06:33 +03:00
Abe Jellinek
ad96323881
Duplicates Merge: Preserve embedded annotations (#2728) 2022-07-29 05:06:44 -04:00
Adomas Venčkauskas
fc0f6157d0 Do not load-timeout on browser PDF download during content load
Closes #2726
2022-07-26 13:49:23 +03:00
Martynas Bagdonas
6733f2edca Freeze note schema version to 8
We can do it because it was only used to create a note from annotations.

No need to update schema version in Zotero client, unless using new
features when creating a note from annotations.
2022-07-25 18:04:00 +03:00
Martynas Bagdonas
e1997620a4 Fix regression that prevents showing the last selected tab on startup
Fixes #2723
2022-07-25 11:51:05 +03:00
Dan Stillman
a396771cbc Update version 2022-07-23 03:56:26 -04:00
Dan Stillman
0e0997078b Update global schema 2022-07-23 03:26:40 -04:00
Martynas Bagdonas
0785b3a086 Fix regression that prevents showing new notes in contextPane notes list 2022-07-21 13:27:32 +03:00
Adomas Venčkauskas
ba5cb7d5c6 Fixes some assets not being saved with SingleFile snapshots
Part of zotero/zotero-connectors#394

When saving an item (e.g. on arxiv) with a site translator, the
translator may add a custom snapshot attachment URL which does not match
the translation URL (the URL in the browser where translation was
initiated) in which case Zotero handles snapshot saving. Zotero loads
the page in a background browser. By default images are not loaded in
this browser. SingleFile due to CORS restrictions has to refetch all
resources that are not from the same domain or subdomain, but uses
pre-loaded resources (images) from the same domain. Before this change
any images that were from the same domain as the saved website were not
saved in the snapshot. This commit fixes that.
2022-07-20 14:28:56 +03:00
Adomas Venčkauskas
d822da4c06 Changes for updated SingleFile submodule 2022-07-20 14:28:56 +03:00
Adomas Venčkauskas
a297692487 Update SingleFile submodule 2022-07-20 14:28:56 +03:00
Dan Stillman
6921268dc0 Localize "Export PDF…" menu option
Fixes #2710
2022-07-20 01:04:00 -04:00
Dan Stillman
ba1906c054 Update version 2022-07-18 23:10:31 -04:00
Dan Stillman
b5be7cbd57 Update note-editor submodule 2022-07-18 19:47:23 -04:00
Dan Stillman
5c61815f36 Update translate submodule 2022-07-18 19:43:18 -04:00
Dan Stillman
b5ca5aed3a Update submodules 2022-07-18 19:14:24 -04:00
Dan Stillman
4b6598acc4 Update locales from Transifex 2022-07-18 08:57:41 -04:00
Dan Stillman
14a6ce000e Disable sort tests from 26a5e2900a for now
Until I can figure out the race-condition breakage they trigger later in
the file
2022-07-18 08:56:57 -04:00
Dan Stillman
9d0cd005f8 Increase Mendeley API timeout to 60 seconds
To see if we can fix the 30-second timeout here:

https://forums.zotero.org/discussion/comment/412750/#Comment_412750
2022-07-18 08:29:45 -04:00
Dan Stillman
26db75971e Update pdf-reader submodule 2022-07-18 03:51:09 -04:00
Dan Stillman
cc45e9019b Apply Big Sur styling when macOS version comes through correctly
Follow-up to zotero/zotero-standalone-build@f7a506a32 -- now that some
people are somehow getting an accurate macOS version number, we need to
apply the Big Sur CSS to all higher versions.

https://forums.zotero.org/discussion/98406/zotero-will-not-open-mac-os-monterey
2022-07-17 05:09:05 -04:00
Dan Stillman
98d6f40e4a Add warning in toolbar for 'dev' channel builds 2022-07-16 04:19:42 -04:00
Martynas Bagdonas
2cad5b00d1 Use Quick Copy (Markdown) for direct plain-text copying from note-editor
Fixes #2505
2022-07-15 15:06:19 +03:00
Abe Jellinek
95239db0fd Prevent deleting My Publications
Previously showed a blank confirmation dialog and logged an error.
2022-07-13 16:23:50 -04:00
Martynas Bagdonas
4c063e7b6e Prevent delayed context pane expansion and pdf-reader rerender/resize when switching from library tab 2022-07-12 18:00:21 +03:00
Martynas Bagdonas
07f1517712 Don't include quotes when {{highlight}} is placed in a blockquote
Fixes #2697
2022-07-12 12:12:21 +03:00
Martynas Bagdonas
f0e0971c69 Include quotation marks for highlights by default, but allow to disable
Fixes #2697
2022-07-11 15:32:02 +03:00
Dan Stillman
acba90f272 Fix test failure after 26a5e2900a 2022-07-11 05:19:18 -04:00
Dan Stillman
26a5e2900a Better fix for errors from invalid sort fields
Just catch the error from `ItemTree::sort()` and clear the
secondary-sort and fallback-sort prefs so that sorting works on the next
attempt.

Replacement for a8ed30ce80

https://groups.google.com/g/zotero-dev/c/kc0-C6-SA74/m/bhHniGceAQAJ
2022-07-11 01:30:38 -04:00
Dan Stillman
34d66381d1 Revert "Validate fields in ItemTree::getSortFields()"
This reverts commit a8ed30ce80 and related
commits.

We'll address breakage from invalid sort fields another way, without
inconveniencing plugin authors.

https://groups.google.com/g/zotero-dev/c/kc0-C6-SA74/m/bhHniGceAQAJ
2022-07-11 00:43:15 -04:00
Dan Stillman
f7dbcdde4b Sort empty rows last for Date/Year columns when descending
Date columns start in descending order, so put empty rows at end

Also remove a seemingly unnecessary exception for Title when not the
primary sort and just always sort it with empty rows first
2022-07-10 23:47:02 -04:00
Dan Stillman
c4563af381 Fix sorting of attachments and notes columns
Regression from a8ed30ce80
2022-07-10 23:26:36 -04:00
Dan Stillman
56e7f0a10f Fix sorting by Item Type and feed item itemID
Regression from a8ed30ce80
2022-07-10 17:31:01 -04:00
Dan Stillman
3a160b2630 Increase downloadPDFViaBrowser.onLoadTimeout to 3000
Apparently necessary for some people to get PDFs on ScienceDirect. We
should come up with a better way of detecting the page load if we can,
so that we can avoid this long of a timeout for people who just will
never have access to ScienceDirect PDFs.

https://forums.zotero.org/discussion/comment/411056/#Comment_411056

Addresses #2698
2022-07-10 06:40:32 -04:00
Dan Stillman
124c18d54d CookieSandbox: Fix error setting request cookies
Regression from 6af3b0276a, showing up as "trackedBy is undefined" error

https://forums.zotero.org/discussion/98322/no-snapshot-or-pdf-autosaved-using-zotero-connector-on-safari
2022-07-10 06:28:56 -04:00
Dan Stillman
57c582e171 Merge new English strings 2022-07-09 18:08:38 -04:00
Dan Stillman
cf8819cc06 Add localized string for timestamp CSL locator
Not included in the CSL locales:

https://forums.zotero.org/discussion/comment/412060/#Comment_412060
2022-07-09 18:07:07 -04:00
Dan Stillman
fe74b02d28 Merge new English strings 2022-07-09 01:56:56 -04:00
Dan Stillman
1dcc2728dd Tweak confirmation message for recursive collection-item removal
I guess the "its" is technically a little clearer, but it's a bit
awkward, and I think it's reasonably implied.
2022-07-09 01:54:07 -04:00
Dan Stillman
c5dbf0169f
Merge pull request #2693 from tnajdek/mendeley-import-improve-feedback
Multiple improvements to Mendeley importer
2022-07-09 01:43:50 -04:00
Abe Jellinek
3f282fc25f
recursiveCollections: Remove from all subcollections on delete (#2689) 2022-07-09 01:37:14 -04:00
Dan Stillman
7163a196f8 Update pdf-reader and pdf-worker submodules 2022-07-09 01:32:45 -04:00
Dan Stillman
8c0499468e Update locales from Transifex 2022-07-09 01:28:18 -04:00
Dan Stillman
629892b0b8 Add CSL 1.0.2 locators and switch to CSL locales for localization
And move Page to middle of list, while still selecting by default

https://forums.zotero.org/discussion/comment/396602/#Comment_396602
2022-07-09 01:00:07 -04:00
Dan Stillman
2ff67c987e Fix test failure after a8ed30ce8
We added a `toggleSort()` call on the title, so then when called again
it reverses the direction. To avoid, sort by creator and then title in
the second test.
2022-07-08 06:44:10 -04:00
Dan Stillman
a8ed30ce80 Validate fields in ItemTree::getSortFields()
To avoid startup hang if a plugin does something bad:

https://forums.zotero.org/discussion/comment/411843/#Comment_411843

Fixes #2692
2022-07-08 06:01:23 -04:00
Tom Najdek
43f15e7517
Improve log output for multiple Mendeley imports
Helps with debugging cases where new import is started while previous
one is still being cancelled
2022-07-07 13:40:40 +02:00
Tom Najdek
df11227e0a
Lint code related to Mendeley importer 2022-07-07 13:40:40 +02:00
Tom Najdek
d418d79e70
Fix a bug in regex extracting fields to "extra"
Because regex is built using a template string, \s* is actually escaped
into s*, i.e. literal "s" appearing 0 or more times. In most cases this
would mean that output can have spacing slightly off. In extreme case,
when identifier starts with letter "s", this could this could lead to
identifier being stored incorrectly.

Also adjusted tests to be more strict and mock data to cover this case.
2022-07-07 13:40:40 +02:00
Tom Najdek
42aebccbf4
Mendeley import: detect win close, better feedback
* Detect wizard cancel/close and interrupt import. This will still
  have to wait for current fetch (file or metadata) to complete but will
  then advance to the cleanup stage
* Advance progress bar during metadata fetch
* Add some extra logging
2022-07-07 13:40:33 +02:00
Tom Najdek
7b6e19435b
Don't try to remove temporary dir after import
Previously we had a structure that also created folders in temp dir,
that has since changed but code in cleanup phase to remove containing
dir has been accidentally left unremoved.
2022-07-07 10:09:53 +02:00
Martynas Bagdonas
f650f95081 Fix disabled "Add to Note" when a child note is open
Fixes #2687
2022-07-06 11:33:22 +03:00
Dan Stillman
0357026865 Update global schema and utilities for CSL 1.0.2 2022-07-05 01:34:10 -04:00
Martynas Bagdonas
92d89becc7 Adjust page rotation labels in PDF reader
Fixes #2669
2022-07-01 16:33:08 +03:00
Martynas Bagdonas
49f9642c0d Show parent item title for child notes in context pane
Fixes #2681
2022-07-01 15:32:14 +03:00
Dan Stillman
94372111f5 Update pdf-reader submodule 2022-07-01 04:32:42 -04:00
Martynas Bagdonas
f0b896d282
Implement smart notes switching in contextPane (#2671)
Fixes #2650
2022-07-01 03:28:20 -04:00
Dan Stillman
ec68216310 Update version 2022-06-24 01:05:08 -04:00
Dan Stillman
b215d93ac9 Update utilities submodule and date-parsing tests
Includes strToDate() fixes
2022-06-22 04:41:33 -04:00
Dan Stillman
bf1480f8f0 Update locales from Transifex 2022-06-21 23:05:20 -04:00
Dan Stillman
6f089445a5 Update translators 2022-06-21 22:17:44 -04:00
Martynas Bagdonas
14e62ee34f Update pdf-reader and note-editor, use specific prebuilds, tweak code 2022-06-17 16:47:53 +03:00
Adomas Venčkauskas
43c86c0e58 Limit the force-click during drag fix to macOS (4fb0661b) 2022-06-15 09:08:30 +03:00
Adomas Venčkauskas
6f3f5d2ea8 Fix alt-arrow not working on macOS when editing collection names.
Fixes #2651
2022-06-14 14:27:34 +03:00
Adomas Venčkauskas
4fb0661b50 Fix force-click during drag sometimes causing collections to switch
Closes #2652
2022-06-14 14:18:36 +03:00
Abe Jellinek
57ab92b5b9 Fix error if New Collection prompt is canceled 2022-06-09 18:32:14 -05:00
Dan Stillman
7b20972c27 Use String.fromCharCode() to load Chinese characters in strToDate() test
Follow-up to dc045250a6
2022-06-09 00:03:34 -04:00
Dan Stillman
dc045250a6 Fix parsing of Chinese and other non-ASCII month names
https://forums.zotero.org/discussion/97683/bug-locale-issues-concerning-zotero-and-zotero-connector
2022-06-08 23:45:18 -04:00
Dan Stillman
256bd157ed Properly strip ZWJ in isOnlyEmoji() 2022-06-07 20:36:48 -04:00
Dan Stillman
2b45b7fea3 Fix Emoji detection
And move to Zotero.Utilities.Internal.isOnlyEmoji()

Fixes #2643
2022-06-07 20:12:29 -04:00
Dan Stillman
1d5fd1308a Update styles submodule 2022-06-07 03:36:10 -04:00
Abe Jellinek
d8069fac50
Fix sorting by attachment (#2586) 2022-06-07 01:32:27 -04:00
Dan Stillman
18516a33d9 Update locales from Transifex and merge new English strings 2022-06-07 01:25:57 -04:00
Dan Stillman
b658f09dd8 citeproc-js: Add space after left margin in plain-text mode
This adds a space after the the number in some numeric styles ("[1]" in
IEEE style, "1." in Nature) when pasting bibliography entries into a
plain-text editor.

Some styles (ACS, AMA, Vancouver) will need to be updated to remove
their hard-coded spaces to avoid double spaces after this change.

Patch from @zepinglee

Fixes #2633
2022-06-07 00:57:01 -04:00
Martynas Bagdonas
5799bdd554
Add support for PDF page deletion and rotation (#2595)
* Add support for PDF page deletion and rotation
Fixes #2561
2022-06-07 00:53:37 -04:00
Adomas Venčkauskas
62c7b4db2b Log full hidden browser count after its creation 2022-06-06 12:50:21 +03:00
Adomas Venčkauskas
caa0b05e5f Fix a MIMETypeHandler.removeHandler() bug 2022-06-06 12:49:58 +03:00
Adomas Venčkauskas
5095543bef Improve downloadPDFViaBrowser handlers and logging 2022-06-06 12:49:28 +03:00
Adomas Venčkauskas
6af3b0276a Fix cookieSandbox not overriding user agent if no cookies for domain.
Potential fix for #2635
2022-06-06 12:48:29 +03:00
Martynas Bagdonas
67700f8a2c Fix library tab tooltip always staying on top 2022-05-31 15:37:30 +03:00
Dan Stillman
d12f3eda61 Fix detection of some emoji set as colored tags
Emoji such as ⭐️ that began as text-based Unicode characters and depend
on Variation Selector-16 to display the emoji presentation weren't being
properly detected.
2022-05-26 06:53:36 -04:00
Dan Stillman
deabf3f042 Fix support tests after 28ed3e34b 2022-05-24 04:32:05 -04:00
Dan Stillman
28ed3e34b5 Update global schema
Includes the Preprint archiveID/number mapping for #2481
2022-05-24 03:43:36 -04:00
Dan Stillman
354183c25d Update locales from Transifex 2022-05-24 03:43:36 -04:00
Dan Stillman
b0f69ef7e3 Update note-editor submodule 2022-05-23 16:19:20 -04:00
Dan Stillman
ebc23d73b0 Add prefs for timeouts for downloading PDF via browser
1 second isn't enough for some users, so try 1.5, and allow for easier
debugging via a pref.

https://forums.zotero.org/discussion/96431/i-have-access-to-pdf-but-zotero-cannot-automatically-add-it
https://forums.zotero.org/discussion/97277/unable-to-download-papers-using-zotero-chrome-extension
2022-05-20 05:01:57 -04:00
Dan Stillman
419f74379b Feed import: Don't fail on OPML entry with no title or text
https://forums.zotero.org/discussion/96841/impossible-dimporter-ompl-rss
2022-05-20 00:33:25 -04:00
Dan Stillman
603b843f71 Update version 2022-05-17 23:54:20 -04:00
Dan Stillman
1e3959020d Update note-editor submodule 2022-05-17 04:57:06 -04:00
Martynas Bagdonas
3fd0abf6b0 Avoid error interrupting annotation extraction from multiple items 2022-05-16 13:42:44 +03:00
Dan Stillman
111a254696 Update locales from Transifex 2022-05-16 00:56:22 -04:00
Dan Stillman
69616500d8 Update translators, styles, and CSL locales 2022-05-16 00:56:13 -04:00
Dan Stillman
693f9a7e1b Try to open external PDF reader to page number for "Show on Page"
PDF-handling code from `ZoteroPane.viewAttachment()` predates
`Zotero.OpenPDF` and didn't handle page numbers for external PDF
readers, so when called from "Show on Page" in a note (via
`ZoteroPane.viewPDF()`) it just launched the file itself in the
specified handler or via the OS.

For now, change to use `Zotero.OpenPDF.openToPage()` when there's a page
number. Later we should use `Zotero.OpenPDF` for everything, but that
code is more complicated than just using `launchFile()`, so we'll need
to do some testing (particularly on Linux) to see what's best.

(Note that "Show on Page" with an external reader doesn't make a ton of
sense, since you can't actually see the annotation, but opening the
right page is better than nothing.)

Fixes #2612
2022-05-15 04:28:30 -04:00
Abe Jellinek
0c0afb129b Scaffold templates: Pull URL from doc
Link URLs could lead to redirects.
2022-05-12 14:49:30 -07:00
Abe Jellinek
a11c5987ad Fix Scaffold load error when fontSize pref is set 2022-05-10 09:10:13 -07:00
308 changed files with 12618 additions and 5383 deletions

View file

@ -7,8 +7,9 @@ content zotero-platform chrome/content/zotero-platform/unix/ os=FreeBSD
content zotero-platform chrome/content/zotero-platform/unix/ os=OpenBSD
content zotero-platform-version chrome/content/zotero-platform/default-version/
# Big Sur shows up as 10.16 when linked against 10.11
content zotero-platform-version chrome/content/zotero-platform/mac-big-sur/ os=Darwin osversion=10.16
# Big Sur and later show up as 10.16 when linked against 10.11
# (Usually: https://forums.zotero.org/discussion/98406/zotero-will-not-open-mac-os-monterey)
content zotero-platform-version chrome/content/zotero-platform/mac-big-sur/ os=Darwin osversion>=10.16
#content zotero-platform-version chrome/content/zotero-platform/mac-big-sur/ os=Darwin
resource zotero resource/

View file

@ -57,6 +57,7 @@ var Scaffold = new function () {
var _translatorsLoadedPromise;
var _translatorProvider = null;
var _lastModifiedTime = 0;
var _needRebuildTranslatorSuggestions = true;
var _editors = {};
@ -98,16 +99,7 @@ var Scaffold = new function () {
);
}
});
// Set font size from general pref
Zotero.setFontSize(document.getElementById('scaffold-pane'));
// Set font size of code editor
var size = Zotero.Prefs.get("scaffold.fontSize");
if (size) {
this.setFontSize(size);
}
this.generateTranslatorID();
// Add List fields help menu entries for all other item types
@ -146,6 +138,15 @@ var Scaffold = new function () {
this.initCodeEditor();
this.initTestsEditor();
// Set font size from general pref
Zotero.setFontSize(document.getElementById('scaffold-pane'));
// Set font size of code editor
var size = Zotero.Prefs.get("scaffold.fontSize");
if (size) {
this.setFontSize(size);
}
// Listen for Scaffold coming to the foreground and reload translators
window.addEventListener('activate', () => this.reloadTranslators());
@ -156,6 +157,7 @@ var Scaffold = new function () {
onLoadComplete: () => {
document.getElementById('cmd_load').removeAttribute('disabled');
_needRebuildTranslatorSuggestions = true;
}
});
@ -267,6 +269,7 @@ var Scaffold = new function () {
monaco.languages.registerCodeLensProvider('javascript', this.createRunCodeLensProvider(monaco, editor));
monaco.languages.registerHoverProvider('javascript', this.createHoverProvider(monaco, editor));
monaco.languages.registerCompletionItemProvider('javascript', this.createCompletionProvider(monaco, editor));
let tsLib = await Zotero.File.getContentsAsync(
OS.Path.join(Scaffold_Translators.getDirectory(), 'index.d.ts'));
@ -500,6 +503,43 @@ var Scaffold = new function () {
resolveCodeLens: (_model, codeLens, _token) => codeLens
};
};
this.createCompletionProvider = function (monaco, editor) {
let suggestions = null;
return {
provideCompletionItems(model, position) {
let prefixText = model.getValueInRange({
startLineNumber: position.lineNumber,
startColumn: 1,
endLineNumber: position.lineNumber,
endColumn: position.column
});
if (/setTranslator\([^)]*$/.test(prefixText)) {
let word = model.getWordUntilPosition(position);
let range = {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: word.startColumn,
endColumn: word.endColumn
};
if (!suggestions || _needRebuildTranslatorSuggestions) {
// Cache the suggestions minus the range field
suggestions = [...Scaffold_Translators._translators.entries()].map(([id, meta]) => {
return {
label: `${meta.translator.label}: '${id}'`,
kind: monaco.languages.CompletionItemKind.Constant,
insertText: `'${id}'`
};
});
_needRebuildTranslatorSuggestions = false;
}
// Add the range to each suggestion before returning
return { suggestions: suggestions.map(s => ({ ...s, range })) };
}
return { suggestions: [] };
}
};
};
this.updateModelMarkers = function (translatorPath) {
runESLint(translatorPath)
@ -1729,6 +1769,9 @@ var Scaffold = new function () {
var url = item.getElementsByTagName("listcell")[0].getAttribute("label");
var test = JSON.parse(item.getUserData("test-string"));
var urlOrData = (test.input !== undefined) ? test.input : url;
if (typeof urlOrData !== 'string') {
urlOrData = JSON.stringify(urlOrData, null, '\t');
}
Zotero.Utilities.Internal.copyTextToClipboard(urlOrData);
};

View file

@ -22,7 +22,7 @@
*/
async function detectWeb(doc, url) {
function detectWeb(doc, url) {
// TODO: adjust the logic here
if ($$CURSOR$$url.includes('/article/')) {
return 'newspaperArticle';
@ -52,12 +52,12 @@ function getSearchResults(doc, checkOnly) {
}
async function doWeb(doc, url) {
if (await detectWeb(doc, url) == 'multiple') {
if (detectWeb(doc, url) == 'multiple') {
let items = await Zotero.selectItems(getSearchResults(doc, false));
if (items) {
await Promise.all(
Object.keys(items)
.map(url => requestDocument(url).then(doc => scrape(doc, url)))
.map(url => requestDocument(url).then(scrape))
);
}
}
@ -65,3 +65,8 @@ async function doWeb(doc, url) {
await scrape(doc, url);
}
}
async function scrape(doc, url = doc.location.href) {
// TODO: implement or add a scrape function template
}

View file

@ -1,4 +1,4 @@
async function scrape(doc, url) {
async function scrape(doc, url = doc.location.href) {
// TODO adjust the url building
let m = url.match(/FId=([\w\d]+)&/);
if (m) {

View file

@ -1,4 +1,4 @@
async function scrape(doc, url) {
async function scrape(doc, url = doc.location.href) {
let translator = Zotero.loadTranslator('web');
// Embedded Metadata
translator.setTranslator('951c027d-74ac-47d4-a107-9c3069ab7b48');

View file

@ -1,4 +1,4 @@
async function scrape(doc, url) {
async function scrape(doc, url = doc.location.href) {
// TODO adjust the selector for the lines here
let lines = doc.querySelectorAll('table#marcData tr');

View file

@ -1,4 +1,4 @@
async function scrape(doc, url) {
async function scrape(doc, url = doc.location.href) {
let DOI = url.match(/\/(10\.[^#?]+)/)[1];
// TODO adjust the URL here
let risURL = `http://citation-needed.services.springer.com/v2/references/${DOI}?format=refman&flavour=citation`;

View file

@ -25,6 +25,12 @@ row > label:first-child, .creator-type-label, #more-creators-label
max-height: 7px;
}
.zotero-focusable:focus {
/* At least in Fx60, the color doesn't quite match Monterey's native textbox outline,
but it will have to do for now */
outline: 2px solid -moz-mac-focusring !important;
}
textbox
{
margin: -1px 5px -1px 0;

View file

@ -162,7 +162,8 @@ input {
/* This seems to be necessary to center the tabs. Not sure why. */
.zotero-editpane-tabs > tab:last-of-type > hbox .tab-text {
margin: 2px 9px 2px 9px !important;
margin-left: 9px !important;
margin-right: 9px !important;
}
.zotero-editpane-tabs > tab[selected=true] > hbox .tab-text {

View file

@ -96,6 +96,8 @@ var ZoteroAdvancedSearch = new function() {
ref: _searchBox.search,
isSearchMode: () => true,
getItems: async function () {
await Zotero.Libraries.get(_libraryID).waitForDataLoad('item');
var search = _searchBox.search.clone();
search.libraryID = _libraryID;
var ids = await search.search();

View file

@ -719,7 +719,7 @@
<![CDATA[
// getCreatorFields(), switchCreatorMode() and handleCreatorAutoCompleteSelect()
// may need need to be adjusted if this DOM structure changes
var tabindex = this._tabIndexMinCreators + (this._creatorCount * 6);
var fieldMode = Zotero.Prefs.get('lastCreatorFieldMode');
var firstName = '';
var lastName = '';
@ -757,9 +757,15 @@
}
typeBox.setAttribute("fieldname", 'creator-' + this._creatorCount + '-typeID');
if (this.editable) {
typeBox.className = 'creator-type-label zotero-clicky';
typeBox.className = 'creator-type-label zotero-clicky zotero-focusable';
typeBox.setAttribute('ztabindex', tabindex - 1);
typeBox.setAttribute('role', 'listbox');
typeBox.addEventListener('focus', ev => {
this._lastTabIndex = parseInt(ev.target.getAttribute('ztabindex'));
});
var img = document.createElement('image');
typeBox.appendChild(img);
typeBox.setAttribute('onkeypress', 'return document.getBindingParent(this).handleKeyPress(event)');
}
else {
typeBox.className = 'creator-type-label';
@ -772,12 +778,18 @@
var hbox = document.createElement("hbox");
hbox.className = 'creator-type-value';
hbox.addEventListener('focusin', ev => {
if(ev.target.classList.contains('zotero-focusable')) {
this._lastTabIndex = parseInt(ev.target.getAttribute('ztabindex'));
}
});
// Name
var firstlast = document.createElement("hbox");
firstlast.className = 'creator-name-box';
firstlast.setAttribute("flex","1");
var tabindex = this._tabIndexMinCreators + (this._creatorCount * 2);
var fieldName = 'creator-' + this._creatorCount + '-lastName';
var lastNameLabel = firstlast.appendChild(
this.createValueElement(
@ -820,29 +832,40 @@
var toggleButton = document.createElement('label');
toggleButton.setAttribute('fieldname',
'creator-' + this._creatorCount + '-fieldMode');
toggleButton.className = 'zotero-field-toggle zotero-clicky';
toggleButton.setAttribute('ztabindex', tabindex + 2);
toggleButton.setAttribute('role', 'button');
toggleButton.className = 'zotero-field-toggle zotero-focusable zotero-clicky';
toggleButton.setAttribute('onkeypress', 'return document.getBindingParent(this).handleKeyPress(event)');
hbox.appendChild(toggleButton);
// Minus (-) button
var removeButton = document.createElement('label');
removeButton.setAttribute('ztabindex', tabindex + 3);
removeButton.setAttribute('role', 'button');
removeButton.setAttribute('aria-label', Zotero.getString('general.delete'));
removeButton.setAttribute("value","-");
removeButton.setAttribute("class","zotero-clicky zotero-clicky-minus");
removeButton.setAttribute('class','zotero-clicky zotero-focusable zotero-clicky-minus');
removeButton.setAttribute('onkeypress', 'return document.getBindingParent(this).handleKeyPress(event)');
// If default first row, don't let user remove it
if (defaultRow) {
this.disableButton(removeButton);
}
else {
removeButton.setAttribute("onclick",
"document.getBindingParent(this).removeCreator("
+ this._creatorCount
+ ", this.parentNode.parentNode)");
let index = this._creatorCount;
removeButton.onclick = (ev) => {
document.getBindingParent(ev.currentTarget)
.removeCreator(index, ev.currentTarget.parentNode.parentNode);
}
}
hbox.appendChild(removeButton);
// Plus (+) button
var addButton = document.createElement('label');
addButton.setAttribute('ztabindex', tabindex + 4);
addButton.setAttribute("value","+");
addButton.setAttribute("class", "zotero-clicky zotero-clicky-plus");
addButton.setAttribute('role', 'button');
addButton.setAttribute('class', 'zotero-clicky zotero-focusable zotero-clicky-plus');
addButton.setAttribute('onkeypress', 'return document.getBindingParent(this).handleKeyPress(event)');
// If row isn't saved, don't let user add more
if (unsaved) {
this.disableButton(addButton);
@ -958,7 +981,11 @@
button.style.background = `url("chrome://zotero/skin/textfield-dual${Zotero.hiDPISuffix}.png") center/21px auto no-repeat`;
button.setAttribute('tooltiptext', Zotero.getString('pane.item.switchFieldMode.two'));
lastName.setAttribute('fieldMode', '1');
button.setAttribute('onclick', "document.getBindingParent(this).switchCreatorMode(Zotero.getAncestorByTagName(this, 'row'), 0, false, true)");
button.onclick = (ev) => {
document
.getBindingParent(ev.currentTarget)
.switchCreatorMode(Zotero.getAncestorByTagName(ev.currentTarget, 'row'), 0, false, true);
};
lastName.setAttribute('flex', '1');
delete lastName.style.width;
delete lastName.style.maxWidth;
@ -998,7 +1025,11 @@
button.style.background = `url("chrome://zotero/skin/textfield-single${Zotero.hiDPISuffix}.png") center/21px auto no-repeat`;
button.setAttribute('tooltiptext', Zotero.getString('pane.item.switchFieldMode.one'));
lastName.setAttribute('fieldMode', '0');
button.setAttribute('onclick', "document.getBindingParent(this).switchCreatorMode(Zotero.getAncestorByTagName(this, 'row'), 1, false, true)");
button.onclick = (ev) => {
document
.getBindingParent(ev.currentTarget)
.switchCreatorMode(Zotero.getAncestorByTagName(ev.currentTarget, 'row'), 1, false, true)
};
lastName.setAttribute('flex', '0');
// appropriately truncate lastName
@ -1720,6 +1751,19 @@
<![CDATA[
var target = event.target;
var focused = document.commandDispatcher.focusedElement;
if ((event.key == 'ArrowUp' || event.key === 'ArrowDown')
&& target.classList.contains('creator-type-label')) {
event.preventDefault();
target.click();
return true;
}
if (event.key === ' ' && target.classList.contains('zotero-focusable')) {
event.preventDefault();
target.click();
return true;
}
switch (event.keyCode)
{
@ -1932,8 +1976,6 @@
newVal = val;
// Reset creator mode settings here so that flex attribute gets reset
this.switchCreatorMode(row, (otherFields.fieldMode ? 1 : 0), true);
if (Zotero.ItemTypes.getName(this.item.itemTypeID) === "bookSection") {
var creatorTypeLabels = document.getAnonymousNodes(this)[0].getElementsByClassName("creator-type-label");
Zotero.debug(creatorTypeLabels[creatorTypeLabels.length-1] + "");
@ -2018,6 +2060,11 @@
// Disassociate textbox from label
label.setAttribute('control', elem.getAttribute('id'));
if (field == 'creator') {
// Set correct flex settings and fieldMode attribute
this.switchCreatorMode(row, (otherFields.fieldMode ? 1 : 0), true);
}
if (this.saveOnEdit) {
await this.item.saveTx();
}
@ -2295,6 +2342,18 @@
</method>
<method name="focusField">
<parameter name="fieldName"/>
<body>
<![CDATA[
let field = document.getAnonymousNodes(this)[0].querySelector(`[fieldname="${fieldName}"][ztabindex]`);
if (!field) return false;
return this._focusNextField(field.getAttribute('ztabindex'));
]]>
</body>
</method>
<!--
Advance the field focus forward or backward
@ -2312,7 +2371,7 @@
tabindex = parseInt(tabindex);
// Get all fields with ztabindex attributes
var tabbableFields = box.querySelectorAll('*[ztabindex]');
var tabbableFields = box.querySelectorAll('*[ztabindex]:not([disabled=true])');
if (!tabbableFields.length) {
Zotero.debug("No tabbable fields found");
@ -2323,16 +2382,20 @@
if (back) {
Zotero.debug('Looking for previous tabindex before ' + tabindex, 4);
for (let i = tabbableFields.length - 1; i >= 0; i--) {
if (parseInt(tabbableFields[i].getAttribute('ztabindex')) < tabindex) {
let field = tabbableFields[i];
let tabIndexHere = parseInt(field.getAttribute('ztabindex'));
if (tabIndexHere !== -1 && tabIndexHere < tabindex) {
next = tabbableFields[i];
break;
}
}
}
}
else {
Zotero.debug('Looking for tabindex ' + tabindex, 4);
for (var pos = 0; pos < tabbableFields.length; pos++) {
if (parseInt(tabbableFields[pos].getAttribute('ztabindex')) >= tabindex) {
let field = tabbableFields[pos];
let tabIndexHere = parseInt(field.getAttribute('ztabindex'));
if (tabIndexHere !== -1 && tabIndexHere >= tabindex) {
next = tabbableFields[pos];
break;
}
@ -2345,7 +2408,7 @@
}
// Drop-down needs to be focused
if (next.id == 'item-type-menu') {
if (next.id == 'item-type-menu' || next.classList.contains('zotero-focusable')) {
next.focus();
}
// Fields need to be clicked

View file

@ -353,15 +353,16 @@
<body>
<![CDATA[
(async () => {
let n = 0;
while (!this._editorInstance && n++ < 100) {
await Zotero.Promise.delay(10);
}
await this._editorInstance._initPromise;
this._iframe.focus();
try {
let n = 0;
while (!this._editorInstance && n++ < 100) {
await Zotero.Promise.delay(10);
}
await this._editorInstance._initPromise;
this._iframe.focus();
this._editorInstance._iframeWindow.document.querySelector('.toolbar-button-return').focus();
} catch(e) {
}
catch (e) {
}
})();
]]>

View file

@ -640,11 +640,8 @@ var CollectionTree = class CollectionTree extends LibraryTree {
this.tree.invalidate();
return;
}
if (action == 'refresh') {
// If trash is refreshed, we probably need to update the icon from full to empty
if (type == 'trash') {
this.tree.invalidate();
}
if (action == 'refresh' && type != 'trash') {
// Trash handled below
return;
}
if (type == 'feed' && (action == 'unreadCountUpdated' || action == 'statusChanged')) {
@ -826,6 +823,11 @@ var CollectionTree = class CollectionTree extends LibraryTree {
}
}
}
else if (action == 'refresh' && type == 'trash') {
// We need to update the trash's status (full or empty), and if empty,
// the row might be removed
await this.reload();
}
this.forceUpdate();
var promise = this.waitForSelect();
@ -839,6 +841,7 @@ var CollectionTree = class CollectionTree extends LibraryTree {
* @param ids {String[]} list of row ids to be highlighted
*/
async setHighlightedRows(ids) {
if (this._editing) return;
try {
this._highlightedRows = new Set();
@ -1614,7 +1617,7 @@ var CollectionTree = class CollectionTree extends LibraryTree {
return false;
}
let newAttachment = Zotero.Attachments.copyAttachmentToLibrary(item, targetLibraryID);
let newAttachment = await Zotero.Attachments.copyAttachmentToLibrary(item, targetLibraryID);
if (options.annotations) {
await Zotero.Items.copyChildItems(item, newAttachment);
}
@ -2306,7 +2309,7 @@ var CollectionTree = class CollectionTree extends LibraryTree {
}
if (showTrash) {
let deletedItems = await Zotero.Items.getDeleted(libraryID);
let deletedItems = await Zotero.Items.getDeleted(libraryID, true);
if (deletedItems.length || Zotero.Prefs.get("showTrashWhenEmpty")) {
var ref = {
libraryID: libraryID

View file

@ -48,7 +48,7 @@ function AnnotationBox({ data }) {
<div className="title">{Zotero.getString('itemTypes.annotation')}</div>
<div className="container">
<div className="header">
<div>{Zotero.getString('citation.locator.page')} {data.pageLabel}</div>
<div>{Zotero.Cite.getLocatorString('page')} {data.pageLabel}</div>
</div>
{data.text !== undefined
? <div className="text" style={textStyle}>{data.text}</div>

View file

@ -152,7 +152,7 @@ i('TreeitemThesis', 'chrome://zotero/skin/treeitem-thesis.png');
i('TreeitemTvBroadcast', 'chrome://zotero/skin/treeitem-tvBroadcast.png', false);
i('TreeitemVideoRecording', 'chrome://zotero/skin/treeitem-videoRecording.png', false);
i('TreeitemWebpageGray', 'chrome://zotero/skin/treeitem-webpage-gray.png');
i('TreeitemWebpage', 'chrome://zotero/skin/treeitem-webpage.png', false);
i('TreeitemWebpage', 'chrome://zotero/skin/treeitem-webpage.png');
// Treesource
i('TreesourceBucket', 'chrome://zotero/skin/treesource-bucket.png', false);

View file

@ -235,7 +235,7 @@ const TabBar = forwardRef(function (props, ref) {
function handleWheel(event) {
// Normalize wheel speed
let x = event.deltaX;
let x = event.deltaX || event.deltaY;
if (x && event.deltaMode) {
if (event.deltaMode === 1) {
x *= 20;
@ -302,7 +302,10 @@ const TabBar = forwardRef(function (props, ref) {
onWheel={handleWheel}
>
<div className="pinned-tabs">
<div className="tabs">
<div
className="tabs"
onMouseOut={handleTabBarMouseOut}
>
{tabs.length ? renderTab(tabs[0], 0) : null}
</div>
</div>

View file

@ -660,7 +660,14 @@ class VirtualizedTable extends React.Component {
this._isMouseDrag = true;
}
_onDragEnd = () => {
_onDragEnd = async () => {
// macOS force-click sometimes causes a second mouseup event to be fired some time later
// causing a collection change on dragend, so we add a delay here. It shouldn't cause any issues
// because isMouseDrag is only used in mouseup handler to exactly prevent from accidentally switching
// selection after dragend.
if (Zotero.isMac) {
await Zotero.Promise.delay(500);
}
this._isMouseDrag = false;
}
@ -740,6 +747,7 @@ class VirtualizedTable extends React.Component {
this.selection.pivot = index;
this.invalidateRow(previousFocused);
this.invalidateRow(index);
this.selection._updateTree(shouldDebounce);
}
// Normal selection
else if (!toggleSelection) {

View file

@ -51,7 +51,10 @@ var ZoteroContextPane = new function () {
var _itemContexts = [];
var _notesContexts = [];
var _globalDeckIndex = [];
var _preventGlobalDeckChange = false;
// Using attribute instead of property to set 'selectedIndex'
// is more reliable
@ -176,6 +179,20 @@ var ZoteroContextPane = new function () {
if (Zotero_Tabs.deck.children.length == 1) {
_notesContexts.forEach(x => x.notesListRef.current.setExpanded(false));
}
// Close tab specific notes if tab id no longer exists, but
// do that only when unloaded tab is reloaded
setTimeout(() => {
var contextNodes = Array.from(_notesPaneDeck.children);
for (let contextNode of contextNodes) {
var nodes = Array.from(contextNode.querySelector('.zotero-context-pane-tab-notes-deck').children);
for (let node of nodes) {
var tabID = node.getAttribute('data-tab-id');
if (!document.getElementById(tabID)) {
node.remove();
}
}
}
});
}
else if (action == 'select') {
// It seems that changing `hidden` or `collapsed` values might
@ -188,6 +205,14 @@ var ZoteroContextPane = new function () {
_tabCover.hidden = true;
}
else if (Zotero_Tabs.selectedType == 'reader') {
if (_panesDeck.selectedIndex == 1
&& _notesPaneDeck.selectedPanel.selectedIndex != 2
&& !_preventGlobalDeckChange) {
let libraryID = _notesPaneDeck.selectedPanel.getAttribute('data-library-id');
_globalDeckIndex[libraryID] = _notesPaneDeck.selectedPanel.selectedIndex;
}
_preventGlobalDeckChange = false;
var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
if (reader) {
_tabCover.hidden = false;
@ -207,11 +232,25 @@ var ZoteroContextPane = new function () {
var notesContext = _getNotesContext(attachment.libraryID);
notesContext.updateFromCache();
}
let tabNotesDeck = _notesPaneDeck.selectedPanel.querySelector('.zotero-context-pane-tab-notes-deck');
let selectedIndex = Array.from(tabNotesDeck.children).findIndex(x => x.getAttribute('data-tab-id') == ids[0]);
if (selectedIndex != -1) {
tabNotesDeck.setAttribute('selectedIndex', selectedIndex);
_notesPaneDeck.selectedPanel.setAttribute('selectedIndex', 2);
}
else {
let libraryID = _notesPaneDeck.selectedPanel.getAttribute('data-library-id');
_notesPaneDeck.selectedPanel.setAttribute('selectedIndex', _globalDeckIndex[libraryID] || 0);
}
})();
}
_contextPaneSplitter.setAttribute('hidden', false);
// It seems that on heavy load (i.e. syncing) the line below doesn't set the correct value
_contextPane.setAttribute('collapsed', !(_contextPaneSplitter.getAttribute('state') != 'collapsed'));
// It seems that on heavy load (i.e. syncing) the line below doesn't set the correct value,
// therefore we repeat the same operation at the end of JS message queue
setTimeout(() => {
_contextPane.setAttribute('collapsed', !(_contextPaneSplitter.getAttribute('state') != 'collapsed'));
});
@ -243,9 +282,14 @@ var ZoteroContextPane = new function () {
if (splitter.getAttribute('state') != 'collapsed') {
if (_panesDeck.selectedIndex == 1) {
var node = _notesPaneDeck.selectedPanel;
if (node.selectedIndex == 1) {
return node.querySelector('zoteronoteeditor');
var libraryContext = _notesPaneDeck.selectedPanel;
// Global note
if (libraryContext.selectedIndex == 1) {
return libraryContext.querySelector('zoteronoteeditor');
}
// Tab specific child note
else if (libraryContext.selectedIndex == 2) {
return libraryContext.querySelector('.zotero-context-pane-tab-notes-deck').selectedPanel.querySelector('zoteronoteeditor');
}
}
}
@ -419,12 +463,21 @@ var ZoteroContextPane = new function () {
editor.className = 'zotero-context-pane-pinned-note';
editor.setAttribute('flex', 1);
noteContainer.append(title, editor);
var tabNotesContainer = document.createElement('vbox');
var title = document.createElement('vbox');
title.className = 'zotero-context-pane-editor-parent-line';
let tabNotesDeck = document.createElement('deck');
tabNotesDeck.className = 'zotero-context-pane-tab-notes-deck';
tabNotesDeck.setAttribute('flex', 1);
tabNotesContainer.append(title, tabNotesDeck);
let contextNode = document.createElement('deck');
contextNode.append(list, noteContainer);
contextNode.append(list, noteContainer, tabNotesContainer);
_notesPaneDeck.append(contextNode);
contextNode.className = 'context-node';
contextNode.setAttribute('data-library-id', libraryID);
contextNode.setAttribute('selectedIndex', 0);
editor.returnHandler = () => {
@ -440,8 +493,15 @@ var ZoteroContextPane = new function () {
if (!attachment) {
return;
}
var annotations = attachment.getAnnotations().filter(x => x.annotationType != 'ink');
if (!annotations.length) {
return;
}
var note = await Zotero.EditorInstance.createNoteFromAnnotations(
attachment.getAnnotations().filter(x => x.annotationType != 'ink'), child && attachment.parentID
annotations,
{
parentID: child && attachment.parentID
}
);
_updateAddToNote();
@ -449,7 +509,7 @@ var ZoteroContextPane = new function () {
input.value = '';
_updateNotesList();
_setPinnedNote(note.id);
_setPinnedNote(note);
}
function _createNote(child) {
@ -463,10 +523,7 @@ var ZoteroContextPane = new function () {
}
item.parentID = attachment.parentID;
}
editor.mode = 'edit';
editor.item = item;
editor.parentItem = null;
editor.focus();
_setPinnedNote(item);
_updateAddToNote();
input.value = '';
@ -672,7 +729,10 @@ var ZoteroContextPane = new function () {
<NotesList
ref={notesListRef}
onClick={(id) => {
_setPinnedNote(id);
let item = Zotero.Items.get(id);
if (item) {
_setPinnedNote(item);
}
}}
onContextMenu={(id, event) => {
document.getElementById('context-pane-list-move-to-trash').setAttribute('disabled', readOnly);
@ -727,21 +787,63 @@ var ZoteroContextPane = new function () {
return !Zotero.Libraries.get(libraryID).editable;
}
function _setPinnedNote(itemID) {
var item = Zotero.Items.get(itemID);
if (!item) {
return;
}
function _setPinnedNote(item) {
var readOnly = _isLibraryReadOnly(item.libraryID);
var context = _getNotesContext(item.libraryID);
if (context) {
var { editor, node } = context;
node.setAttribute('selectedIndex', 1);
editor.mode = readOnly ? 'view' : 'edit';
editor.item = item;
editor.parentItem = null;
node.querySelector('.zotero-context-pane-editor-parent-line').innerHTML = '';
let isChild = false;
var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
if (reader) {
let attachment = Zotero.Items.get(reader.itemID);
if (attachment.parentItemID == item.parentItemID) {
isChild = true;
}
}
var tabNotesDeck = _notesPaneDeck.selectedPanel.querySelector('.zotero-context-pane-tab-notes-deck');
var parentTitleContainer;
if (isChild) {
var vbox = document.createElement('vbox');
vbox.setAttribute('data-tab-id', Zotero_Tabs.selectedID);
vbox.style.display = 'flex';
editor = document.createElement('zoteronoteeditor');
editor.style.display = 'flex';
editor.style.width = '100%';
vbox.append(editor);
tabNotesDeck.append(vbox);
editor.mode = readOnly ? 'view' : 'edit';
editor.item = item;
editor.parentItem = null;
editor.returnHandler = () => {
_panesDeck.setAttribute('selectedIndex', 1);
_notesPaneDeck.selectedPanel.setAttribute('selectedIndex', 0);
vbox.remove();
_updateAddToNote();
_preventGlobalDeckChange = true;
};
_notesPaneDeck.selectedPanel.setAttribute('selectedIndex', 2);
tabNotesDeck.setAttribute('selectedIndex', tabNotesDeck.children.length - 1);
parentTitleContainer = _notesPaneDeck.selectedPanel.children[2].querySelector('.zotero-context-pane-editor-parent-line');
}
else {
node.setAttribute('selectedIndex', 1);
editor.mode = readOnly ? 'view' : 'edit';
editor.item = item;
editor.parentItem = null;
parentTitleContainer = node.querySelector('.zotero-context-pane-editor-parent-line');
}
editor.focus();
parentTitleContainer.innerHTML = '';
var parentItem = item.parentItem;
if (parentItem) {
var container = document.createElementNS(HTML_NS, 'div');
@ -752,7 +854,7 @@ var ZoteroContextPane = new function () {
title.append(parentItem.getDisplayTitle());
title.className = 'parent-title';
container.append(img, title);
node.querySelector('.zotero-context-pane-editor-parent-line').append(container);
parentTitleContainer.append(container);
}
_updateAddToNote();
}

View file

@ -238,26 +238,29 @@ var Zotero_File_Interface = new function() {
/*
* exports items to clipboard
*/
function exportItemsToClipboard(items, translatorID) {
function _translate(items, translatorID, callback) {
function exportItemsToClipboard(items, format) {
function _translate(items, format, callback) {
let translation = new Zotero.Translate.Export();
translation.setItems(items.slice());
translation.setTranslator(translatorID);
translation.setTranslator(format.id);
if (format.options) {
translation.setDisplayOptions(format.options);
}
translation.setHandler("done", callback);
translation.translate();
}
// If translating with virtual "Markdown + Rich Text" translator, use Note Markdown and
// Note HTML instead
if (translatorID == Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) {
translatorID = Zotero.Translators.TRANSLATOR_ID_NOTE_MARKDOWN;
_translate(items, translatorID, (obj, worked) => {
if (format.id == Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) {
let markdownFormat = { mode: 'export', id: Zotero.Translators.TRANSLATOR_ID_NOTE_MARKDOWN, options: format.markdownOptions };
let htmlFormat = { mode: 'export', id: Zotero.Translators.TRANSLATOR_ID_NOTE_HTML, options: format.htmlOptions };
_translate(items, markdownFormat, (obj, worked) => {
if (!worked) {
Zotero.log(Zotero.getString('fileInterface.exportError'), 'warning');
return;
}
translatorID = Zotero.Translators.TRANSLATOR_ID_NOTE_HTML;
_translate(items, translatorID, (obj2, worked) => {
_translate(items, htmlFormat, (obj2, worked) => {
if (!worked) {
Zotero.log(Zotero.getString('fileInterface.exportError'), 'warning');
return;
@ -293,14 +296,14 @@ var Zotero_File_Interface = new function() {
});
}
else {
_translate(items, translatorID, (obj, worked) => {
_translate(items, format, (obj, worked) => {
if (!worked) {
Zotero.log(Zotero.getString('fileInterface.exportError'), 'warning');
return;
}
let text = obj.string;
// For Note HTML translator use body content only
if (translatorID == Zotero.Translators.TRANSLATOR_ID_NOTE_HTML) {
if (format.id == Zotero.Translators.TRANSLATOR_ID_NOTE_HTML) {
let parser = Components.classes['@mozilla.org/xmlextras/domparser;1']
.createInstance(Components.interfaces.nsIDOMParser);
let doc = parser.parseFromString(text, 'text/html');

View file

@ -9,6 +9,9 @@ const parseCitavi5Quads = (quadsRaw) => {
const ImportCitaviAnnotatons = async (translation) => {
const IDMap = translation._itemSaver._IDMap;
const ZU = translation._sandboxZotero.Utilities;
// stream might be closed by now, re-init to make sure getXML() works
translation._io.init('xml/dom');
const doc = translation._sandboxZotero.getXML();
const isCitavi5 = ZU.xpathText(doc, '//CitaviExchangeData/@Version').startsWith('5');
var annotationNodes = ZU.xpath(doc, '//Annotations/Annotation');
@ -29,17 +32,29 @@ const ImportCitaviAnnotatons = async (translation) => {
const stageProgress = 50;
let progress = baseProgress;
translation.getProgress = () => progress;
for (var i = 0, n = annotationNodes.length; i < n; i++) {
const id = ZU.xpathText(annotationNodes[i], '@id');
const quadsRaw = ZU.xpathText(annotationNodes[i], './Quads');
const locationID = ZU.xpathText(annotationNodes[i], './LocationID');
const location = ZU.xpath(doc, `//Locations/Location[@${isCitavi5 ? 'ID' : 'id'}='${locationID}']`)[0];
const location = ZU.xpath(doc, `//Locations/Location[@id='${locationID}']|//Locations/Location[@ID='${locationID}']`)[0];
if (!location) {
Zotero.debug(`Missing <Location> entry for annotation ${id}, skipping...`);
continue;
}
const referenceID = ZU.xpathText(location, './ReferenceID');
const entityLink = ZU.xpath(doc, `//EntityLinks/EntityLink[TargetID='${id}']`)[0];
if (!entityLink) {
Zotero.debug(`Missing <EntityLink> entry for annotation ${id}, skipping...`);
continue;
}
const entitySourceID = ZU.xpathText(entityLink, './SourceID');
const knowledgeItem = ZU.xpath(doc, `//KnowledgeItems/KnowledgeItem[@${isCitavi5 ? 'ID' : 'id'}='${entitySourceID}']`)[0];
const knowledgeItem = ZU.xpath(doc, `//KnowledgeItems/KnowledgeItem[@id='${entitySourceID}']|//KnowledgeItems/KnowledgeItem[@ID='${entitySourceID}']`)[0];
const keywordsIDsText = ZU.xpathText(doc, `//KnowledgeItemKeywords/OnetoN[starts-with(text(), "${entitySourceID}")]`);
const keywords = keywordsIDsText
? keywordsIDsText
@ -134,16 +149,22 @@ const ImportCitaviAnnotatons = async (translation) => {
annotations.push(annotation);
});
annotations = await Zotero.PDFWorker.processCitaviAnnotations(
itemAttachment.getFilePath(), annotations
);
annotations.forEach((annotation) => {
promises.push(Zotero.Annotations.saveFromJSON(
itemAttachment, annotation, { skipSelect: true }
));
});
try {
// eslint-disable-next-line no-await-in-loop
annotations = await Zotero.PDFWorker.processCitaviAnnotations(
itemAttachment.getFilePath(), annotations
);
annotations.forEach((annotation) => {
promises.push(Zotero.Annotations.saveFromJSON(
itemAttachment, annotation, { skipSelect: true }
));
});
}
catch (e) {
Zotero.debug(`Could not process annotations for attachment item ${itemAttachment.key} (file path: ${itemAttachment.getFilePath()})`);
}
progress = baseProgress + Math.ceil((i / annotationNodes.length) * stageProgress);
translation._runHandler('itemDone', []);

View file

@ -57,7 +57,12 @@ var Zotero_Import_Wizard = {
Zotero.Translators.init(); // async
},
onCancel: function () {
if (this._translation && this._translation.interrupt) {
this._translation.interrupt();
}
},
onModeChosen: async function () {
var wizard = this._wizard;

View file

@ -10,6 +10,7 @@
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
title="&zotero.import;"
onwizardcancel="Zotero_Import_Wizard.onCancel()"
onload="Zotero_Import_Wizard.init()">
<script src="../include.js"/>

View file

@ -33,7 +33,7 @@ const getNextLinkFromResponse = (response) => {
const apiFetchUrl = async (tokens, url, headers = {}, options = {}) => {
headers = { ...headers, Authorization: `Bearer ${tokens.access_token}` };
options = { ...options, headers };
options = { ...options, headers, timeout: 60000 };
const method = 'GET';
// Run the request. If we see 401 or 403, try to refresh tokens and run the request again
@ -64,16 +64,18 @@ const get = async (tokens, endPoint, params = {}, headers = {}, options = {}) =>
return JSON.parse(response.responseText);
};
const getAll = async (tokens, endPoint, params = {}, headers = {}, options = {}) => {
const getAll = async (tokens, endPoint, params = {}, headers = {}, options = {}, interruptChecker = () => {}) => {
const PER_PAGE = endPoint === 'annotations' ? 200 : 500;
const response = await apiFetch(tokens, endPoint, { ...params, limit: PER_PAGE }, headers, options);
var next = getNextLinkFromResponse(response);
var data = JSON.parse(response.responseText);
interruptChecker();
while (next) {
const response = await apiFetchUrl(tokens, next, headers, options); //eslint-disable-line no-await-in-loop
data = [...data, ...JSON.parse(response.responseText)];
next = getNextLinkFromResponse(response);
interruptChecker();
}
return data;

View file

@ -1,15 +1,29 @@
/* global map:false, mendeleyOnlineMappings:false, mendeleyAPIUtils:false */
var EXPORTED_SYMBOLS = ["Zotero_Import_Mendeley"];
/* eslint-disable no-await-in-loop, camelcase */
/* global mendeleyDBMaps:false, mendeleyOnlineMappings:false, mendeleyAPIUtils:false */
var EXPORTED_SYMBOLS = ["Zotero_Import_Mendeley"]; //eslint-disable-line no-unused-vars
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/osfile.jsm");
Services.scriptloader.loadSubScript("chrome://zotero/content/include.js");
Services.scriptloader.loadSubScript("chrome://zotero/content/import/mendeley/mendeleyOnlineMappings.js");
Services.scriptloader.loadSubScript("chrome://zotero/content/import/mendeley/mendeleyAPIUtils.js");
Services.scriptloader.loadSubScript("chrome://zotero/content/import/mendeley/mendeleySchemaMap.js");
const { apiTypeToDBType, apiFieldToDBField } = mendeleyOnlineMappings;
const { apiFetch, get, getAll, getTokens } = mendeleyAPIUtils;
const colorMap = new Map();
colorMap.set('rgb(255, 245, 173)', '#ffd400');
colorMap.set('#fff5ad', '#ffd400');
colorMap.set('rgb(255, 181, 182)', '#ff6666');
colorMap.set('#ffb5b6', '#ff6666');
colorMap.set('rgb(186, 226, 255)', '#2ea8e5');
colorMap.set('#bae2ff', '#2ea8e5');
colorMap.set('rgb(211, 194, 255)', '#a28ae5');
colorMap.set('#d3c2ff', '#a28ae5');
colorMap.set('rgb(220, 255, 176)', '#5fb236');
colorMap.set('#dcffb0', '#5fb236');
var Zotero_Import_Mendeley = function () {
this.createNewCollection = null;
this.linkFiles = null;
@ -17,13 +31,32 @@ var Zotero_Import_Mendeley = function () {
this.mendeleyCode = null;
this._tokens = null;
this._db;
this._file;
this._db = null;
this._file = null;
this._saveOptions = null;
this._itemDone;
this._itemDone = () => {};
this._progress = 0;
this._progressMax;
this._progressMax = 0;
this._tmpFilesToDelete = [];
this._caller = null;
this._interrupted = false;
this._totalSize = 0;
this._started = Date.now();
this._interruptChecker = (tickProgress = false) => {
if (this._interrupted) {
throw new Error(`Mendeley import interrupted!
Started: ${this._started} (${Math.round((Date.now() - this._started) / 1000)}s ago)
Progress: ${this._progress} / ${this._progressMax}
New items created: ${this.newItems.length}
Total size of files to download: ${Math.round(this._totalSize / 1024)}KB
`);
}
if (tickProgress) {
this._progress++;
this._itemDone();
}
};
};
Zotero_Import_Mendeley.prototype.setLocation = function (file) {
@ -32,9 +65,9 @@ Zotero_Import_Mendeley.prototype.setLocation = function (file) {
Zotero_Import_Mendeley.prototype.setHandler = function (name, handler) {
switch (name) {
case 'itemDone':
this._itemDone = handler;
break;
case 'itemDone':
this._itemDone = handler;
break;
}
};
@ -57,34 +90,16 @@ Zotero_Import_Mendeley.prototype.translate = async function (options = {}) {
...(options.saveOptions || {})
};
if (true) {
Services.scriptloader.loadSubScript("chrome://zotero/content/import/mendeley/mendeleySchemaMap.js");
}
// TEMP: Load uncached from ~/zotero-client for development
else {
Components.utils.import("resource://gre/modules/FileUtils.jsm");
let file = FileUtils.getDir("Home", []);
file = OS.Path.join(file.path, 'zotero-client', 'chrome', 'content', 'zotero', 'import', 'mendeley', 'mendeleySchemaMap.js');
let fileURI = OS.Path.toFileURI(file);
let xmlhttp = await Zotero.HTTP.request(
'GET',
fileURI,
{
noCache: true,
responseType: 'text'
}
);
eval(xmlhttp.response);
}
const libraryID = options.libraryID || Zotero.Libraries.userLibraryID;
const { key: rootCollectionKey } = options.collections
? Zotero.Collections.getLibraryAndKeyFromID(options.collections[0])
: {};
Zotero.debug(`Begining Mendeley import at ${this._started}. libraryID: ${libraryID}, linkFiles: ${this.linkFiles}, rootCollectionKey: ${rootCollectionKey}`);
// TODO: Get appropriate version based on schema version
const mapVersion = 83;
map = map[mapVersion];
const map = mendeleyDBMaps[mapVersion];
const mendeleyGroupID = 0;
@ -94,7 +109,7 @@ Zotero_Import_Mendeley.prototype.translate = async function (options = {}) {
if (this._file) {
this._db = new Zotero.DBConnection(this._file);
}
try {
if (this._file && !await this._isValidDatabase()) {
throw new Error("Not a valid Mendeley database");
@ -108,14 +123,22 @@ Zotero_Import_Mendeley.prototype.translate = async function (options = {}) {
throw new Error("Missing import token");
}
// we don't know how long the import will be but want to show progress to give
// feedback that import has started so we arbitrary set progress at 2%
this._progress = 1;
this._progressMax = 50;
this._itemDone();
const folders = this._tokens
? await this._getFoldersAPI(mendeleyGroupID)
: await this._getFoldersDB(mendeleyGroupID);
const collectionJSON = this._foldersToAPIJSON(folders, rootCollectionKey);
const folderKeys = this._getFolderKeys(collectionJSON);
await this._saveCollections(libraryID, collectionJSON, folderKeys);
this._interruptChecker(true);
//
// Items
//
@ -123,40 +146,59 @@ Zotero_Import_Mendeley.prototype.translate = async function (options = {}) {
? await this._getDocumentsAPI(mendeleyGroupID)
: await this._getDocumentsDB(mendeleyGroupID);
this._progressMax = documents.length;
// Update progress to reflect items to import and remaining meta data stages
// We arbitrary set progress at approx 4%. We then add 8, one "tick" for each remaining meta data download.
this._progress = Math.max(Math.floor(0.04 * documents.length), 2);
this._progressMax = documents.length + this._progress + 8;
// Get various attributes mapped to document ids
let urls = this._tokens
? await this._getDocumentURLsAPI(documents)
: await this._getDocumentURLsDB(mendeleyGroupID);
this._interruptChecker(true);
let creators = this._tokens
? await this._getDocumentCreatorsAPI(documents)
: await this._getDocumentCreatorsDB(mendeleyGroupID, map.creatorTypes);
this._interruptChecker(true);
let tags = this._tokens
? await this._getDocumentTagsAPI(documents)
: await this._getDocumentTagsDB(mendeleyGroupID);
this._interruptChecker(true);
let collections = this._tokens
? await this._getDocumentCollectionsAPI(documents, rootCollectionKey, folderKeys)
: await this._getDocumentCollectionsDB(mendeleyGroupID, documents, rootCollectionKey, folderKeys);
this._interruptChecker(true);
let files = this._tokens
? await this._getDocumentFilesAPI(documents)
: await this._getDocumentFilesDB(mendeleyGroupID);
this._interruptChecker(true);
let annotations = this._tokens
? await this._getDocumentAnnotationsAPI(mendeleyGroupID)
: await this._getDocumentAnnotationsDB(mendeleyGroupID);
this._interruptChecker(true);
let profile = this._tokens
? await this._getProfileAPI()
: await this._getProfileDB();
this._interruptChecker(true);
let groups = this._tokens
? await this._getGroupsAPI()
: await this._getGroupsDB();
this._interruptChecker(true);
const fileHashLookup = new Map();
for (let [documentID, fileEntries] of files) {
@ -165,7 +207,6 @@ Zotero_Import_Mendeley.prototype.translate = async function (options = {}) {
}
}
for (let group of groups) {
let groupAnnotations = this._tokens
? await this._getDocumentAnnotationsAPI(group.id, profile.id)
@ -263,11 +304,12 @@ Zotero_Import_Mendeley.prototype.translate = async function (options = {}) {
);
}
this.newItems.push(Zotero.Items.get(documentIDMap.get(document.id)));
this._progress++;
if (this._itemDone) {
this._itemDone();
}
this._interruptChecker(true);
}
Zotero.debug(`Completed Mendeley import in ${Math.round((Date.now() - this._started) / 1000)}s. (Started: ${this._started})`);
}
catch (e) {
Zotero.logError(e);
}
finally {
try {
@ -275,6 +317,7 @@ Zotero_Import_Mendeley.prototype.translate = async function (options = {}) {
await this._db.closeDatabase();
}
if (this._tokens) {
Zotero.debug(`Clearing ${this._tmpFilesToDelete.length} temporary files after Mendeley import`);
await Promise.all(
this._tmpFilesToDelete.map(f => this._removeTemporaryFile(f))
);
@ -288,14 +331,20 @@ Zotero_Import_Mendeley.prototype.translate = async function (options = {}) {
}
};
Zotero_Import_Mendeley.prototype.interrupt = function () {
this._interrupted = true;
if (this._caller) {
this._caller.stop();
}
};
Zotero_Import_Mendeley.prototype._removeTemporaryFile = async function (file) {
const containingDir = OS.Path.dirname(file);
try {
await Zotero.File.removeIfExists(file);
await OS.File.removeEmptyDir(containingDir);
}
catch (e) {
Zotero.logError(e);
Zotero.logError("Error while removing temporary file " + file + ": " + e);
}
};
@ -345,7 +394,7 @@ Zotero_Import_Mendeley.prototype._getFoldersAPI = async function (groupID) {
if (groupID && groupID !== 0) {
params.group_id = groupID; //eslint-disable-line camelcase
}
return (await getAll(this._tokens, 'folders', params, headers)).map(f => ({
return (await getAll(this._tokens, 'folders', params, headers, {}, this._interruptChecker)).map(f => ({
id: f.id,
uuid: f.id,
name: f.name,
@ -365,11 +414,11 @@ Zotero_Import_Mendeley.prototype._foldersToAPIJSON = function (folderRows, paren
};
Zotero_Import_Mendeley.prototype._getFolderDescendents = function (folderID, folderKey, folderRows, maxDepth) {
if (maxDepth == 0) return []
if (maxDepth == 0) return [];
var descendents = [];
var children = folderRows
.filter(f => f.parentId == folderID)
.map(f => {
.map((f) => {
let c = {
folderID: f.id,
remoteUUID: f.remoteUuid,
@ -492,7 +541,7 @@ Zotero_Import_Mendeley.prototype._getDocumentsAPI = async function (groupID) {
}
return (await getAll(this._tokens, 'documents', params, headers)).map(d => ({
return (await getAll(this._tokens, 'documents', params, headers, {}, this._interruptChecker)).map(d => ({
...d,
uuid: d.id,
remoteUuid: d.id
@ -693,12 +742,13 @@ Zotero_Import_Mendeley.prototype._fetchFile = async function (fileID, filePath)
Zotero_Import_Mendeley.prototype._getDocumentFilesAPI = async function (documents) {
const map = new Map();
let totalSize = 0;
this._totalSize = 0;
Components.utils.import("resource://zotero/concurrentCaller.js");
var caller = new ConcurrentCaller({
this._caller = new ConcurrentCaller({
numConcurrent: 6,
onError: e => Zotero.logError(e),
logger: Zotero.debug,
Promise: Zotero.Promise
});
@ -712,7 +762,7 @@ Zotero_Import_Mendeley.prototype._getDocumentFilesAPI = async function (document
let tmpFile = OS.Path.join(Zotero.getTempDirectory().path, `m-api-${file.id}.${ext}`);
this._tmpFilesToDelete.push(tmpFile);
caller.add(this._fetchFile.bind(this, file.id, tmpFile));
this._caller.add(this._fetchFile.bind(this, file.id, tmpFile));
files.push({
fileURL: OS.Path.toFileURI(tmpFile),
title: file.file_name || '',
@ -720,13 +770,14 @@ Zotero_Import_Mendeley.prototype._getDocumentFilesAPI = async function (document
hash: file.filehash,
fileBaseName
});
totalSize += file.size;
this._totalSize += file.size;
this._progressMax += 1;
}
map.set(doc.id, files);
}
// TODO: check if enough space available totalSize
await caller.runAll();
await this._caller.runAll();
this._caller = null;
return map;
};
@ -823,7 +874,7 @@ Zotero_Import_Mendeley.prototype._getDocumentAnnotationsAPI = async function (gr
}
const map = new Map();
(await getAll(this._tokens, 'annotations', params, { Accept: 'application/vnd.mendeley-annotation.1+json' }))
(await getAll(this._tokens, 'annotations', params, { Accept: 'application/vnd.mendeley-annotation.1+json' }, {}, this._interruptChecker))
.forEach((a) => {
if (profileID !== null && a.profile_id !== profileID) {
// optionally filter annotations by profile id
@ -885,7 +936,7 @@ Zotero_Import_Mendeley.prototype._getGroupsAPI = async function () {
const params = { type: 'all' };
const headers = { Accept: 'application/vnd.mendeley-group-list+json' };
return getAll(this._tokens, 'groups/v2', params, headers);
return getAll(this._tokens, 'groups/v2', params, headers, {}, this._interruptChecker);
};
Zotero_Import_Mendeley.prototype._getGroupsDB = async function () {
@ -985,14 +1036,14 @@ Zotero_Import_Mendeley.prototype._documentToAPIJSON = async function (map, docum
for (let field in parent) {
switch (field) {
case 'itemType':
case 'key':
case 'parentItem':
case 'note':
case 'creators':
case 'dateAdded':
case 'dateModified':
continue;
case 'itemType':
case 'key':
case 'parentItem':
case 'note':
case 'creators':
case 'dateAdded':
case 'dateModified':
continue;
}
// Move unknown/invalid fields to Extra
@ -1035,7 +1086,7 @@ Zotero_Import_Mendeley.prototype._documentToAPIJSON = async function (map, docum
// seriesEditor isn't valid on some item types (e.g., book)
if (creator.creatorType == 'seriesEditor'
&& !Zotero.CreatorTypes.isValidForItemType(
Zotero.CreatorTypes.getID('seriesEditor'), itemTypeID)) {
Zotero.CreatorTypes.getID('seriesEditor'), itemTypeID)) {
creator.creatorType = 'editor';
}
}
@ -1160,7 +1211,6 @@ Zotero_Import_Mendeley.prototype._processField = function (parent, children, zFi
}
else {
Zotero.warn(`Unknown function subfield: ${subfield}`);
return;
}
}
else {
@ -1191,20 +1241,21 @@ Zotero_Import_Mendeley.prototype._makeCreator = function (creatorType, firstName
Zotero_Import_Mendeley.prototype._addExtraField = function (extra, field, val) {
// Strip the field if it appears at the beginning of the value (to avoid "DOI: DOI: 10...")
if (typeof val == 'string') {
val = val.replace(new RegExp(`^${field}:\s*`, 'i'), "");
val = val.replace(new RegExp(`^${field}:\\s*`, 'i'), "");
}
extra = extra ? extra + '\n' : '';
if (field != 'arXiv') {
field = field[0].toUpperCase() + field.substr(1);
field = field.replace(/([a-z])([A-Z][a-z])/, "$1 $2");
}
return extra + `${field}: ${val}`;
};
Zotero_Import_Mendeley.prototype._convertNote = function (note) {
return note
// Add newlines after <br>
.replace(/<br\s*\/>/g, '<br\/>\n')
.replace(/<br\s*\/>/g, '<br/>\n')
//
// Legacy pre-HTML stuff
//
@ -1360,7 +1411,7 @@ Zotero_Import_Mendeley.prototype._findExistingItem = async function (libraryID,
Zotero.debug(`Found existing item ${existingItem.libraryKey} for `
+ `${predicate} ${itemJSON.relations[predicate]}`);
return existingItem;
}
};
Zotero_Import_Mendeley.prototype._getItemByRelation = async function (libraryID, predicate, object) {
@ -1443,7 +1494,7 @@ Zotero_Import_Mendeley.prototype._saveFilesAndAnnotations = async function (file
Zotero.logError(e);
}
}
}
};
Zotero_Import_Mendeley.prototype._findExistingFile = function (parentItemID, file) {
@ -1460,7 +1511,7 @@ Zotero_Import_Mendeley.prototype._findExistingFile = function (parentItemID, fil
}
}
return false;
}
};
Zotero_Import_Mendeley.prototype._isDownloadedFile = function (path) {
var parentDir = OS.Path.dirname(path);
@ -1468,7 +1519,7 @@ Zotero_Import_Mendeley.prototype._isDownloadedFile = function (path) {
|| parentDir.endsWith(OS.Path.join('Local', 'Mendeley Ltd', 'Mendeley Desktop', 'Downloaded'))
|| parentDir.endsWith(OS.Path.join('Local', 'Mendeley Ltd.', 'Mendeley Desktop', 'Downloaded'))
|| parentDir.endsWith(OS.Path.join('data', 'Mendeley Ltd.', 'Mendeley Desktop', 'Downloaded'));
}
};
Zotero_Import_Mendeley.prototype._isTempDownloadedFile = function (path) {
return path.startsWith(OS.Path.join(Zotero.getTempDirectory().path, 'm-api'));
@ -1498,7 +1549,7 @@ Zotero_Import_Mendeley.prototype._getRealFilePath = async function (path) {
return altPath;
}
return false;
}
};
Zotero_Import_Mendeley.prototype._saveAnnotations = async function (annotations, parentItemID, attachmentItemID, fileHash) {
if (!annotations.length) return;
@ -1507,7 +1558,6 @@ Zotero_Import_Mendeley.prototype._saveAnnotations = async function (annotations,
var libraryID = parentItem.libraryID;
if (attachmentItemID) {
var attachmentItem = Zotero.Items.get(attachmentItemID);
var attachmentURIPath = Zotero.API.getLibraryPrefix(libraryID) + '/items/' + attachmentItem.key;
if (attachmentItem) {
let file = await attachmentItem.getFilePathAsync();
@ -1530,6 +1580,10 @@ Zotero_Import_Mendeley.prototype._saveAnnotations = async function (annotations,
}
// PDFWorker needs 'id'
annotations.forEach(annotation => annotation.id = annotation.uuid);
annotations.forEach(annotation => (colorMap.has(annotation.color)
? annotation.color = colorMap.get(annotation.color)
: annotation.color
));
// Returns 'id', 'position', 'type', 'pageLabel', 'sortIndex', 'text' (for highlight)
Zotero.debug("Processing annotations in " + file);
annotations = await Zotero.PDFWorker.processMendeleyAnnotations(file, annotations);
@ -1616,7 +1670,7 @@ Zotero_Import_Mendeley.prototype._saveAnnotations = async function (annotations,
});
}
note.setNote('<h1>' + Zotero.getString('extractedAnnotations') + '</h1>\n' + noteStrings.join('\n'));
return note.saveTx(this._saveOptions);
await note.saveTx(this._saveOptions);
};
@ -1633,17 +1687,7 @@ Zotero_Import_Mendeley.prototype._updateParentKeys = function (objectType, json,
break;
}
}
}
Zotero_Import_Mendeley.prototype._updateItemCollectionKeys = function (json, oldKey, newKey) {
for (; i < json.length; i++) {
let x = json[i];
if (x[prop] == oldKey) {
x[prop] = newKey;
}
}
}
};
//
// Clean up extra files created <5.0.51

View file

@ -1,4 +1,5 @@
var map = {
// eslint-disable-next-line no-unused-vars
var mendeleyDBMaps = {
83: {
itemTypes: {
Bill: "bill",

View file

@ -113,9 +113,10 @@ var Zotero_Citation_Dialog = new function () {
var menu = document.getElementById("label");
var label_list = document.getElementById("locator-type-popup");
var i = 0;
var pageLocatorIndex;
for(var value in locators) {
var locator = locators[value];
var locatorLabel = Zotero.getString('citation.locator.'+locator.replace(/\s/g,''));
let locatorLabel = Zotero.Cite.getLocatorString(locator);
// add to list of labels
var child = document.createElement("menuitem");
child.setAttribute("value", value);
@ -124,9 +125,13 @@ var Zotero_Citation_Dialog = new function () {
// add to array
_locatorIndexArray[locator] = i;
_locatorNameArray[i] = locator;
if (locator == 'page') {
pageLocatorIndex = i;
_preserveData.label[1] = i;
}
i++;
}
menu.selectedIndex = 0;
menu.selectedIndex = pageLocatorIndex;
// load (from selectItemsDialog.js)
yield doLoad();

View file

@ -116,10 +116,9 @@ var Zotero_QuickFormat = new function () {
// add labels to popup
var locators = Zotero.Cite.labels;
var menu = document.getElementById("locator-label");
var labelList = document.getElementById("locator-label-popup");
for(var locator of locators) {
var locatorLabel = Zotero.getString('citation.locator.'+locator.replace(/\s/g,''));
let locatorLabel = Zotero.Cite.getLocatorString(locator);
// add to list of labels
var child = document.createElement("menuitem");
@ -127,7 +126,6 @@ var Zotero_QuickFormat = new function () {
child.setAttribute("label", locatorLabel);
labelList.appendChild(child);
}
menu.selectedIndex = 0;
}
@ -729,9 +727,7 @@ var Zotero_QuickFormat = new function () {
// Title, if no creator (getDisplayTitle in order to get case, e-mail, statute which don't have a title field)
title = item.getDisplayTitle();
if (item.isNote()) {
title = title.substr(0, 24) + '…';
}
title = title.substr(0, 32) + (title.length > 32 ? "…" : "");
if (!str) {
str = Zotero.getString("punctuation.openingQMark") + title + Zotero.getString("punctuation.closingQMark");
}
@ -758,15 +754,17 @@ var Zotero_QuickFormat = new function () {
// Prefix
if(citationItem.prefix && Zotero.CiteProc.CSL.ENDSWITH_ROMANESQUE_REGEXP) {
str = citationItem.prefix
let prefix = citationItem.prefix.substr(0, 10) + (citationItem.prefix.length > 10 ? "…" : "")
str = prefix
+(Zotero.CiteProc.CSL.ENDSWITH_ROMANESQUE_REGEXP.test(citationItem.prefix) ? " " : "")
+str;
}
// Suffix
if(citationItem.suffix && Zotero.CiteProc.CSL.STARTSWITH_ROMANESQUE_REGEXP) {
let suffix = citationItem.suffix.substr(0, 10) + (citationItem.suffix.length > 10 ? "…" : "")
str += (Zotero.CiteProc.CSL.STARTSWITH_ROMANESQUE_REGEXP.test(citationItem.suffix) ? " " : "")
+citationItem.suffix;
+ suffix;
}
return str;
@ -778,9 +776,6 @@ var Zotero_QuickFormat = new function () {
function _insertBubble(citationItem, nextNode) {
var str = _buildBubbleString(citationItem);
// It's entirely unintuitive why, but after trying a bunch of things, it looks like using
// a XUL label for these things works best. A regular span causes issues with moving the
// cursor.
var bubble = qfiDocument.createElement("span");
bubble.setAttribute("class", "citation-dialog bubble");
bubble.setAttribute("draggable", "true");
@ -1085,15 +1080,16 @@ var Zotero_QuickFormat = new function () {
let citationItem = JSON.parse(target.dataset.citationItem);
panelPrefix.value = citationItem["prefix"] ? citationItem["prefix"] : "";
panelSuffix.value = citationItem["suffix"] ? citationItem["suffix"] : "";
var pageOption = panelLocatorLabel.getElementsByAttribute("value", "page")[0];
if(citationItem["label"]) {
var option = panelLocatorLabel.getElementsByAttribute("value", citationItem["label"]);
if(option.length) {
panelLocatorLabel.selectedItem = option[0];
} else {
panelLocatorLabel.selectedIndex = 0;
panelLocatorLabel.selectedItem = pageOption;
}
} else {
panelLocatorLabel.selectedIndex = 0;
panelLocatorLabel.selectedItem = pageOption;
}
panelLocator.value = citationItem["locator"] ? citationItem["locator"] : "";
panelSuppressAuthor.checked = !!citationItem["suppress-author"];

View file

@ -38,9 +38,8 @@ const { Cc, Ci, Cu } = require('chrome');
Cu.import("resource://gre/modules/osfile.jsm");
const CHILD_INDENT = 12;
const COLORED_TAGS_RE = new RegExp("^[0-" + Zotero.Tags.MAX_COLORED_TAGS + "]{1}$");
const COLORED_TAGS_RE = new RegExp("^(?:Numpad|Digit)([0-" + Zotero.Tags.MAX_COLORED_TAGS + "]{1})$");
const COLUMN_PREFS_FILEPATH = OS.Path.join(Zotero.Profile.dir, "treePrefs.json");
const EMOJI_RE = /\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/gu;
const HTML_NS = "http://www.w3.org/1999/xhtml";
const ATTACHMENT_STATE_LOAD_DELAY = 150; //ms
@ -448,11 +447,20 @@ var ItemTree = class ItemTree extends LibraryTree {
let row = this.getRowIndexByID(id);
if (row === false) continue;
let item = Zotero.Items.get(id);
if (!item.deleted && !item.numChildren()) {
// Remove parent row if it isn't deleted and doesn't have any deleted children
// (shown by the numChildren including deleted being the same as numChildren not including deleted)
if (!item.deleted && (!item.isRegularItem() || item.numChildren(true) == item.numChildren(false))) {
rows.push(row);
// And all its children in the tree
for (let child = row + 1; child < this.rowCount && this.getLevel(child) > this.getLevel(row); child++) {
rows.push(child);
}
}
}
this._removeRows(rows);
if (rows.length) {
this._removeRows(rows);
this.tree.invalidate();
}
}
return;
@ -475,14 +483,17 @@ var ItemTree = class ItemTree extends LibraryTree {
return;
}
var visibleSubcollections = Zotero.Prefs.get('recursiveCollections')
? collectionTreeRow.ref.getDescendents(false, 'collection')
: [];
var splitIDs = [];
for (let id of ids) {
var split = id.split('-');
// Skip if not an item in this collection
if (split[0] != collectionTreeRow.ref.id) {
continue;
// Include if an item in this collection or a visible subcollection
if (split[0] == collectionTreeRow.ref.id
|| visibleSubcollections.some(c => split[0] == c.id)) {
splitIDs.push(split[1]);
}
splitIDs.push(split[1]);
}
ids = splitIDs;
}
@ -859,9 +870,9 @@ var ItemTree = class ItemTree extends LibraryTree {
}
return false;
}
if (!event.shiftKey && !event.ctrlKey && !event.metaKey && !event.altKey && COLORED_TAGS_RE.test(event.key)) {
if (!event.shiftKey && !event.ctrlKey && !event.metaKey && !event.altKey && COLORED_TAGS_RE.test(event.code)) {
let libraryID = this.collectionTreeRow.ref.libraryID;
let position = parseInt(event.key) - 1;
let position = COLORED_TAGS_RE.exec(event.code)[1] - 1;
// When 0 is pressed, remove all colored tags
if (position == -1) {
let items = this.getSelectedItems();
@ -978,7 +989,7 @@ var ItemTree = class ItemTree extends LibraryTree {
onItemContextMenu: (...args) => this.props.onContextMenu(...args),
role: 'treegrid',
role: 'tree',
label: Zotero.getString('pane.items.title'),
}
);
@ -1241,20 +1252,14 @@ var ItemTree = class ItemTree extends LibraryTree {
? `for ${itemIDs.length} ` + Zotero.Utilities.pluralize(itemIDs.length, ['item', 'items'])
: ""));
// Set whether rows with empty values should be displayed last,
// which may be different for primary and secondary sorting.
var emptyFirst = {};
switch (primaryField) {
case 'title':
emptyFirst.title = true;
break;
// When sorting by title we want empty titles at the top, but if not
// sorting by title, empty titles should sort to the bottom so that new
// empty items don't get sorted to the middle of the items list.
default:
emptyFirst.title = false;
}
// Set whether rows with empty values should sort at the beginning
var emptyFirst = {
title: true,
// Date columns start descending, so put empty rows at end
date: true,
year: true,
};
// Cache primary values while sorting, since base-field-mapped getField()
// calls are relatively expensive
@ -1262,7 +1267,7 @@ var ItemTree = class ItemTree extends LibraryTree {
sortFields.forEach(x => cache[x] = {});
// Get the display field for a row (which might be a placeholder title)
function getField(field, row) {
let getField = (field, row) => {
var item = row.ref;
switch (field) {
@ -1270,23 +1275,12 @@ var ItemTree = class ItemTree extends LibraryTree {
return Zotero.Items.getSortTitle(item.getDisplayTitle());
case 'hasAttachment':
if (item.isFileAttachment()) {
var state = item.fileExistsCached() ? 1 : -1;
}
else if (item.isRegularItem()) {
var state = item.getBestAttachmentStateCached();
if (this._canGetBestAttachmentState(item)) {
return item.getBestAttachmentStateCached();
}
else {
return 0;
}
// Make sort order present, missing, empty when ascending
if (state === 1) {
state = 2;
}
else if (state === -1) {
state = 1;
}
return state;
case 'numNotes':
return row.numNotes(false, true) || 0;
@ -1350,7 +1344,11 @@ var ItemTree = class ItemTree extends LibraryTree {
}
if (sortField == 'hasAttachment') {
return fieldB - fieldA;
// PDFs at the top
const order = ['pdf', 'snapshot', 'other', 'none'];
fieldA = order.indexOf(fieldA.type || 'none') + (fieldA.exists ? 0 : 4);
fieldB = order.indexOf(fieldB.type || 'none') + (fieldB.exists ? 0 : 4);
return fieldA - fieldB;
}
if (sortField == 'callNumber') {
@ -1439,18 +1437,27 @@ var ItemTree = class ItemTree extends LibraryTree {
var openItemIDs = this._saveOpenState(true);
// Sort specific items
if (itemIDs) {
let idsToSort = new Set(itemIDs);
this._rows.sort((a, b) => {
// Don't re-sort existing items. This assumes a stable sort(), which is the case in Firefox
// but not Chrome/v8.
if (!idsToSort.has(a.ref.id) && !idsToSort.has(b.ref.id)) return 0;
return rowSort(a, b) * order;
});
try {
if (itemIDs) {
let idsToSort = new Set(itemIDs);
this._rows.sort((a, b) => {
// Don't re-sort existing items. This assumes a stable sort(), which is the case in Firefox
// but not Chrome/v8.
if (!idsToSort.has(a.ref.id) && !idsToSort.has(b.ref.id)) return 0;
return rowSort(a, b) * order;
});
}
// Full sort
else {
this._rows.sort((a, b) => rowSort(a, b) * order);
}
}
// Full sort
else {
this._rows.sort((a, b) => rowSort(a, b) * order);
catch (e) {
Zotero.logError("Error sorting fields: " + e.message);
Zotero.debug(e, 1);
// Clear anything that might be contributing to the error
Zotero.Prefs.clear('secondarySort.' + this.getSortField());
Zotero.Prefs.clear('fallbackSort');
}
this._refreshRowMap();
@ -1748,8 +1755,21 @@ var ItemTree = class ItemTree extends LibraryTree {
await Zotero.Items.trashTx(ids);
}
else if (collectionTreeRow.isCollection()) {
let collectionIDs = [collectionTreeRow.ref.id];
if (Zotero.Prefs.get('recursiveCollections')) {
collectionIDs.push(...collectionTreeRow.ref.getDescendents(false, 'collection').map(c => c.id));
}
await Zotero.DB.executeTransaction(async () => {
await collectionTreeRow.ref.removeItems(ids);
for (let itemID of ids) {
let item = Zotero.Items.get(itemID);
for (let collectionID of collectionIDs) {
item.removeFromCollection(collectionID);
}
await item.save({
skipDateModifiedUpdate: true
});
}
});
}
else if (collectionTreeRow.isPublications()) {
@ -1788,6 +1808,11 @@ var ItemTree = class ItemTree extends LibraryTree {
return this._rows.map(row => asIDs ? row.ref.id : row.ref);
}
/**
* Get the current sort order of the items list
*
* @return {Number} - -1 for descending, 1 for ascending
*/
getSortDirection(sortFields) {
sortFields = sortFields || this.getSortFields();
if (this.collectionTreeRow.isFeed()) {
@ -1987,8 +2012,8 @@ var ItemTree = class ItemTree extends LibraryTree {
// If exporting with virtual "Markdown + Rich Text" translator, call Note Markdown
// and Note HTML translators instead
if (format.id === Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) {
let markdownFormat = { mode: 'export', id: Zotero.Translators.TRANSLATOR_ID_NOTE_MARKDOWN };
let htmlFormat = { mode: 'export', id: Zotero.Translators.TRANSLATOR_ID_NOTE_HTML };
let markdownFormat = { mode: 'export', id: Zotero.Translators.TRANSLATOR_ID_NOTE_MARKDOWN, options: format.markdownOptions };
let htmlFormat = { mode: 'export', id: Zotero.Translators.TRANSLATOR_ID_NOTE_HTML, options: format.htmlOptions };
Zotero.QuickCopy.getContentFromItems(items, markdownFormat, (obj, worked) => {
if (!worked) {
Zotero.log(Zotero.getString('fileInterface.exportError'), 'warning');
@ -2752,93 +2777,60 @@ var ItemTree = class ItemTree extends LibraryTree {
// TEMP: For now, we use the blue bullet for all non-PDF attachments, but there's
// commented-out code for showing different icons for snapshots, files, and URL/DOI links
if (this.isContainer(index)) {
if (item.isRegularItem()) {
const { type, exists } = item.getBestAttachmentStateCached();
let icon = "";
let ariaLabel;
// If the item has a child attachment
if (type !== null && type != 'none') {
if (type == 'pdf') {
icon = getDOMElement('IconTreeitemAttachmentPDF');
ariaLabel = Zotero.getString('pane.item.attachments.hasPDF');
if (!exists) {
icon.classList.add('icon-missing-file');
}
}
else if (type == 'snapshot') {
//icon = getDOMElement('IconTreeitemAttachmentSnapshot');
icon = exists ? getDOMElement('IconBulletBlue') : getDOMElement('IconBulletBlueEmpty');
ariaLabel = Zotero.getString('pane.item.attachments.hasSnapshot');
}
else {
//icon = getDOMElement('IconTreeitem');
icon = exists ? getDOMElement('IconBulletBlue') : getDOMElement('IconBulletBlueEmpty');
ariaLabel = Zotero.getString('pane.item.attachments.has');
}
icon.classList.add('cell-icon');
//if (!exists) {
// icon.classList.add('icon-missing-file');
//}
}
//else if (type == 'none') {
// if (item.getField('url') || item.getField('DOI')) {
// icon = getDOMElement('IconLink');
// ariaLabel = Zotero.getString('pane.item.attachments.hasLink');
// icon.classList.add('cell-icon');
// }
//}
if (ariaLabel) {
icon.setAttribute('aria-label', ariaLabel + '.');
span.setAttribute('title', ariaLabel);
}
span.append(icon);
// Don't run this immediately since it might cause a db check and disk access
// but delay for some time and see if the item is still visible in the tree
// (i.e. if we haven't scrolled right past it)
setTimeout(() => {
if (!this.tree.rowIsVisible(index)) return;
item.getBestAttachmentState()
// Refresh cell when promise is fulfilled
.then(({ type: newType, exists: newExists }) => {
if (newType !== type || newExists !== exists) {
this.tree.invalidateRow(index);
}
});
}, ATTACHMENT_STATE_LOAD_DELAY);
}
}
if (item.isFileAttachment()) {
const exists = item.fileExistsCached();
if (this._canGetBestAttachmentState(item)) {
const { type, exists } = item.getBestAttachmentStateCached();
let icon = "";
if (exists !== null) {
if (item.isPDFAttachment()) {
let ariaLabel;
// If the item has a child attachment
if (type !== null && type != 'none') {
if (type == 'pdf') {
icon = getDOMElement('IconTreeitemAttachmentPDF');
ariaLabel = Zotero.getString('pane.item.attachments.hasPDF');
if (!exists) {
icon.classList.add('icon-missing-file');
}
}
else if (item.isSnapshotAttachment()) {
else if (type == 'snapshot') {
//icon = getDOMElement('IconTreeitemAttachmentSnapshot');
icon = exists ? getDOMElement('IconBulletBlue') : getDOMElement('IconBulletBlueEmpty');
ariaLabel = Zotero.getString('pane.item.attachments.hasSnapshot');
}
else {
//icon = getDOMElement('IconTreeitem');
icon = exists ? getDOMElement('IconBulletBlue') : getDOMElement('IconBulletBlueEmpty');
ariaLabel = Zotero.getString('pane.item.attachments.has');
}
icon.classList.add('cell-icon');
//if (!exists) {
// icon.classList.add('icon-missing-file');
//}
}
//else if (type == 'none') {
// if (item.getField('url') || item.getField('DOI')) {
// icon = getDOMElement('IconLink');
// ariaLabel = Zotero.getString('pane.item.attachments.hasLink');
// icon.classList.add('cell-icon');
// }
//}
if (ariaLabel) {
icon.setAttribute('aria-label', ariaLabel + '.');
span.setAttribute('title', ariaLabel);
}
span.append(icon);
item.fileExists()
// TODO: With no cell refreshing this is possibly somewhat inefficient
// Refresh cell when promise is fulfilled
.then(realExists => realExists != exists && this.tree.invalidateRow(index));
// Don't run this immediately since it might cause a db check and disk access
// but delay for some time and see if the item is still visible in the tree
// (i.e. if we haven't scrolled right past it)
setTimeout(() => {
if (!this.tree.rowIsVisible(index)) return;
item.getBestAttachmentState()
// Refresh cell when promise is fulfilled
.then(({ type: newType, exists: newExists }) => {
if (newType !== type || newExists !== exists) {
this.tree.invalidateRow(index);
}
});
}, ATTACHMENT_STATE_LOAD_DELAY);
}
return span;
@ -2911,7 +2903,7 @@ var ItemTree = class ItemTree extends LibraryTree {
}
// Accessibility
div.setAttribute('role', 'row');
div.setAttribute('role', 'treeitem');
div.setAttribute('aria-level', this.getLevel(index) + 1);
if (!this.isContainerEmpty(index)) {
div.setAttribute('aria-expanded', this.isContainerOpen(index));
@ -3550,7 +3542,7 @@ var ItemTree = class ItemTree extends LibraryTree {
let t = new Date();
for (let i = 0; i < this._rows.length; i++) {
let item = this.getRow(i).ref;
if (item.isRegularItem()) {
if (this._canGetBestAttachmentState(item)) {
await item.getBestAttachmentState();
}
}
@ -3824,10 +3816,10 @@ var ItemTree = class ItemTree extends LibraryTree {
}
return icon;
}
_isOnlyEmoji(str) {
// Remove emoji and zero-width joiner and see if anything's left
return !str.replace(EMOJI_RE, '').replace(/\u200D/g,'');
_canGetBestAttachmentState(item) {
return (item.isRegularItem() && item.numAttachments())
|| (item.isFileAttachment() && item.isTopLevelItem());
}
_getTagSwatch(tag, color) {
@ -3837,7 +3829,7 @@ var ItemTree = class ItemTree extends LibraryTree {
//
// TODO: Check for a maximum number of graphemes, which is hard to do
// https://stackoverflow.com/a/54369605
if (this._isOnlyEmoji(tag)) {
if (Zotero.Utilities.Internal.isOnlyEmoji(tag)) {
span.textContent = tag;
}
// Otherwise display color

View file

@ -207,6 +207,14 @@ const COLUMNS = [
flex: 1,
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
},
{
dataKey: "number",
disabledIn: "feed",
submenu: true,
label: "itemFields.number",
flex: 1,
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
},
{
dataKey: "pages",
disabledIn: "feed",

@ -1 +1 @@
Subproject commit 4877f181b1a962ea3e26d23f12d1acba3ccce347
Subproject commit cb98d366912a0c0be361880e12fbc43cef6d383e

View file

@ -520,7 +520,13 @@ var Zotero_LocateMenu = new function() {
if(attachment.attachmentLinkMode !== Zotero.Attachments.LINK_MODE_LINKED_URL) {
var path = yield attachment.getFilePathAsync();
if (path) {
var ext = Zotero.File.getExtension(Zotero.File.pathToFile(path));
try {
var ext = Zotero.File.getExtension(Zotero.File.pathToFile(path));
}
catch (e) {
Zotero.logError(e);
return false;
}
if(!attachment.attachmentContentType ||
Zotero.MIME.hasNativeHandler(attachment.attachmentContentType, ext) ||
!Zotero.MIME.hasInternalHandler(attachment.attachmentContentType, ext)) {
@ -589,9 +595,23 @@ var Zotero_LocateMenu = new function() {
this.icon = "chrome://zotero/skin/locate-library-lookup.png";
this.canHandleItem = function (item) { return Zotero.Promise.resolve(item.isRegularItem()); };
this.handleItems = Zotero.Promise.method(function (items, event) {
// If no resolver configured, just switch to the default
// If no resolver configured, show error
if (!Zotero.Prefs.get('openURL.resolver')) {
Zotero.Prefs.clear('openURL.resolver')
let ps = Services.prompt;
let buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL);
let index = ps.confirmEx(
null,
Zotero.getString('locate.libraryLookup.noResolver.title'),
Zotero.getString('locate.libraryLookup.noResolver.text', Zotero.appName),
buttonFlags,
Zotero.getString('general.openPreferences'),
null, null, null, {}
);
if (index == 0) {
Zotero.Utilities.Internal.openPreferences('zotero-prefpane-advanced');
}
return;
}
var urls = [];
for (let item of items) {

View file

@ -27,8 +27,6 @@ Components.utils.import("resource://gre/modules/Services.jsm");
import FilePicker from 'zotero/modules/filePicker';
Zotero_Preferences.Advanced = {
DEFAULT_OPENURL_RESOLVER: 'https://www.worldcat.org/registry/gateway',
_openURLResolvers: null,
init: function () {
@ -462,12 +460,6 @@ Zotero_Preferences.Advanced = {
var menupopup = openURLMenu.firstChild;
menupopup.innerHTML = '';
var defaultMenuItem = document.createElement('menuitem');
defaultMenuItem.setAttribute('label', Zotero.getString('general.default'));
defaultMenuItem.setAttribute('value', this.DEFAULT_OPENURL_RESOLVER);
defaultMenuItem.setAttribute('type', 'checkbox');
menupopup.appendChild(defaultMenuItem);
var customMenuItem = document.createElement('menuitem');
customMenuItem.setAttribute('label', Zotero.getString('general.custom'));
customMenuItem.setAttribute('value', 'custom');
@ -521,13 +513,8 @@ Zotero_Preferences.Advanced = {
}
}
// Default
if (currentResolver == this.DEFAULT_OPENURL_RESOLVER) {
openURLMenu.setAttribute('label', Zotero.getString('general.default'));
defaultMenuItem.setAttribute('checked', true);
Zotero.Prefs.clear('openURL.name');
}
else if (selectedName) {
// From directory
if (selectedName) {
openURLMenu.setAttribute('label', selectedName);
// If we found a match, update stored name
Zotero.Prefs.set('openURL.name', selectedName);
@ -555,15 +542,8 @@ Zotero_Preferences.Advanced = {
var openURLServerField = document.getElementById('openURLServerField');
var openURLVersionMenu = document.getElementById('openURLVersionMenu');
// Default
if (event.target.value == this.DEFAULT_OPENURL_RESOLVER) {
Zotero.Prefs.clear('openURL.name');
Zotero.Prefs.clear('openURL.resolver');
Zotero.Prefs.clear('openURL.version');
openURLServerField.value = this.DEFAULT_OPENURL_RESOLVER;
}
// If "Custom" selected, clear URL field
else if (event.target.value == "custom") {
if (event.target.value == "custom") {
Zotero.Prefs.clear('openURL.name');
Zotero.Prefs.set('openURL.resolver', '');
Zotero.Prefs.clear('openURL.version');

View file

@ -89,12 +89,16 @@ Zotero_Preferences.Export = {
* Builds the note Quick Copy drop-down from the current global pref
*/
populateNoteQuickCopyList: async function () {
document.getElementById('noteQuickCopy-format-options').removeAttribute('hidden');
// Initialize default format drop-down
var format = Zotero.Prefs.get("export.noteQuickCopy.setting");
format = Zotero.QuickCopy.unserializeSetting(format);
var menulist = document.getElementById("zotero-noteQuickCopy-menu");
menulist.setAttribute('preference', "pref-noteQuickCopy-setting");
menulist.removeEventListener('command', this.updateNoteQuickCopyUI);
menulist.addEventListener('command', this.updateNoteQuickCopyUI);
if (!format) {
format = menulist.value;
}
@ -149,11 +153,42 @@ Zotero_Preferences.Export = {
return;
}
var val = JSON.stringify({ mode: 'export', id: translator.translatorID });
var value = { mode: 'export', id: translator.translatorID };
if (translator.translatorID == format.id) {
value = format;
}
else if (translator.translatorID == Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) {
value = {
mode: 'export',
id: translator.translatorID,
markdownOptions: {
includeAppLinks: true
},
htmlOptions: {
includeAppLinks: false
}
};
if (format.id == Zotero.Translators.TRANSLATOR_ID_NOTE_HTML && format.options) {
value.htmlOptions = format.options;
}
}
else if (translator.translatorID == Zotero.Translators.TRANSLATOR_ID_NOTE_HTML) {
value = {
mode: 'export',
id: translator.translatorID,
options: {
includeAppLinks: false
}
};
if (format.id == Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT && format.htmlOptions) {
value.options = format.htmlOptions;
}
}
value = JSON.stringify(value);
var itemNode = document.createElement('menuitem');
itemNode.setAttribute('value', val);
itemNode.setAttribute('value', value);
itemNode.setAttribute('label', translator.label);
// itemNode.setAttribute('oncommand', 'Zotero_Preferences.Export.updateQuickCopyUI()');
popup.appendChild(itemNode);
if (format.mode == 'export' && format.id == translator.translatorID) {
@ -162,6 +197,76 @@ Zotero_Preferences.Export = {
});
menulist.click();
this.updateNoteQuickCopyUI();
},
updateNoteQuickCopyUI: () => {
var format = document.getElementById('zotero-noteQuickCopy-menu').value;
format = JSON.parse(format);
var markdownOptions = document.getElementById('noteQuickCopy-markdown-options');
var htmlOptions = document.getElementById('noteQuickCopy-html-options');
var markdownOptionsLabel = document.querySelector('#noteQuickCopy-markdown-options label');
var htmlOptionsLabel = document.querySelector('#noteQuickCopy-html-options label');
var markdownIncludeAppLinks = document.getElementById("noteQuickCopy-markdown-includeAppLinks");
var htmlIncludeAppLinks = document.getElementById("noteQuickCopy-html-includeAppLinks");
markdownOptionsLabel.value = Zotero.Utilities.Internal.stringWithColon("Markdown");
htmlOptionsLabel.value = Zotero.Utilities.Internal.stringWithColon(
Zotero.getString('zotero.preferences.export.quickCopy.note.htmlOptions.label')
);
markdownIncludeAppLinks.label = Zotero.getString('exportOptions.includeAppLinks', Zotero.appName);
htmlIncludeAppLinks.label = Zotero.getString('exportOptions.includeAppLinks', Zotero.appName);
if (format.id == Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) {
markdownOptions.hidden = false;
htmlOptions.hidden = false;
markdownIncludeAppLinks.checked = format.markdownOptions && format.markdownOptions.includeAppLinks;
htmlIncludeAppLinks.checked = format.htmlOptions && format.htmlOptions.includeAppLinks;
}
else if (format.id == Zotero.Translators.TRANSLATOR_ID_NOTE_HTML) {
markdownOptions.hidden = true;
htmlOptions.hidden = false;
htmlIncludeAppLinks.checked = format.options && format.options.includeAppLinks;
}
else {
markdownOptions.hidden = true;
htmlOptions.hidden = true;
}
},
onUpdateNoteExportOptions() {
var menulist = document.getElementById("zotero-noteQuickCopy-menu");
var markdownIncludeAppLinks = document.getElementById("noteQuickCopy-markdown-includeAppLinks");
var htmlIncludeAppLinks = document.getElementById("noteQuickCopy-html-includeAppLinks");
for (let i = 0; i < menulist.itemCount; i++) {
let item = menulist.getItemAtIndex(i);
let format = JSON.parse(item.getAttribute('value'));
if (format.id == Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) {
if (!format.markdownOptions) {
format.markdownOptions = {};
}
if (!format.htmlOptions) {
format.htmlOptions = {};
}
format.markdownOptions.includeAppLinks = markdownIncludeAppLinks.checked;
format.htmlOptions.includeAppLinks = htmlIncludeAppLinks.checked;
}
else if (format.id == Zotero.Translators.TRANSLATOR_ID_NOTE_HTML) {
if (!format.options) {
format.options = {};
}
format.options.includeAppLinks = htmlIncludeAppLinks.checked;
}
else {
continue;
}
item.value = JSON.stringify(format);
}
// After updating menulist item value we have to wait a bit before doing click(), to avoid anomalies
// like an empty row in menulist. 0 in setTimeout is not enough
setTimeout(() => menulist.click(), 50);
},

View file

@ -71,9 +71,38 @@
<label value="&zotero.preferences.quickCopy.noteFormat;" control="zotero-noteQuickCopy-menu"/>
<menulist id="zotero-noteQuickCopy-menu" label="&zotero.general.loading;"/>
<table id="noteQuickCopy-format-options"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
hidden="hidden">
<tbody id="noteQuickCopy-markdown-options">
<tr>
<th>
<xul:label/>
</th>
<td>
<xul:checkbox id="noteQuickCopy-markdown-includeAppLinks"
oncommand="Zotero_Preferences.Export.onUpdateNoteExportOptions()"/>
</td>
</tr>
</tbody>
<tbody id="noteQuickCopy-html-options">
<tr>
<th>
<xul:label/>
</th>
<td>
<xul:checkbox id="noteQuickCopy-html-includeAppLinks"
oncommand="Zotero_Preferences.Export.onUpdateNoteExportOptions()"/>
</td>
</tr>
</tbody>
</table>
<separator/>
<label value="&zotero.preferences.quickCopy.siteEditor.setings;" control="quickCopy-siteSettings"/>
<hbox class="virtualized-table-container" flex="1" height="120px">
<html:div id="quickCopy-siteSettings"/>

View file

@ -69,6 +69,25 @@
<menuitem id="menu_copy"/>
<menuitem id="menu_paste"/>
<menuitem id="menu_delete"/>
<menuseparator class="menu-type-reader"/>
<menuitem
id="edit-menuitem-rotate-left"
class="menu-type-reader"
label="&zotero.pdfReader.rotatePageLeft;"
oncommand="menuCmd('rotateLeft')"
/>
<menuitem
id="edit-menuitem-rotate-right"
class="menu-type-reader"
label="&zotero.pdfReader.rotatePageRight;"
oncommand="menuCmd('rotateRight')"
/>
<!--<menuitem
id="edit-menuitem-rotate-180"
class="menu-type-reader"
label="&zotero.pdfReader.rotatePage180;"
oncommand="menuCmd('rotate180')"
/>-->
</menupopup>
</menu>
<menu label="View" onpopupshowing="onViewMenuOpen()">
@ -81,15 +100,6 @@
-->
<!-- <menuitem label="Switch to Presentation Mode" oncommand="menuCmd('presentationmode')"/>-->
<!-- <menuseparator/>-->
<menuitem
label="&zotero.pdfReader.rotateClockwise;"
oncommand="menuCmd('rotatecw')"
/>
<menuitem
label="&zotero.pdfReader.rotateCounterclockwise;"
oncommand="menuCmd('rotateccw')"
/>
<menuseparator/>
<menuitem
id="view-menuitem-hand-tool"
label="&zotero.pdfReader.handTool;"
@ -163,6 +173,19 @@
label="&zotero.pdfReader.zoomPageHeight;"
oncommand="menuCmd('zoomPageHeight')"
/>
<menuseparator class="menu-type-reader"/>
<menuitem
id="view-menuitem-split-horizontally"
type="checkbox"
label="&zotero.pdfReader.splitHorizontally;"
oncommand="menuCmd('splitHorizontally')"
/>
<menuitem
id="view-menuitem-split-vertically"
type="checkbox"
label="&zotero.pdfReader.splitVertically;"
oncommand="menuCmd('splitVertically')"
/>
</menupopup>
</menu>
<menu

View file

@ -30,9 +30,8 @@ var Zotero_Report_Interface = new function() {
*/
this.loadCollectionReport = function () {
var sortColumn = ZoteroPane_Local.getSortField();
var sortDirection = ZoteroPane_Local.getSortDirection();
var queryString = '?sort=' + sortColumn
+ '&direction=' + (sortDirection == 'ascending' ? 'asc' : 'desc');
+ '&direction=' + (ZoteroPane.getSortDirection() == 1 ? 'asc' : 'desc');
var url = 'zotero://report/';

View file

@ -160,8 +160,7 @@ const ZoteroStandalone = new function() {
let item = Zotero.Items.get(reader.itemID);
let library = Zotero.Libraries.get(item.libraryID);
if (item
// Don't allow annotation transfer in group libraries
&& library.libraryType == 'user'
&& library.filesEditable
&& library.editable
&& !(item.deleted || item.parentItem && item.parentItem.deleted)) {
let annotations = item.getAnnotations();
@ -192,12 +191,9 @@ const ZoteroStandalone = new function() {
if (numFiles) {
menuitem.hidden = false;
sep.hidden = false;
if (numFiles == 1) {
menuitem.label = 'Export PDF…';
}
else {
menuitem.label = 'Export PDFs…';
}
menuitem.label = Zotero.getString(
'pane.items.menu.exportPDF' + (numFiles == 1 ? '' : '.multiple')
);
}
else {
menuitem.hidden = true;
@ -408,6 +404,8 @@ const ZoteroStandalone = new function() {
this.updateMenuItemCheckmark('view-menuitem-zoom-auto', reader.isZoomAutoActive());
this.updateMenuItemCheckmark('view-menuitem-zoom-page-width', reader.isZoomPageWidthActive());
this.updateMenuItemCheckmark('view-menuitem-zoom-page-height', reader.isZoomPageHeightActive());
this.updateMenuItemCheckmark('view-menuitem-split-vertically', reader.isSplitVerticallyActive());
this.updateMenuItemCheckmark('view-menuitem-split-horizontally', reader.isSplitHorizontallyActive());
}
// Layout mode

View file

@ -232,6 +232,25 @@
label="&bidiSwitchTextDirectionItem.label;"
accesskey="&bidiSwitchTextDirectionItem.accesskey;"
hidden="true"/>
<menuseparator class="menu-type-reader"/>
<menuitem
id="edit-menuitem-rotate-left"
class="menu-type-reader"
label="&zotero.pdfReader.rotatePageLeft;"
oncommand="ZoteroStandalone.onReaderCmd('rotateLeft')"
/>
<menuitem
id="edit-menuitem-rotate-right"
class="menu-type-reader"
label="&zotero.pdfReader.rotatePageRight;"
oncommand="ZoteroStandalone.onReaderCmd('rotateRight')"
/>
<!--<menuitem
id="edit-menuitem-rotate-180"
class="menu-type-reader"
label="&zotero.pdfReader.rotatePage180;"
oncommand="ZoteroStandalone.onReaderCmd('rotate180')"
/>-->
</menupopup>
</menu>
@ -240,17 +259,6 @@
accesskey="&viewMenu.accesskey;"
onpopupshowing="ZoteroStandalone.onViewMenuOpen()">
<menupopup id="menu_viewPopup">
<menuitem
class="menu-type-reader"
label="&zotero.pdfReader.rotateClockwise;"
oncommand="ZoteroStandalone.onReaderCmd('rotatecw')"
/>
<menuitem
class="menu-type-reader"
label="&zotero.pdfReader.rotateCounterclockwise;"
oncommand="ZoteroStandalone.onReaderCmd('rotateccw')"
/>
<menuseparator class="menu-type-reader"/>
<menuitem
id="view-menuitem-hand-tool"
class="menu-type-reader"
@ -337,6 +345,21 @@
oncommand="ZoteroStandalone.onReaderCmd('zoomPageHeight')"
/>
<menuseparator class="menu-type-reader"/>
<menuitem
id="view-menuitem-split-horizontally"
class="menu-type-reader"
type="checkbox"
label="&zotero.pdfReader.splitHorizontally;"
oncommand="ZoteroStandalone.onReaderCmd('splitHorizontally')"
/>
<menuitem
id="view-menuitem-split-vertically"
class="menu-type-reader"
type="checkbox"
label="&zotero.pdfReader.splitVertically;"
oncommand="ZoteroStandalone.onReaderCmd('splitVertically')"
/>
<menuseparator class="menu-type-reader"/>
<menu id="layout-menu" label="&layout.label;">
<menupopup oncommand="ZoteroStandalone.onViewMenuItemClick(event)">
@ -390,6 +413,8 @@
<menuitem label="12" type="checkbox"/>
<menuitem label="13" type="checkbox"/>
<menuitem label="14" type="checkbox"/>
<menuitem label="15" type="checkbox"/>
<menuitem label="16" type="checkbox"/>
<menuitem label="18" type="checkbox"/>
<menuitem label="24" type="checkbox"/>
<menuitem label="36" type="checkbox"/>

View file

@ -30,7 +30,9 @@ var React = require('react');
var ReactDOM = require('react-dom');
import TabBar from 'components/tabBar';
const MAX_LOADED_TABS = 5;
// Reduce loaded tabs limit if the system has 8 GB or less memory.
// TODO: Revise this after upgrading to Zotero 7
const MAX_LOADED_TABS = Services.sysinfo.getProperty("memsize") / 1024 / 1024 / 1024 <= 8 ? 2 : 5;
const UNLOAD_UNUSED_AFTER = 86400; // 24h
var Zotero_Tabs = new function () {
@ -84,6 +86,17 @@ var Zotero_Tabs = new function () {
window.Zotero_Tooltip.stop();
};
this.getTabIDByItemID = function (itemID) {
let tab = this._tabs.find(tab => tab.data && tab.data.itemID === itemID);
return tab && tab.id;
};
this.setSecondViewState = function (tabID, state) {
let { tab } = this._getTab(tabID);
tab.data.secondViewState = state;
Zotero.Session.debounceSave();
};
this.init = function () {
ReactDOM.render(
<TabBar
@ -128,12 +141,24 @@ var Zotero_Tabs = new function () {
}
else if (tab.type === 'reader') {
if (Zotero.Items.exists(tab.data.itemID)) {
this.add({
type: 'reader-unloaded',
title: tab.title,
index: i,
data: tab.data
});
if (tab.selected) {
Zotero.Reader.open(tab.data.itemID,
null,
{
title: tab.title,
openInBackground: !tab.selected,
secondViewState: tab.data.secondViewState
}
);
}
else {
this.add({
type: 'reader-unloaded',
title: tab.title,
index: i,
data: tab.data
});
}
}
}
}
@ -152,7 +177,7 @@ var Zotero_Tabs = new function () {
* @param {Function} onClose
* @return {{ id: string, container: XULElement}} id - tab id, container - a new tab container created in the deck
*/
this.add = function ({ type, data, title, index, select, onClose }) {
this.add = function ({ id, type, data, title, index, select, onClose }) {
if (typeof type != 'string') {
}
if (typeof title != 'string') {
@ -164,7 +189,7 @@ var Zotero_Tabs = new function () {
if (onClose !== undefined && typeof onClose != 'function') {
throw new Error(`'onClose' should be a function (was ${typeof onClose})`);
}
var id = 'tab-' + Zotero.Utilities.randomString();
id = id || 'tab-' + Zotero.Utilities.randomString();
var container = document.createElement('vbox');
container.id = id;
this.deck.appendChild(container);
@ -251,7 +276,7 @@ var Zotero_Tabs = new function () {
closedIDs.push(id);
}
this._history.push(historyEntry);
Zotero.Notifier.trigger('close', 'tab', [closedIDs]);
Zotero.Notifier.trigger('close', 'tab', [closedIDs], true);
this._update();
};
@ -321,7 +346,7 @@ var Zotero_Tabs = new function () {
* @param {String} id
* @param {Boolean} reopening
*/
this.select = function (id, reopening) {
this.select = function (id, reopening, options) {
var { tab, tabIndex } = this._getTab(id);
if (!tab || tab.id === this._selectedID) {
return;
@ -332,10 +357,12 @@ var Zotero_Tabs = new function () {
}
if (tab.type === 'reader-unloaded') {
this.close(tab.id);
Zotero.Reader.open(tab.data.itemID, null, {
Zotero.Reader.open(tab.data.itemID, options && options.location, {
tabID: tab.id,
title: tab.title,
tabIndex,
allowDuplicate: true
allowDuplicate: true,
secondViewState: tab.data.secondViewState
});
return;
}
@ -385,6 +412,7 @@ var Zotero_Tabs = new function () {
}
this.close(tab.id);
this.add({
id: tab.id,
type: 'reader-unloaded',
title: tab.title,
index: tabIndex,
@ -440,7 +468,7 @@ var Zotero_Tabs = new function () {
};
this._openMenu = function (x, y, id) {
var { tabIndex } = this._getTab(id);
var { tab, tabIndex } = this._getTab(id);
window.Zotero_Tooltip.stop();
let menuitem;
let popup = document.createElement('menupopup');
@ -497,7 +525,8 @@ var Zotero_Tabs = new function () {
var reader = Zotero.Reader.getByTabID(id);
if (reader) {
this.close(id);
Zotero.Reader.open(reader.itemID, null, { openInWindow: true });
let { secondViewState } = tab.data;
Zotero.Reader.open(reader.itemID, null, { openInWindow: true, secondViewState });
}
});
menupopup.appendChild(menuitem);
@ -505,11 +534,10 @@ var Zotero_Tabs = new function () {
menuitem = document.createElement('menuitem');
menuitem.setAttribute('label', Zotero.getString('tabs.duplicate'));
menuitem.addEventListener('command', () => {
var { tab, tabIndex } = this._getTab(id);
if (tab.data.itemID) {
tabIndex++;
Zotero.Reader.open(tab.data.itemID, null, { tabIndex, allowDuplicate: true });
let { secondViewState } = tab.data;
Zotero.Reader.open(tab.data.itemID, null, { tabIndex, allowDuplicate: true, secondViewState });
}
});
popup.appendChild(menuitem);

View file

@ -59,10 +59,8 @@ var Zotero_CSL_Editor = new function() {
var pageList = document.getElementById('zotero-csl-page-type');
var locators = Zotero.Cite.labels;
for (let type of locators) {
var locator = type;
locator = Zotero.getString('citation.locator.'+locator.replace(/\s/g,''));
pageList.appendItem(locator, type);
for (let locator of locators) {
pageList.appendItem(Zotero.Cite.getLocatorString(locator), locator);
}
pageList.selectedIndex = 0;

View file

@ -177,7 +177,7 @@ Zotero.Annotations = new function () {
o.tags = tags;
}
o.dateModified = item.dateModified;
o.dateModified = Zotero.Date.sqlToISO8601(item.dateModified);
return o;
};

View file

@ -270,7 +270,7 @@ Zotero.Attachments = new function(){
/**
* @param {Object} options - 'file', 'url', 'title', 'contentType', 'charset', 'parentItemID', 'singleFile'
* @param {Object} options - 'file', 'url', 'title', 'contentType', 'charset', 'libraryID', 'parentItemID', 'singleFile'
* @param {Object} [options.saveOptions] - Options to pass to Zotero.Item::save()
* @return {Promise<Zotero.Item>}
*/
@ -287,10 +287,19 @@ Zotero.Attachments = new function(){
var title = options.title;
var contentType = options.contentType;
var charset = options.charset;
var libraryID = options.libraryID;
var parentItemID = options.parentItemID;
var saveOptions = options.saveOptions;
if (!parentItemID) {
if (parentItemID) {
libraryID = Zotero.Items.getLibraryAndKeyFromID(parentItemID).libraryID;
}
else if (contentType == 'text/html') {
throw new Error("parentItemID not provided");
}
// Webpage snapshots must have parent items
if (!parentItemID && contentType == 'text/html') {
throw new Error("parentItemID not provided");
}
@ -299,8 +308,9 @@ Zotero.Attachments = new function(){
yield Zotero.DB.executeTransaction(function* () {
// Create a new attachment
attachmentItem = new Zotero.Item('attachment');
let {libraryID, key: parentKey} = Zotero.Items.getLibraryAndKeyFromID(parentItemID);
attachmentItem.libraryID = libraryID;
if (libraryID) {
attachmentItem.libraryID = libraryID;
}
attachmentItem.setField('title', title);
attachmentItem.setField('url', url);
attachmentItem.parentID = parentItemID;
@ -563,7 +573,8 @@ Zotero.Attachments = new function(){
reject(e);
},
true,
cookieSandbox
cookieSandbox,
{ allowImages: true }
);
});
};
@ -1143,67 +1154,82 @@ Zotero.Attachments = new function(){
*/
this.downloadPDFViaBrowser = async function (url, path, options = {}) {
Zotero.debug(`downloadPDFViaBrowser: Downloading file via browser from ${url}`);
const onLoadTimeout = 1e3;
const onLoadTimeout = Zotero.Prefs.get('downloadPDFViaBrowser.onLoadTimeout');
// Technically this is not a download, but the full operation timeout
const downloadTimeout = 60e3;
const downloadTimeout = Zotero.Prefs.get('downloadPDFViaBrowser.downloadTimeout');
let channelBrowser, hiddenBrowser;
let hiddenBrowserPDFFoundDeferred = Zotero.Promise.defer();
var pdfMIMETypeHandler = async (blob, name, _, channel) => {
Zotero.debug(`downloadPDFViaBrowser: Sniffing a PDF loaded at ${name}`);
let isOurPDF = false;
// try the browser
try {
channelBrowser = channel.notificationCallbacks.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell).chromeEventHandler;
}
catch (e) {}
if (channelBrowser) {
isOurPDF = hiddenBrowser === channelBrowser;
}
else {
// try the document for the load group
let isOurPDF = false;
var pdfMIMETypeHandler = {
onStartRequest: function (name, _, channel) {
Zotero.debug(`downloadPDFViaBrowser: Sniffing a PDF loaded at ${name}`);
// try the browser
try {
channelBrowser = channel.loadGroup.notificationCallbacks.getInterface(Ci.nsIWebNavigation)
channelBrowser = channel.notificationCallbacks.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell).chromeEventHandler;
}
catch(e) {}
catch (e) {}
if (channelBrowser) {
isOurPDF = hiddenBrowser === channelBrowser;
}
}
if (isOurPDF) {
Zotero.debug(`downloadPDFViaBrowser: Found our PDF at ${name}`);
await Zotero.File.putContentsAsync(path, blob);
hiddenBrowserPDFFoundDeferred.resolve();
return true;
}
else {
Zotero.debug(`downloadPDFViaBrowser: Not our PDF at ${name}`);
return false;
else {
// try the document for the load group
try {
channelBrowser = channel.loadGroup.notificationCallbacks.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell).chromeEventHandler;
}
catch(e) {}
if (channelBrowser) {
isOurPDF = hiddenBrowser === channelBrowser;
}
}
},
onContent: async (blob, name, _, channel) => {
if (isOurPDF) {
Zotero.debug(`downloadPDFViaBrowser: Found our PDF at ${name}`);
await Zotero.File.putContentsAsync(path, blob);
hiddenBrowserPDFFoundDeferred.resolve();
return true;
}
else {
Zotero.debug(`downloadPDFViaBrowser: Not our PDF at ${name}`);
return false;
}
}
};
try {
Zotero.MIMETypeHandler.addHandler("application/pdf", pdfMIMETypeHandler, true);
let noop = () => 0;
Zotero.MIMETypeHandler.addHandlers("application/pdf", pdfMIMETypeHandler, true);
function noop() {};
hiddenBrowser = Zotero.HTTP.loadDocuments([url], noop, noop, noop, true, options.cookieSandbox);
let onLoadTimeoutDeferred = Zotero.Promise.defer();
hiddenBrowser.addEventListener("load", async function onLoad() {
Zotero.debug(`downloadPDFViaBrowser: Page with potential JS redirect loaded, giving it ${onLoadTimeout}ms to process`);
hiddenBrowser.addEventListener("unload", () => {
Zotero.debug(`downloadPDFViaBrowser: A JS redirect occurred, short timeout cancelled`);
hiddenBrowser.removeEventListener('load', onLoad);
});
await Zotero.Promise.delay(onLoadTimeout);
onLoadTimeoutDeferred.reject(`Loading PDF via browser timed out on the JS challenge page after ${onLoadTimeout}ms`);
}, true);
let currentUrl = "";
hiddenBrowser.addProgressListener({
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIWebProgressListener,
Components.interfaces.nsISupportsWeakReference]),
onProgressChange: noop,
onStateChange: noop,
onStatusChange: noop,
onSecurityChange: noop,
async onLocationChange() {
let url = hiddenBrowser.contentDocument.location.href;
if (currentUrl) {
Zotero.debug(`downloadPDFViaBrowser: A JS redirect occurred to ${hiddenBrowser.contentDocument.location.href}`);
}
currentUrl = url;
Zotero.debug(`downloadPDFViaBrowser: Page with potential JS redirect loaded, giving it ${onLoadTimeout}ms to process`);
await Zotero.Promise.delay(onLoadTimeout);
// If URL changed that means we got redirected and the onLoadTimeout needs to restart
if (currentUrl === url && !isOurPDF) {
onLoadTimeoutDeferred.reject(new Error(`downloadPDFViaBrowser: Loading PDF via browser timed out on the JS challenge page after ${onLoadTimeout}ms`));
}
}
});
await Zotero.Promise.race([
onLoadTimeoutDeferred.promise,
Zotero.Promise.delay(downloadTimeout).then(() => {
if (!hiddenBrowserPDFFoundDeferred.promise.isResolved()) {
throw new Error(`Loading PDF via browser timed out after ${downloadTimeout}ms`);
if (!isOurPDF) {
throw new Error(`downloadPDFViaBrowser: Loading PDF via browser timed out after ${downloadTimeout}ms`);
}
}),
hiddenBrowserPDFFoundDeferred.promise
@ -1213,13 +1239,13 @@ Zotero.Attachments = new function(){
try {
await OS.File.remove(path, { ignoreAbsent: true });
}
catch (e) {
Zotero.logError(e, 1);
catch (err) {
Zotero.logError(err);
}
throw e;
}
finally {
Zotero.MIMETypeHandler.removeHandler('application/pdf', pdfMIMETypeHandler);
Zotero.MIMETypeHandler.removeHandlers('application/pdf', pdfMIMETypeHandler);
if (hiddenBrowser) {
Zotero.Browser.deleteHiddenBrowser(hiddenBrowser);
}
@ -1259,13 +1285,14 @@ Zotero.Attachments = new function(){
this.canFindPDFForItem = function (item) {
return item.isRegularItem()
&& !item.isFeedItem
&& (!!item.getField('DOI') || !!item.getField('url') || !!item.getExtraField('DOI'))
&& item.numPDFAttachments() == 0;
};
/**
* Look for an available PDF for an item and add it as an attachment
* Get the PDF resolvers that can be used for a given item based on the available fields
*
* @param {Zotero.Item} item
* @param {String[]} [methods=['doi', 'url', 'oa', 'custom']]
@ -2011,7 +2038,7 @@ Zotero.Attachments = new function(){
addTriedURL(url);
// Backoff loop
let tries = 3;
while (tries-- >= 0) {
while (tries-- > 0) {
try {
await beforeRequest(url);
await this.downloadFile(url, path, options);
@ -2043,14 +2070,22 @@ Zotero.Attachments = new function(){
let contentType;
let skip = false;
let domains = new Set();
let redirectLimit = 10;
let redirectURLTries = new Map();
while (true) {
if (redirectLimit == 0) {
Zotero.debug("Too many redirects -- stopping");
skip = true;
break;
}
let domain = urlToDomain(nextURL);
let noDelay = domains.has(domain);
domains.add(domain);
// Backoff loop
let tries = 3;
while (tries-- >= 0) {
while (tries-- > 0) {
try {
await beforeRequest(nextURL, noDelay);
req = await Zotero.HTTP.request(
@ -2058,7 +2093,9 @@ Zotero.Attachments = new function(){
nextURL,
{
responseType: 'blob',
followRedirects: false
followRedirects: false,
// Use our own error handling
errorDelayMax: 0
}
);
}
@ -2069,22 +2106,39 @@ Zotero.Attachments = new function(){
noDelay = false;
continue;
}
throw e;
}
break;
}
afterRequest(nextURL);
if (!req) {
break;
}
if ([301, 302, 303, 307].includes(req.status)) {
let location = req.getResponseHeader('Location');
if (!location) {
throw new Error("Location header not provided");
}
let currentURL = nextURL;
nextURL = Services.io.newURI(nextURL, null, null).resolve(location);
if (isTriedURL(nextURL)) {
Zotero.debug("Redirect URL has already been tried -- skipping");
skip = true;
break;
}
// Keep track of tries for each redirect URL, and stop if too many
let maxTriesPerRedirectURL = 2;
let tries = (redirectURLTries.get(currentURL) || 0) + 1;
if (tries > maxTriesPerRedirectURL) {
Zotero.debug(`Too many redirects to ${currentURL} -- stopping`);
skip = true;
break;
}
redirectURLTries.set(currentURL, tries);
// And keep track of total redirects for this chain
redirectLimit--;
continue;
}
@ -2131,7 +2185,7 @@ Zotero.Attachments = new function(){
}
// If DOI resolves directly to a PDF, save it to disk
if (contentType.startsWith('application/pdf')) {
if (contentType && contentType.startsWith('application/pdf')) {
Zotero.debug("URL resolves directly to PDF");
await Zotero.File.putContentsAsync(path, blob);
await _enforcePDF(path);
@ -2147,7 +2201,7 @@ Zotero.Attachments = new function(){
continue;
}
if (!url) {
Zotero.debug(`No PDF found on ${responseURL}`);
Zotero.debug(`No PDF found on ${responseURL || pageURL}`);
continue;
}
if (isTriedURL(url)) {

View file

@ -8,9 +8,110 @@ Zotero.Cite = {
/**
* Locator labels
*/
"labels":["page", "book", "chapter", "column", "figure", "folio",
"issue", "line", "note", "opus", "paragraph", "part", "section", "sub verbo",
"volume", "verse"],
"labels": [
"act",
"appendix",
"article-locator",
"book",
"canon",
"chapter",
"column",
"elocation",
"equation",
"figure",
"folio",
"issue",
"line",
"note",
"opus",
"page",
"paragraph",
"part",
"rule",
"scene",
"section",
"sub-verbo",
"table",
//"timestamp",
"title-locator",
"verse",
"volume"
],
_locatorStrings: new Map(),
/**
* Get localized string for locator
*
* @param {String} locator - Locator name (e.g., 'book')
* @return {String} - Localized string (e.g., 'Livre')
*/
getLocatorString: function (locator) {
// 'timestamp' not included in CSL locales
if (locator == 'timestamp') {
return Zotero.getString('citation.locator.timestamp');
}
// Get the best CSL locale for the current Zotero locale
var cslLocale = Zotero.Utilities.Internal.resolveLocale(
Zotero.locale,
Object.keys(Zotero.Styles.locales)
);
// If locator strings are already cached for the current locale, use that
if (this._locatorStrings.has(cslLocale)) {
return this._locatorStrings.get(cslLocale).get(locator);
}
var map = new Map();
this._locatorStrings.set(cslLocale, map);
var localeXML = Zotero.Cite.Locale.get(cslLocale);
var parser;
if (Zotero.platformMajorVersion > 60) {
parser = new DOMParser();
}
else {
parser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
.createInstance(Components.interfaces.nsIDOMParser);
}
var doc = parser.parseFromString(localeXML, 'text/xml');
var englishDoc;
// Cache all locators for the current locale
for (let locator of this.labels) {
let elem = doc.querySelector(`term[name="${locator}"]:not([form="short"]) > single`);
if (!elem) {
// If locator not found, get from the U.S. English locale
if (cslLocale != 'en-US') {
Zotero.logError(`Locator '${locator}' not found in ${cslLocale} locale -- trying en-US`);
if (!englishDoc) {
englishDoc = parser.parseFromString(Zotero.Cite.Locale.get('en-US'), 'text/xml');
}
elem = englishDoc.querySelector(`term[name="${locator}"]:not([form="short"]) > single`);
if (!elem) {
Zotero.logError(`Locator '${locator}' not found in en-US locale -- using name`);
}
}
else {
Zotero.logError(`Locator '${locator}' not found in en-US locale -- using name`);
}
// If still not found, use the locator name directly
if (!elem) {
map.set(locator, Zotero.Utilities.capitalize(locator));
continue;
}
}
// If <single> is empty, use the locator name directly
let str = elem.textContent;
if (!str) {
Zotero.logError(`Locator '${locator}' is empty in ${cslLocale} locale -- using name`);
map.set(locator, Zotero.Utilities.capitalize(locator));
continue;
}
map.set(locator, Zotero.Utilities.capitalize(str));
}
return map.get(locator);
},
/**
* Remove specified item IDs in-place from a citeproc-js bibliography object returned

View file

@ -22261,7 +22261,7 @@ CSL.Output.Formats.prototype.text = {
return "\n"+str;
},
"@display/left-margin": function (state, str) {
return str;
return str + " ";
},
"@display/right-inline": function (state, str) {
return str;

View file

@ -61,6 +61,7 @@ Zotero.HTTPIntegrationClient.Application = function() {
this.supportedNotes = ['footnotes'];
this.supportsImportExport = false;
this.supportsTextInsertion = false;
this.supportsCitationMerging = false;
this.processorName = "HTTP Integration";
};
Zotero.HTTPIntegrationClient.Application.prototype = {
@ -70,6 +71,7 @@ Zotero.HTTPIntegrationClient.Application.prototype = {
this.supportedNotes = result.supportedNotes || this.supportedNotes;
this.supportsImportExport = result.supportsImportExport || this.supportsImportExport;
this.supportsTextInsertion = result.supportsTextInsertion || this.supportsTextInsertion;
this.supportsCitationMerging = result.supportsCitationMerging || this.supportsCitationMerging;
this.processorName = result.processorName || this.processorName;
return new Zotero.HTTPIntegrationClient.Document(result.documentID, this.processorName);
}
@ -168,6 +170,10 @@ Zotero.HTTPIntegrationClient.Field = function(documentID, json) {
this._code = json.code;
this._text = json.text;
this._noteIndex = json.noteIndex;
this._adjacent = json.adjacent;
if (this._adjacent !== 'undefined') {
this.isAdjacentToNextField = this._isAdjacentToNextField;
}
};
Zotero.HTTPIntegrationClient.Field.prototype = {};
@ -202,3 +208,6 @@ Zotero.HTTPIntegrationClient.Field.prototype.getNoteIndex = async function() {
Zotero.HTTPIntegrationClient.Field.prototype.equals = async function(arg) {
return this._id === arg._id;
};
Zotero.HTTPIntegrationClient.Field.prototype._isAdjacentToNextField = async function() {
return this._adjacent;
}

View file

@ -1689,9 +1689,8 @@ Zotero.Server.Connector.Ping.prototype = {
*/
init: function (req) {
if (req.method == 'GET') {
return [200, "text/html", '<!DOCTYPE html><html><head>' +
'<title>Zotero Connector Server is Available</title></head>' +
'<body>Zotero Connector Server is Available</body></html>'];
return [200, "text/html", '<!DOCTYPE html><html>'
+ '<body>Zotero is running</body></html>'];
} else {
// Store the active URL so it can be used for site-specific Quick Copy
if (req.data.activeURL) {

View file

@ -383,6 +383,10 @@ Zotero.CookieSandbox.Observer = new function() {
if(trackedBy) {
var cookiesForURI = trackedBy.getCookiesForURI(channel.URI);
}
if (trackedBy && trackedBy.userAgent) {
channel.setRequestHeader("User-Agent", trackedBy.userAgent, false);
}
if(!trackedBy || !cookiesForURI) {
channel.setRequestHeader("Cookie", "", false);
@ -390,11 +394,7 @@ Zotero.CookieSandbox.Observer = new function() {
Zotero.debug("CookieSandbox: Cleared cookies to be sent to "+channelURI, 5);
return;
}
if(trackedBy.userAgent) {
channel.setRequestHeader("User-Agent", trackedBy.userAgent, false);
}
// add cookies to be sent to this domain
channel.setRequestHeader("Cookie", Zotero.CookieSandbox.generateCookieString(cookiesForURI), false);
Zotero.debug("CookieSandbox: Added cookies for request to "+channelURI, 5);

View file

@ -275,7 +275,7 @@ Zotero.DataObjectUtilities = {
var pred2 = Object.keys(data2);
pred2.sort();
if (!Zotero.Utilities.arrayEquals(pred1, pred2)) return true;
for (let pred in pred1) {
for (let pred of pred1) {
let vals1 = typeof data1[pred] == 'string' ? [data1[pred]] : data1[pred];
let vals2 = (!data2[pred] || data2[pred] === '')
? []

View file

@ -133,8 +133,9 @@ Zotero.Feeds = new function() {
for (let feedElem of feedElems) {
let url = feedElem.getAttribute('xmlUrl');
if (!url) url = feedElem.getAttribute('url');
let name = feedElem.getAttribute('title');
if (!name) name = feedElem.getAttribute('text');
let name = feedElem.getAttribute('title')
|| feedElem.getAttribute('text')
|| Zotero.getString('pane.collections.untitled');
if (Zotero.Feeds.existsByURL(url) || registeredUrls.has(url)) {
Zotero.debug("Feed Import from OPML: Feed " + name + " : " + url + " already exists. Skipping");
continue;

View file

@ -153,7 +153,16 @@ Zotero.defineProperty(Zotero.Item.prototype, 'parentItemKey', {
Zotero.defineProperty(Zotero.Item.prototype, 'parentItem', {
get: function() { return Zotero.Items.get(this.parentID) || undefined; },
});
Zotero.defineProperty(Zotero.Item.prototype, 'topLevelItem', {
get: function () {
var item = this; // eslint-disable-line consistent-this
var parentItem;
while ((parentItem = item.parentItem)) {
item = parentItem;
}
return item;
}
});
Zotero.defineProperty(Zotero.Item.prototype, 'firstCreator', {
get: function() { return this._firstCreator; }
@ -768,7 +777,7 @@ Zotero.Item.prototype.setField = function(field, value, loadIn) {
if (value !== false && !Zotero.ItemFields.isValidForType(fieldID, itemTypeID)) {
let msg = "'" + field + "' is not a valid field for type '"
+ Zotero.ItemFields.getName(itemTypeID) + "'";
+ Zotero.ItemTypes.getName(itemTypeID) + "'";
if (loadIn) {
Zotero.debug(msg + " -- ignoring value '" + value + "'", 2);
@ -3713,7 +3722,7 @@ Zotero.Item.prototype.getBestAttachments = Zotero.Promise.coroutine(function* ()
/**
* Return state of best attachment
* Return state of best attachment (or this item if it's a standalone attachment)
*
* @return {Promise<Object>} - Promise for object with string 'type' ('none'|'pdf'|'snapshot'|'other')
* and boolean 'exists'
@ -3722,7 +3731,9 @@ Zotero.Item.prototype.getBestAttachmentState = async function () {
if (this._bestAttachmentState !== null) {
return this._bestAttachmentState;
}
var item = await this.getBestAttachment();
var item = this.isAttachment() && this.isTopLevelItem()
? this
: await this.getBestAttachment();
if (!item) {
return this._bestAttachmentState = {
type: 'none'
@ -3953,6 +3964,29 @@ Zotero.Item.prototype.getAnnotations = function (includeTrashed) {
};
/**
* Determine if the item is a PDF attachment that exists on disk and contains
* embedded markup annotations.
*
* @return {Promise<Boolean>}
*/
Zotero.Item.prototype.hasEmbeddedAnnotations = async function () {
if (!this.isPDFAttachment()) {
return false;
}
let path = await this.getFilePathAsync();
if (!path) {
return false;
}
let contents = await Zotero.File.getContentsAsync(path);
// Check for "markup" annotations per the PDF spec
// https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf, p. 390
let re = /\s\/Subtype\s+\/(Text|FreeText|Line|Square|Circle|Polygon|PolyLine|Highlight|Underline|Squiggly|StrikeOut|Stamp|Caret|Ink|FileAttachment|Sound|Redact)\s/;
return re.test(contents);
};
//
// Methods dealing with item tags

View file

@ -1035,6 +1035,29 @@ Zotero.Items = function() {
for (let otherItem of otherItems) {
let mergedMasterAttachments = new Set();
let doMerge = async (fromAttachment, toAttachment) => {
mergedMasterAttachments.add(toAttachment.id);
await this.moveChildItems(fromAttachment, toAttachment, true);
await this._moveEmbeddedNote(fromAttachment, toAttachment);
await this._moveRelations(fromAttachment, toAttachment);
fromAttachment.deleted = true;
await fromAttachment.save();
// Later on, when processing notes, we'll use this to remap
// URLs pointing to the old attachment.
remapAttachmentKeys.set(fromAttachment.key, toAttachment.key);
// Items can only have one replaced item predicate
if (!toAttachment.getRelationsByPredicate(Zotero.Relations.replacedItemPredicate)) {
toAttachment.addRelation(Zotero.Relations.replacedItemPredicate,
Zotero.URI.getItemURI(fromAttachment));
}
await toAttachment.save();
};
for (let otherAttachment of await this.getAsync(otherItem.getAttachments(true))) {
if (!otherAttachment.isPDFAttachment()) {
continue;
@ -1065,13 +1088,12 @@ Zotero.Items = function() {
await otherAttachment.save();
continue;
}
mergedMasterAttachments.add(masterAttachmentID);
let masterAttachment = await this.getAsync(masterAttachmentID);
if (masterAttachment.attachmentContentType !== otherAttachment.attachmentContentType) {
Zotero.debug(`Master attachment ${masterAttachment.key} matches ${otherAttachment.key}, `
+ 'but content types differ - moving');
+ 'but content types differ - keeping both');
otherAttachment.parentItemID = item.id;
await otherAttachment.save();
continue;
@ -1080,31 +1102,35 @@ Zotero.Items = function() {
if (!((masterAttachment.isImportedAttachment() && otherAttachment.isImportedAttachment())
|| (masterAttachment.isLinkedFileAttachment() && otherAttachment.isLinkedFileAttachment()))) {
Zotero.debug(`Master attachment ${masterAttachment.key} matches ${otherAttachment.key}, `
+ 'but link modes differ - moving');
+ 'but link modes differ - keeping both');
otherAttachment.parentItemID = item.id;
await otherAttachment.save();
continue;
}
Zotero.debug(`Master attachment ${masterAttachment.key} matches ${otherAttachment.key} - merging`);
await this.moveChildItems(otherAttachment, masterAttachment, true);
await this._moveEmbeddedNote(otherAttachment, masterAttachment);
await this._moveRelations(otherAttachment, masterAttachment);
otherAttachment.deleted = true;
await otherAttachment.save();
// Later on, when processing notes, we'll use this to remap
// URLs pointing to the old attachment.
remapAttachmentKeys.set(otherAttachment.key, masterAttachment.key);
// Items can only have one replaced item predicate
if (!masterAttachment.getRelationsByPredicate(Zotero.Relations.replacedItemPredicate)) {
masterAttachment.addRelation(Zotero.Relations.replacedItemPredicate,
Zotero.URI.getItemURI(otherAttachment));
// Check whether master and other have embedded annotations
// Master yes, other yes -> keep both
// Master yes, other no -> keep master
// Master no, other yes -> keep other
if (await otherAttachment.hasEmbeddedAnnotations()) {
if (await masterAttachment.hasEmbeddedAnnotations()) {
Zotero.debug(`Master attachment ${masterAttachment.key} matches ${otherAttachment.key}, `
+ 'but both have embedded annotations - keeping both');
otherAttachment.parentItemID = item.id;
await otherAttachment.save();
}
else {
Zotero.debug(`Master attachment ${masterAttachment.key} matches ${otherAttachment.key}, `
+ 'but other has embedded annotations - merging into other');
await doMerge(masterAttachment, otherAttachment);
otherAttachment.parentItemID = item.id;
await otherAttachment.save();
}
continue;
}
await masterAttachment.save();
Zotero.debug(`Master attachment ${masterAttachment.key} matches ${otherAttachment.key} - merging into master`);
await doMerge(otherAttachment, masterAttachment);
}
}
@ -1477,6 +1503,7 @@ Zotero.Items = function() {
);
}
Zotero.debug("Emptied " + deleted.length + " item(s) from trash in " + (new Date() - t) + " ms");
Zotero.Notifier.trigger('refresh', 'trash', libraryID);
}
return deleted.length;
@ -1709,22 +1736,40 @@ Zotero.Items = function() {
/**
* Returns an array of items with children of selected parents removed
* Get the top-level items of all passed items
*
* @param {Zotero.Item[]} items
* @return {Zotero.Item[]}
*/
this.keepParents = function (items) {
var parentItems = new Set(
items
.filter(item => item.isTopLevelItem())
.map(item => item.id)
this.getTopLevel = function (items) {
return [...new Set(items.map(item => item.topLevelItem))];
};
/**
* Return an array of items with descendants of selected top-level items removed
*
* Non-top-level items that aren't descendents of selected items are kept.
*
* @param {Zotero.Item[]}
* @return {Zotero.Item[]}
*/
this.keepTopLevel = function (items) {
var topLevelItems = new Set(
items.filter(item => item.isTopLevelItem())
);
return items.filter(item => {
var parentItemID = item.parentItemID;
return items.filter((item) => {
var topLevelItem = !item.isTopLevelItem() && item.topLevelItem;
// Not a child item or not a child of one of the passed items
return !parentItemID || !parentItems.has(parentItemID);
return !topLevelItem || !topLevelItems.has(topLevelItem);
});
}
};
this.keepParents = function (items) {
Zotero.debug("Zotero.Items.keepParents() is deprecated -- use Zotero.Items.keepTopLevel() instead");
return this.keepTopLevel(items);
};
/*
@ -2011,6 +2056,28 @@ Zotero.Items = function() {
}
return title.trim();
};
/**
* Find attachment items whose paths begin with the passed `pathPrefix` and don't exist on disk
*
* @param {Number} libraryID
* @param {String} pathPrefix
* @return {Zotero.Item[]}
*/
this.findMissingLinkedFiles = async function (libraryID, pathPrefix) {
let sql = "SELECT itemID FROM items JOIN itemAttachments USING (itemID) "
+ "WHERE itemID NOT IN (SELECT itemID FROM deletedItems) "
+ `AND linkMode=${Zotero.Attachments.LINK_MODE_LINKED_FILE} `
+ "AND path LIKE ? ESCAPE '\\' "
+ "AND libraryID=?";
let ids = await Zotero.DB.columnQueryAsync(sql, [Zotero.DB.escapeSQLExpression(pathPrefix) + '%', libraryID]);
let items = await this.getAsync(ids);
let missingItems = await Promise.all(
items.map(async item => (await item.fileExists() ? false : item))
);
return missingItems.filter(Boolean);
};
Zotero.DataObjects.call(this);

View file

@ -948,7 +948,11 @@ Zotero.Search.idsToTempTable = Zotero.Promise.coroutine(function* (ids) {
Zotero.Search.prototype._buildQuery = Zotero.Promise.coroutine(function* () {
this._requireData('conditions');
var sql = 'SELECT itemID FROM items';
// TEMP: Match parent attachment for annotation matches
// var sql = 'SELECT itemID FROM items';
var sql = "SELECT COALESCE(IA.parentItemID, itemID) AS itemID FROM items "
+ "LEFT JOIN itemAnnotations IA USING (itemID)";
var sqlParams = [];
// Separate ANY conditions for 'required' condition support
var anySQL = '';
@ -962,7 +966,8 @@ Zotero.Search.prototype._buildQuery = Zotero.Promise.coroutine(function* () {
let conditionData = Zotero.SearchConditions.get(name);
// Has a table (or 'savedSearch', which doesn't have a table but isn't special)
if (conditionData.table || name == 'savedSearch' || name == 'tempTable') {
// TEMP: Or 'tag', which needs to match annotation parents
if (conditionData.table || name == 'savedSearch' || name == 'tempTable' || name == 'tag') {
// For conditions with an inline filter using 'is'/'isNot', combine with last condition
// if the same
if (lastCondition
@ -1077,14 +1082,15 @@ Zotero.Search.prototype._buildQuery = Zotero.Promise.coroutine(function* () {
}
if (unfiled) {
sql += " AND (itemID NOT IN (SELECT itemID FROM collectionItems) "
sql += " AND (itemID NOT IN ("
+ "SELECT itemID FROM collectionItems "
// Exclude children
+ "AND itemID NOT IN "
+ "(SELECT itemID FROM itemAttachments WHERE parentItemID IS NOT NULL "
+ "UNION SELECT itemID FROM itemNotes WHERE parentItemID IS NOT NULL)"
+ ") "
+ "UNION SELECT itemID FROM itemAttachments WHERE parentItemID IS NOT NULL "
+ "UNION SELECT itemID FROM itemNotes WHERE parentItemID IS NOT NULL "
+ "UNION SELECT itemID FROM itemAnnotations "
// Exclude My Publications
+ "AND itemID NOT IN (SELECT itemID FROM publicationsItems)";
+ "UNION SELECT itemID FROM publicationsItems "
+ "))";
}
if (retracted) {
@ -1119,22 +1125,42 @@ Zotero.Search.prototype._buildQuery = Zotero.Promise.coroutine(function* () {
//
// Special table handling
//
if (condition['table']){
switch (condition['table']){
default:
condSelectSQL += 'itemID '
switch (condition['operator']){
case 'isNot':
case 'doesNotContain':
condSelectSQL += 'NOT ';
break;
}
condSelectSQL += 'IN (';
selectOpenParens = 1;
condSQL += 'SELECT itemID FROM ' +
condition['table'] + ' WHERE (';
openParens = 1;
if (condition.table) {
let negationOperators = ['isNot', 'doesNotContain'];
let isNegationOperator = negationOperators.includes(condition.operator);
condSelectSQL += 'itemID '
if (isNegationOperator) {
condSelectSQL += 'NOT ';
}
condSelectSQL += 'IN (';
selectOpenParens = 1;
// TEMP: Don't match annotations for negation operators, since it would result in
// all parent attachments being returned
if (isNegationOperator) {
condSelectSQL += "SELECT itemID FROM items WHERE itemTypeID="
+ Zotero.ItemTypes.getID('annotation') + " UNION ";
}
switch (condition.name) {
// TEMP: Match parent attachments of matching annotations
case 'tag':
condSQL += "SELECT COALESCE(IAnT.parentItemID, itemID) FROM itemTags "
+ "LEFT JOIN itemAnnotations IAnT USING (itemID) WHERE (";
break;
// TEMP: Match parent attachments of matching annotations
case 'annotationText':
case 'annotationComment':
condSQL += `SELECT parentItemID FROM ${condition.table} WHERE (`
break;
default:
condSQL += `SELECT itemID FROM ${condition.table} WHERE (`;
}
openParens = 1;
}
//
@ -1709,7 +1735,11 @@ Zotero.Search.prototype._buildQuery = Zotero.Promise.coroutine(function* () {
// Add on quicksearch conditions
if (quicksearchSQLSet) {
sql = "SELECT itemID FROM items WHERE itemID IN (" + sql + ") "
// TEMP: Match parent attachments for annotations
//sql = "SELECT itemID FROM items WHERE itemID IN (" + sql + ") "
sql = "SELECT COALESCE(IAn.parentItemID, itemID) AS itemID FROM items "
+ "LEFT JOIN itemAnnotations IAn USING (itemID) "
+ "WHERE itemID IN (" + sql + ") "
+ "AND ((" + quicksearchSQLSet.join(') AND (') + "))";
for (var k=0; k<quicksearchParamsSet.length; k++) {

View file

@ -163,9 +163,6 @@ Zotero.Tags = new function() {
if (libraryID) {
sql += "JOIN items USING (itemID) WHERE libraryID = ? ";
params.push(libraryID);
// TEMP: Don't show annotation tags in tag selector
sql += "AND itemTypeID != ? ";
params.push(Zotero.ItemTypes.getID('annotation'));
}
else {
sql += "WHERE 1 ";
@ -174,7 +171,11 @@ Zotero.Tags = new function() {
if (libraryID) {
throw new Error("tmpTable and libraryID are mutually exclusive");
}
sql += "AND itemID IN (SELECT itemID FROM " + tmpTable + ") ";
sql += "AND itemID IN (SELECT itemID FROM " + tmpTable;
// TEMP: Match parent attachments for annotation tags
sql += " UNION SELECT itemID FROM itemAnnotations WHERE parentItemID IN "
+ "(SELECT itemID FROM " + tmpTable + ")";
sql += ") ";
}
if (types && types.length) {
sql += "AND type IN (" + new Array(types.length).fill('?').join(', ') + ") ";

View file

@ -25,6 +25,8 @@
Components.utils.import("resource://gre/modules/InlineSpellChecker.jsm");
import FilePicker from 'zotero/modules/filePicker';
// Note: TinyMCE is automatically doing some meaningless corrections to
// note-editor produced HTML. Which might result to more
// conflicts, especially in group libraries
@ -43,9 +45,6 @@ const DOWNLOADED_IMAGE_TYPE = [
'image/png'
];
// Schema version here has to be the same as in note-editor!
const SCHEMA_VERSION = 8;
class EditorInstance {
constructor() {
this.instanceID = Zotero.Utilities.randomString();
@ -89,6 +88,76 @@ class EditorInstance {
return doc.execCommand(command, ui, value);
};
// Translate note HTML into Markdown, for setting it as text/plain in clipboard (on text copy/drag)
this._iframeWindow.wrappedJSObject.zoteroTranslateToMarkdown = (html) => {
let item = new Zotero.Item('note');
item.libraryID = this._item.libraryID;
item.setNote(html);
let text = '';
var translation = new Zotero.Translate.Export;
translation.noWait = true;
translation.setItems([item]);
translation.setTranslator(Zotero.Translators.TRANSLATOR_ID_NOTE_MARKDOWN);
translation.setHandler("done", (obj, worked) => {
if (worked) {
text = obj.string.replace(/\r\n/g, '\n');
}
});
translation.translate();
return text;
};
this._iframeWindow.wrappedJSObject.zoteroCopyImage = async (dataURL) => {
let parts = dataURL.split(',');
if (!parts[0].includes('base64')) {
return;
}
let mime = parts[0].match(/:(.*?);/)[1];
let bstr = atob(parts[1]);
let n = bstr.length;
let u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
let imgTools = Components.classes["@mozilla.org/image/tools;1"]
.getService(Components.interfaces.imgITools);
let transferable = Components.classes['@mozilla.org/widget/transferable;1']
.createInstance(Components.interfaces.nsITransferable);
let clipboardService = Components.classes['@mozilla.org/widget/clipboard;1']
.getService(Components.interfaces.nsIClipboard);
let imgPtr = Components.classes["@mozilla.org/supports-interface-pointer;1"]
.createInstance(Components.interfaces.nsISupportsInterfacePointer);
imgPtr.data = imgTools.decodeImageFromArrayBuffer(u8arr.buffer, mime);
transferable.init(null);
transferable.addDataFlavor(mime);
transferable.setTransferData(mime, imgPtr, 0);
clipboardService.setData(transferable, null, Components.interfaces.nsIClipboard.kGlobalClipboard);
};
this._iframeWindow.wrappedJSObject.zoteroSaveImageAs = async (dataURL) => {
let parts = dataURL.split(',');
if (!parts[0].includes('base64')) {
return;
}
let mime = parts[0].match(/:(.*?);/)[1];
let bstr = atob(parts[1]);
let n = bstr.length;
let u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
let ext = Zotero.MIME.getPrimaryExtension(mime, '');
let fp = new FilePicker();
fp.init(this._iframeWindow, Zotero.getString('noteEditor.saveImageAs'), fp.modeSave);
fp.appendFilters(fp.filterImages);
fp.defaultString = Zotero.getString('fileTypes.image').toLowerCase() + '.' + ext;
let rv = await fp.show();
if (rv === fp.returnOK || rv === fp.returnReplace) {
let outputPath = fp.file;
await OS.File.writeAtomic(outputPath, u8arr);
}
};
this._iframeWindow.addEventListener('message', this._messageHandler);
this._iframeWindow.addEventListener('error', (event) => {
Zotero.logError(event.error);
@ -697,6 +766,9 @@ class EditorInstance {
}
menuitem.setAttribute('checked', item.checked);
menuitem.addEventListener('command', () => {
if (item.name === 'insertImage') {
return this._iframeWindow.eval('openImageFilePicker()');
}
this._postMessage({
action: 'contextMenuAction',
ctxAction: item.name,
@ -1193,10 +1265,12 @@ class EditorInstance {
* Create note from annotations
*
* @param {Zotero.Item[]} annotations
* @param {Integer} parentID Creates standalone note if not provided
* @param {Object} options
* @param {Integer} options.parentID - Creates standalone note if not provided
* @param {Integer} options.collectionID - Only valid if parentID not provided
* @returns {Promise<Zotero.Item>}
*/
static async createNoteFromAnnotations(annotations, parentID) {
static async createNoteFromAnnotations(annotations, { parentID, collectionID } = {}) {
if (!annotations.length) {
throw new Error("No annotations provided");
}
@ -1217,7 +1291,12 @@ class EditorInstance {
let note = new Zotero.Item('note');
note.libraryID = annotations[0].libraryID;
note.parentID = parentID;
if (parentID) {
note.parentID = parentID;
}
else if (collectionID) {
note.addToCollection(collectionID);
}
await note.saveTx();
let editorInstance = new EditorInstance();
editorInstance._item = note;
@ -1239,10 +1318,61 @@ class EditorInstance {
html += '\n';
await editorInstance.importImages(jsonAnnotations);
let { html: serializedHTML, citationItems } = Zotero.EditorInstanceUtilities.serializeAnnotations(jsonAnnotations, true);
html += serializedHTML;
let multipleParentParent = false;
let lastParentParentID;
let lastParentID;
// Group annotations per attachment
let groups = [];
for (let i = 0; i < annotations.length; i++) {
let annotation = annotations[i];
let jsonAnnotation = jsonAnnotations[i];
let parentParentID = annotation.parentItem.parentID;
let parentID = annotation.parentID;
if (groups.length) {
if (parentParentID !== lastParentParentID) {
// Multiple top level regular items detected, allow including their titles
multipleParentParent = true;
}
}
if (!groups.length || parentID !== lastParentID) {
groups.push({
parentTitle: annotation.parentItem.getDisplayTitle(),
parentParentID,
parentParentTitle: annotation.parentItem.parentItem && annotation.parentItem.parentItem.getDisplayTitle(),
jsonAnnotations: [jsonAnnotation]
});
}
else {
let group = groups[groups.length - 1];
group.jsonAnnotations.push(jsonAnnotation);
}
lastParentParentID = parentParentID;
lastParentID = parentID;
}
let citationItems = [];
lastParentParentID = null;
for (let group of groups) {
if (multipleParentParent && group.parentParentTitle && lastParentParentID !== group.parentParentID) {
html += `<h2>${group.parentParentTitle}</h2>\n`;
}
lastParentParentID = group.parentParentID;
// If attachment doesn't have a parent or there are more attachments with the same parent, show attachment title
if (!group.parentParentID || groups.filter(x => x.parentParentID === group.parentParentID).length > 1) {
html += `<h3>${group.parentTitle}</h3>\n`;
}
let { html: _html, citationItems: _citationItems } = Zotero.EditorInstanceUtilities.serializeAnnotations(group.jsonAnnotations, true);
html += _html + '\n';
for (let _citationItem of _citationItems) {
if (!citationItems.find(item => item.uris.some(uri => _citationItem.uris.includes(uri)))) {
citationItems.push(_citationItem);
}
}
}
citationItems = encodeURIComponent(JSON.stringify(citationItems));
html = `<div data-citation-items="${citationItems}" data-schema-version="${SCHEMA_VERSION}">${html}</div>`;
// Note: Update schema version only if using new features
let schemaVersion = 8;
html = `<div data-citation-items="${citationItems}" data-schema-version="${schemaVersion}">${html}</div>`;
note.setNote(html);
await note.saveTx();
return note;
@ -1268,7 +1398,8 @@ class EditorInstanceUtilities {
if (!annotation.text
&& !annotation.comment
&& !annotation.imageAttachmentKey) {
&& !annotation.imageAttachmentKey
|| annotation.type === 'ink') {
continue;
}
@ -1359,16 +1490,24 @@ class EditorInstanceUtilities {
template = '<p>{{image}}<br/>{{citation}} {{comment}}</p>';
}
Zotero.debug('Using note template:');
Zotero.debug(template);
template = template.replace(
/(<blockquote>[^<>]*?)({{highlight}})([\s\S]*?<\/blockquote>)/g,
(match, p1, p2, p3) => p1 + "{{highlight quotes='false'}}" + p3
);
let vars = {
color: annotation.color || '',
highlight: (attrs) => attrs.quotes === 'true' ? quotedHighlightHTML : highlightHTML,
// Include quotation marks by default, but allow to disable with `quotes='false'`
highlight: (attrs) => attrs.quotes === 'false' ? highlightHTML : quotedHighlightHTML,
comment: commentHTML,
citation: citationHTML,
image: imageHTML,
tags: (attrs) => (annotation.tags && annotation.tags.map(tag => tag.name) || []).join(attrs.join || ' ')
};
Zotero.debug('Using note template:');
Zotero.debug(template);
let templateHTML = Zotero.Utilities.Internal.generateHTMLFromTemplate(template, vars);
// Remove some spaces at the end of paragraph
templateHTML = templateHTML.replace(/([\s]*)(<\/p)/g, '$2');
@ -1529,5 +1668,4 @@ class EditorInstanceUtilities {
}
Zotero.EditorInstance = EditorInstance;
Zotero.EditorInstance.SCHEMA_VERSION = SCHEMA_VERSION;
Zotero.EditorInstanceUtilities = new EditorInstanceUtilities();

View file

@ -1086,7 +1086,31 @@ Zotero.File = new function(){
}
throw e;
}
}
};
/**
* Normalize to a Unix-style path, replacing backslashes (interpreted as
* separators only on Windows) with forward slashes (interpreted as
* separators everywhere)
*
* @param {String} path
* @return {String}
*/
this.normalizeToUnix = function (path) {
// If we're on Windows, we need to normalize first and then replace
// the slashes, because OS.Path.normalize won't handle forward slashes
// correctly. Otherwise, we replace slashes first and *then* normalize.
// This should ensure consistent behavior across platforms.
if (Zotero.isWin) {
let normalized = OS.Path.normalize(path);
return normalized.replace(/\\/g, '/');
}
else {
let replaced = path.replace(/\\/g, '/');
return OS.Path.normalize(replaced);
}
};
/**
@ -1096,8 +1120,8 @@ Zotero.File = new function(){
if (typeof dir != 'string') throw new Error("dir must be a string");
if (typeof file != 'string') throw new Error("file must be a string");
dir = OS.Path.normalize(dir).replace(/\\/g, "/");
file = OS.Path.normalize(file).replace(/\\/g, "/");
dir = this.normalizeToUnix(dir);
file = this.normalizeToUnix(file);
// Normalize D:\ vs. D:\foo
if (dir != file && !dir.endsWith('/')) {
dir += '/';

View file

@ -381,8 +381,8 @@ Zotero.Fulltext = Zotero.FullText = new function(){
}
if (!charset) {
Zotero.logError(`Item ${itemID} didn't have a charset`);
return false;
Zotero.debug(`Item ${itemID} doesn't have a charset set -- using 'utf-8'`);
charset = 'utf-8';
}
var maxLength = Zotero.Prefs.get('fulltext.textMaxLength');

View file

@ -166,46 +166,48 @@ Zotero.HTTP = new function() {
if (e.xmlhttp.status == 503 && await _checkRetry(e.xmlhttp)) {
continue;
}
// Don't retry if errorDelayMax is 0
if (options.errorDelayMax === 0) {
throw e;
}
// Automatically retry other 5xx errors by default
if (options.errorDelayMax !== 0) {
if (!errorDelayGenerator) {
// Keep trying for up to an hour
errorDelayGenerator = Zotero.Utilities.Internal.delayGenerator(
options.errorDelayIntervals || _errorDelayIntervals,
options.errorDelayMax !== undefined
? options.errorDelayMax
: _errorDelayMax
);
if (!errorDelayGenerator) {
// Keep trying for up to an hour
errorDelayGenerator = Zotero.Utilities.Internal.delayGenerator(
options.errorDelayIntervals || _errorDelayIntervals,
options.errorDelayMax !== undefined
? options.errorDelayMax
: _errorDelayMax
);
}
let delayPromise = errorDelayGenerator.next().value;
let keepGoing;
// Provide caller with a callback to cancel while waiting to retry
if (options.cancellerReceiver) {
let resolve;
let reject;
let cancelPromise = new Zotero.Promise((res, rej) => {
resolve = res;
reject = function () {
rej(new Zotero.HTTP.CancelledException);
};
});
options.cancellerReceiver(reject);
try {
keepGoing = await Promise.race([delayPromise, cancelPromise]);
}
let delayPromise = errorDelayGenerator.next().value;
let keepGoing;
// Provide caller with a callback to cancel while waiting to retry
if (options.cancellerReceiver) {
let resolve;
let reject;
let cancelPromise = new Zotero.Promise((res, rej) => {
resolve = res;
reject = function () {
rej(new Zotero.HTTP.CancelledException);
};
});
options.cancellerReceiver(reject);
try {
keepGoing = await Promise.race([delayPromise, cancelPromise]);
}
catch (e) {
Zotero.debug("Request cancelled");
throw e;
}
resolve();
}
else {
keepGoing = await delayPromise;
}
if (!keepGoing) {
Zotero.logError("Failed too many times");
catch (e) {
Zotero.debug("Request cancelled");
throw e;
}
resolve();
}
else {
keepGoing = await delayPromise;
}
if (!keepGoing) {
Zotero.logError("Failed too many times");
throw e;
}
continue;
}
@ -1217,9 +1219,10 @@ Zotero.HTTP = new function() {
* @param {Boolean} dontDelete Don't delete the hidden browser upon completion; calling function
* must call deleteHiddenBrowser itself.
* @param {Zotero.CookieSandbox} [cookieSandbox] Cookie sandbox object
* @param {Object} [docShellPrefs] See Zotero.Browser.createHiddenBrowser
* @return {browser} Hidden browser used for loading
*/
this.loadDocuments = function (urls, processor, onDone, onError, dontDelete, cookieSandbox) {
this.loadDocuments = function (urls, processor, onDone, onError, dontDelete, cookieSandbox, docShellPrefs={}) {
// (Approximately) how many seconds to wait if the document is left in the loading state and
// pageshow is called before we call pageshow with an incomplete document
const LOADING_STATE_TIMEOUT = 120;
@ -1342,6 +1345,9 @@ Zotero.HTTP = new function() {
currentURL = 0;
for(var i=0; i<urls.length; i++) {
let hiddenBrowser = Zotero.Browser.createHiddenBrowser();
for (let pref in docShellPrefs) {
hiddenBrowser.docShell[pref] = docShellPrefs[pref];
}
if (cookieSandbox) {
cookieSandbox.attachToBrowser(hiddenBrowser);
}

View file

@ -798,7 +798,8 @@ Zotero.Integration.Interface.prototype.addEditBibliography = Zotero.Promise.coro
* @return {Promise}
*/
Zotero.Integration.Interface.prototype.refresh = async function() {
await this._session.init(true, false)
await this._session.init(true, false);
this._session.shouldMerge = true;
this._session.reload = this._session.reload || this._session.data.prefs.delayCitationUpdates;
await this._session.updateFromDocument(FORCE_CITATIONS_REGENERATE);
@ -1037,6 +1038,10 @@ Zotero.Integration.Session.prototype.updateFromDocument = Zotero.Promise.corouti
this.progressBar.start();
if (forceCitations) {
this.regenAll = true;
// See Session.restoreProcessorState() for a comment
if (!Zotero.Prefs.get('cite.useCiteprocRs')) {
this.reload = true;
}
}
yield this._processFields();
try {
@ -1067,15 +1072,24 @@ Zotero.Integration.Session.prototype._processFields = async function () {
if (!this._fields) {
throw new Error("_processFields called without fetching fields first");
}
let adjacentCitations = [];
for (var i = 0; i < this._fields.length; i++) {
let field = await Zotero.Integration.Field.loadExisting(this._fields[i]);
if (field.type === INTEGRATION_TYPE_ITEM) {
var noteIndex = await field.getNoteIndex(),
data = await field.unserialize(),
citation = new Zotero.Integration.Citation(field, data, noteIndex);
await this.addCitation(i, noteIndex, citation);
if (this.shouldMerge && typeof field.isAdjacentToNextField === 'function' && await field.isAdjacentToNextField()) {
adjacentCitations.push(citation);
this._deleteFields[i] = true;
continue;
}
await this.addCitation(i, noteIndex, citation, adjacentCitations);
if (adjacentCitations.length) {
adjacentCitations = [];
}
} else if (field.type === INTEGRATION_TYPE_BIBLIOGRAPHY) {
if (this.ignoreEmptyBibliography && (await field.getText()).trim() === "") {
this._removeCodeFields[i] = true;
@ -1957,9 +1971,17 @@ Zotero.Integration.Session.prototype.importDocument = async function() {
/**
* Adds a citation to the arrays representing the document
*/
Zotero.Integration.Session.prototype.addCitation = async function (index, noteIndex, citation) {
Zotero.Integration.Session.prototype.addCitation = async function (index, noteIndex, citation, adjacentCitations=[]) {
index = parseInt(index, 10);
if (adjacentCitations.length) {
Zotero.debug(`Merging adjacent citations ${adjacentCitations.map(c => c.citationID)} to citation ${citation.citationID}`);
for (let adjacentCitation of adjacentCitations) {
citation.mergeCitation(adjacentCitation);
}
this.updateIndices[index] = true;
}
var action = await citation.loadItemData();
if (action == Zotero.Integration.REMOVE_CODE) {
@ -2157,6 +2179,12 @@ Zotero.Integration.Session.prototype.restoreProcessorState = function() {
citations.push(this.citationsByIndex[i]);
}
}
if (!Zotero.Prefs.get('cite.useCiteprocRs')) {
// Due to a bug in citeproc-js there are disambiguation issues after changing items in Zotero library
// and rebuilding the processor state, so we reinitialize the processor altogether
let style = Zotero.Styles.get(this.data.style.styleID);
this.style = style.getCiteProc(this.data.style.locale, this.outputFormat, this.data.prefs.automaticJournalAbbreviations);
}
this.style.rebuildProcessorState(citations, this.outputFormat, uncited);
}
@ -2623,7 +2651,7 @@ Zotero.Integration.Field = class {
throw new Error("Trying to instantiate Integration.Field with Integration.Field, not doc field");
}
for (let func of Zotero.Integration.Field.INTERFACE) {
if (!(func in this)) {
if (!(func in this) && (func in field)) {
this[func] = field[func].bind(field);
}
}
@ -2682,7 +2710,7 @@ Zotero.Integration.Field = class {
}
};
Zotero.Integration.Field.INTERFACE = ['delete', 'removeCode', 'select', 'setText',
'getText', 'setCode', 'getCode', 'equals', 'getNoteIndex'];
'getText', 'setCode', 'getCode', 'equals', 'getNoteIndex', 'isAdjacentToNextField'];
/**
* Load existing field in document and return correct instance of field type
@ -2921,6 +2949,23 @@ Zotero.Integration.Citation = class {
this._field = citationField;
}
/**
* Merge citation items and remove duplicates, unless the items have different
* @param citation {Citation}
*/
mergeCitation(citation) {
let items = this.citationItems.concat(citation.citationItems);
let addedItems = new Set();
this.citationItems = []
for (let item of items) {
if (addedItems.has(item.id)) {
continue;
}
addedItems.add(item.id);
this.citationItems.push(item);
}
}
/**
* Load citation item data
* @param {Boolean} [promptToReselect=true] - will throw a MissingItemException if false

View file

@ -55,20 +55,22 @@ Zotero.MIMETypeHandler = new function () {
_observers = [];
// Install styles from the Cite preferences
this.addHandler("application/vnd.citationstyles.style+xml", async function (blob, origin) {
let win = Services.wm.getMostRecentWindow("zotero:basicViewer");
var data = await Zotero.Utilities.Internal.blobToText(blob);
try {
await Zotero.Styles.install(data, origin, true);
}
catch (e) {
Zotero.logError(e);
(new Zotero.Exception.Alert("styles.install.unexpectedError",
origin, "styles.install.title", e)).present();
}
// Close styles page in basic viewer after installing a style
if (win) {
win.close();
this.addHandlers("application/vnd.citationstyles.style+xml", {
onContent: async function (blob, origin) {
let win = Services.wm.getMostRecentWindow("zotero:basicViewer");
var data = await Zotero.Utilities.Internal.blobToText(blob);
try {
await Zotero.Styles.install(data, origin, true);
}
catch (e) {
Zotero.logError(e);
(new Zotero.Exception.Alert("styles.install.unexpectedError",
origin, "styles.install.title", e)).present();
}
// Close styles page in basic viewer after installing a style
if (win) {
win.close();
}
}
}, true);
};
@ -76,17 +78,25 @@ Zotero.MIMETypeHandler = new function () {
/**
* Adds a handler to handle a specific MIME type
* @param {String} type MIME type to handle
* @param {Function} fn Function to call to handle type - fn(string, uri)
* @param {Object} handlers
* - handlers.onContent - function to call when content is received
* - handlers.onStartRequest - function to call when response for content type is first received
* @param {Boolean} ignoreContentDisposition If true, ignores the Content-Disposition header,
* which is often used to force a file to download rather than let it be handled by the web
* browser
*/
this.addHandler = function (type, fn, ignoreContentDisposition) {
this.addHandlers = function (type, handlers, ignoreContentDisposition) {
if (typeof handlers == 'function') {
handlers = {
onContent: handlers
};
Zotero.debug('MIMETypeHandler.addHandler: second parameter function is deprecated. Pass an object', 1)
}
if (_typeHandlers[type]) {
_typeHandlers[type].push(fn);
_typeHandlers[type].push(handlers);
}
else {
_typeHandlers[type] = [fn];
_typeHandlers[type] = [handlers];
}
if (ignoreContentDisposition) {
_ignoreContentDispositionTypes.add(type);
@ -96,19 +106,19 @@ Zotero.MIMETypeHandler = new function () {
/**
* Removes a handler for a specific MIME type
* @param {String} type MIME type to handle
* @param {Function} handler Function handler to remove
* @param {Object} handlers Function handlers to remove
*/
this.removeHandler = function (type, handler) {
this.removeHandlers = function (type, handlers) {
// If no handler specified or this is the last handler for the type
// stop monitoring the content type completely.
if (!handler || _typeHandlers[type] && _typeHandlers[type].length <= 1) {
if (!handlers || _typeHandlers[type] && _typeHandlers[type].length <= 1) {
delete _typeHandlers[type];
_ignoreContentDispositionTypes.delete(type);
}
else if (_typeHandlers[type]) {
var i = _typeHandlers[type].indexOf(handler);
var i = _typeHandlers[type].indexOf(handlers);
if (i != -1) {
_typeHandlers.splice(i, 1);
_typeHandlers[type].splice(i, 1);
}
}
};
@ -214,10 +224,27 @@ Zotero.MIMETypeHandler = new function () {
}
/**
* Called when the request is started; we ignore this
* Called when the request is started
*/
_StreamListener.prototype.onStartRequest = function(channel, context) {}
_StreamListener.prototype.onStartRequest = async function(channel, context) {
try {
if (!_typeHandlers[this._contentType]) return;
for (let handlers of _typeHandlers[this._contentType]) {
if (!handlers.onStartRequest) continue;
let maybePromise = handlers.onStartRequest(
this._request.name ? this._request.name : null,
this._contentType,
channel
);
if (maybePromise && maybePromise.then) {
maybePromise = await maybePromise;
}
}
}
catch (e) {
Zotero.logError(e);
}
}
/**
* Called when there's data available; we collect this data and keep it until the request is
@ -259,14 +286,16 @@ Zotero.MIMETypeHandler = new function () {
var handled = false;
try {
for (let handler of _typeHandlers[this._contentType]) {
let maybePromise = handler(
if (!_typeHandlers[this._contentType]) return;
for (let handlers of _typeHandlers[this._contentType]) {
if (!handlers.onContent) continue;
let maybePromise = handlers.onContent(
blob,
this._request.name ? this._request.name : null,
this._contentType,
channel
);
if (maybePromise.then) {
if (maybePromise && maybePromise.then) {
maybePromise = await maybePromise;
}
handled = handled || maybePromise;

View file

@ -271,6 +271,10 @@ class PDFWorker {
}));
let path = await attachment.getFilePathAsync();
let fileSize = (await OS.File.stat(path)).size;
if (fileSize > Math.pow(2, 31) - 1) {
throw new Error(`The file "${path}" is too large`);
}
let buf = await OS.File.read(path, {});
buf = new Uint8Array(buf).buffer;
@ -341,6 +345,10 @@ class PDFWorker {
async processCitaviAnnotations(pdfPath, citaviAnnotations, isPriority, password) {
return this._enqueue(async () => {
let fileSize = (await OS.File.stat(pdfPath)).size;
if (fileSize > Math.pow(2, 31) - 1) {
throw new Error(`The file "${pdfPath}" is too large`);
}
let buf = await OS.File.read(pdfPath, {});
buf = new Uint8Array(buf).buffer;
try {
@ -371,6 +379,10 @@ class PDFWorker {
*/
async processMendeleyAnnotations(pdfPath, mendeleyAnnotations, isPriority, password) {
return this._enqueue(async () => {
let fileSize = (await OS.File.stat(pdfPath)).size;
if (fileSize > Math.pow(2, 31) - 1) {
throw new Error(`The file "${pdfPath}" is too large`);
}
let buf = await OS.File.read(pdfPath, {});
buf = new Uint8Array(buf).buffer;
try {
@ -410,6 +422,162 @@ class PDFWorker {
}
await Promise.all(promises);
}
/**
* Delete pages from PDF attachment
*
* @param {Integer} itemID Attachment item id
* @param {Array} pageIndexes
* @param {Boolean} [isPriority]
* @param {String} [password]
* @returns {Promise}
*/
async deletePages(itemID, pageIndexes, isPriority, password) {
return this._enqueue(async () => {
let attachment = await Zotero.Items.getAsync(itemID);
Zotero.debug(`Deleting [${pageIndexes.join(', ')}] pages for item ${attachment.libraryKey}`);
let t = new Date();
if (!attachment.isPDFAttachment()) {
throw new Error('Item must be a PDF attachment');
}
let annotations = attachment
.getAnnotations()
.map(annotation => ({
id: annotation.id,
position: JSON.parse(annotation.annotationPosition)
}));
let path = await attachment.getFilePathAsync();
let buf = await OS.File.read(path, {});
buf = new Uint8Array(buf).buffer;
try {
var { buf: modifiedBuf } = await this._query('deletePages', {
buf, pageIndexes, password
}, [buf]);
}
catch (e) {
let error = new Error(`Worker 'deletePages' failed: ${JSON.stringify({ error: e.message })}`);
try {
error.name = JSON.parse(e.message).name;
}
catch (e) {
Zotero.logError(e);
}
Zotero.logError(error);
throw error;
}
// Delete annotations from deleted pages
let ids = [];
for (let i = annotations.length - 1; i >= 0; i--) {
let { id, position } = annotations[i];
if (pageIndexes.includes(position.pageIndex)) {
ids.push(id);
annotations.splice(i, 1);
}
}
if (ids.length) {
await Zotero.Items.erase(ids);
}
// Shift page index for other annotations
ids = [];
await Zotero.DB.executeTransaction(async function () {
let rows = await Zotero.DB.queryAsync('SELECT itemID, position FROM itemAnnotations WHERE parentItemID=?', itemID);
for (let { itemID, position } of rows) {
try {
position = JSON.parse(position);
}
catch (e) {
Zotero.logError(e);
continue;
}
// Find the count of deleted pages before the current annotation page
let shift = pageIndexes.reduce((prev, cur) => cur < position.pageIndex ? prev + 1 : prev, 0);
if (shift > 0) {
position.pageIndex -= shift;
position = JSON.stringify(position);
await Zotero.DB.queryAsync('UPDATE itemAnnotations SET position=? WHERE itemID=?', [position, itemID]);
ids.push(itemID);
}
}
});
let objectsClass = Zotero.DataObjectUtilities.getObjectsClassForObjectType('item');
let loadedObjects = objectsClass.getLoaded();
for (let object of loadedObjects) {
if (ids.includes(object.id)) {
await object.reload(null, true);
}
}
await Zotero.Notifier.trigger('modify', 'item', ids, {});
await OS.File.writeAtomic(path, new Uint8Array(modifiedBuf));
let mtime = Math.floor(await attachment.attachmentModificationTime / 1000);
attachment.attachmentLastProcessedModificationTime = mtime;
await attachment.saveTx({
skipAll: true
});
Zotero.debug(`Deleted pages for item ${attachment.libraryKey} in ${new Date() - t} ms`);
}, isPriority);
}
/**
* Rotate pages in PDF attachment
*
* @param {Integer} itemID Attachment item id
* @param {Array} pageIndexes
* @param {Integer} degrees 90, 180, 270
* @param {Boolean} [isPriority]
* @param {String} [password]
* @returns {Promise}
*/
async rotatePages(itemID, pageIndexes, degrees, isPriority, password) {
return this._enqueue(async () => {
let attachment = await Zotero.Items.getAsync(itemID);
Zotero.debug(`Rotating [${pageIndexes.join(', ')}] pages for item ${attachment.libraryKey}`);
let t = new Date();
if (!attachment.isPDFAttachment()) {
throw new Error('Item must be a PDF attachment');
}
let path = await attachment.getFilePathAsync();
let buf = await OS.File.read(path, {});
buf = new Uint8Array(buf).buffer;
try {
var { buf: modifiedBuf } = await this._query('rotatePages', {
buf, pageIndexes, degrees, password
}, [buf]);
}
catch (e) {
let error = new Error(`Worker 'rotatePages' failed: ${JSON.stringify({ error: e.message })}`);
try {
error.name = JSON.parse(e.message).name;
}
catch (e) {
Zotero.logError(e);
}
Zotero.logError(error);
throw error;
}
await OS.File.writeAtomic(path, new Uint8Array(modifiedBuf));
let mtime = Math.floor(await attachment.attachmentModificationTime / 1000);
attachment.attachmentLastProcessedModificationTime = mtime;
await attachment.saveTx({
skipAll: true
});
Zotero.debug(`Rotated pages for item ${attachment.libraryKey} in ${new Date() - t} ms`);
}, isPriority);
}
}
Zotero.PDFWorker = new PDFWorker();

View file

@ -49,7 +49,7 @@ Zotero.Prefs = new function(){
if (!fromVersion) {
fromVersion = 0;
}
var toVersion = 5;
var toVersion = 6;
if (fromVersion < toVersion) {
for (var i = fromVersion + 1; i <= toVersion; i++) {
switch (i) {
@ -85,6 +85,24 @@ Zotero.Prefs = new function(){
case 5:
this.clear('extensions.spellcheck.inline.max-misspellings', true);
break;
// If the note Quick Copy setting was set to Markdown + Rich Text but in a
// non-default way (e.g., because of whitespace differences), clear the pref
// to pick up the new app-link options
case 6:
var o = this.get('export.noteQuickCopy.setting');
try {
o = JSON.parse(o);
if (o.mode == 'export'
&& o.id == Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) {
this.clear('export.noteQuickCopy.setting');
}
}
catch (e) {
Zotero.logError(e);
this.clear('export.noteQuickCopy.setting');
}
break;
}
}
this.set('prefVersion', toVersion);
@ -230,6 +248,7 @@ Zotero.Prefs = new function(){
Zotero.setFontSize(
Zotero.getActiveZoteroPane().document.getElementById('zotero-pane')
);
Zotero.setFontSize(Zotero.getActiveZoteroPane().document.getElementById('zotero-context-pane'));
Zotero.getActiveZoteroPane().collectionsView && Zotero.getActiveZoteroPane().collectionsView.updateFontSize();
Zotero.getActiveZoteroPane().itemsView && Zotero.getActiveZoteroPane().itemsView.updateFontSize();
}],
@ -358,12 +377,13 @@ Zotero.Prefs = new function(){
delete _observersBySymbol[symbol];
var [name, handler] = obs;
var i = obs.indexOf(handler);
var handlers = _observers[name];
var i = handlers.indexOf(handler);
if (i == -1) {
Zotero.debug("Handler was not registered for preference " + name, 2);
return;
}
obs.splice(i, 1);
handlers.splice(i, 1);
}

View file

@ -269,6 +269,9 @@ Zotero.QuickCopy = new function() {
// Allow to reuse items array
translation.setItems(items.slice());
translation.setTranslator(format.id);
if (format.options) {
translation.setDisplayOptions(format.options);
}
translation.setHandler("done", callback);
translation.translate();
return true;
@ -322,14 +325,19 @@ Zotero.QuickCopy = new function() {
var _loadNoteOutputFormat = async function () {
var format = Zotero.Prefs.get("export.noteQuickCopy.setting");
format = Zotero.QuickCopy.unserializeSetting(format);
// If virtual "Markdown + Rich Text" translator is selected, preload Note Markdown and
// Note HTML translators
if (format.id == Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) {
await _preloadFormat({ mode: 'export', id: Zotero.Translators.TRANSLATOR_ID_NOTE_MARKDOWN });
await _preloadFormat({ mode: 'export', id: Zotero.Translators.TRANSLATOR_ID_NOTE_HTML });
return;
// Always preload Note Markdown and Note HTML translators. They're both needed for note
// dragging if the format is "Markdown + Rich Text", HTML is needed for note dragging if
// the format is "HTML", and they're both needed for copying or dragging from the note
// editor, which uses `noWait`.
await _preloadFormat({ mode: 'export', id: Zotero.Translators.TRANSLATOR_ID_NOTE_MARKDOWN });
await _preloadFormat({ mode: 'export', id: Zotero.Translators.TRANSLATOR_ID_NOTE_HTML });
// If there's another format, preload it for note item dragging
if (format.id != Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT
&& format.id != Zotero.Translators.TRANSLATOR_ID_NOTE_HTML) {
await _preloadFormat(format);
}
return _preloadFormat(format);
};

View file

@ -23,6 +23,8 @@
***** END LICENSE BLOCK *****
*/
import FilePicker from 'zotero/modules/filePicker';
class ReaderInstance {
constructor() {
this.pdfStateFileName = '.zotero-pdf-state';
@ -50,7 +52,48 @@ class ReaderInstance {
}
}
async open({ itemID, state, location }) {
getSecondViewState() {
let state = this._iframeWindow.wrappedJSObject.getSecondViewState();
return state ? JSON.parse(JSON.stringify(state)) : undefined;
}
async migrateMendeleyColors(libraryID, annotations) {
let colorMap = new Map();
colorMap.set('#fff5ad', '#ffd400');
colorMap.set('#ffb5b6', '#ff6666');
colorMap.set('#bae2ff', '#2ea8e5');
colorMap.set('#d3c2ff', '#a28ae5');
colorMap.set('#dcffb0', '#5fb236');
let updatedAnnotations = [];
for (let annotation of annotations) {
let color = colorMap.get(annotation.color);
if (color) {
annotation.color = color;
updatedAnnotations.push(annotation);
}
}
if (!updatedAnnotations.length) {
return false;
}
Zotero.debug('Migrating Mendeley colors');
let notifierQueue = new Zotero.Notifier.Queue();
try {
for (let annotation of updatedAnnotations) {
let { id: key, color } = annotation;
let item = Zotero.Items.getByLibraryAndKey(libraryID, key);
if (item && item.isEditable()) {
item.annotationColor = color;
await item.saveTx({ skipDateModifiedUpdate: true, notifierQueue });
}
}
}
finally {
await Zotero.Notifier.commit(notifierQueue);
}
return true;
}
async open({ itemID, state, location, secondViewState }) {
let { libraryID } = Zotero.Items.getLibraryAndKeyFromID(itemID);
let library = Zotero.Libraries.get(libraryID);
await library.waitForDataLoad('item');
@ -64,10 +107,27 @@ class ReaderInstance {
// Set `ReaderTab` title as fast as possible
this.updateTitle();
let path = await item.getFilePathAsync();
// Check file size, otherwise we get uncatchable error:
// JavaScript error: resource://gre/modules/osfile/osfile_native.jsm, line 60: RangeError: invalid array length
// See more https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_array_length
let fileSize = (await OS.File.stat(path)).size;
// Max ArrayBuffer size before fx89 is 2GB-1 bytes
if (fileSize > Math.pow(2, 31) - 1) {
throw new Error(`The file "${path}" is too large`);
}
let buf = await OS.File.read(path, {});
buf = new Uint8Array(buf).buffer;
let annotationItems = item.getAnnotations();
let annotations = (await Promise.all(annotationItems.map(x => this._getAnnotation(x)))).filter(x => x);
// TODO: Remove after some time
// Migrate Mendeley colors to Zotero PDF reader colors
let migrated = await this.migrateMendeleyColors(libraryID, annotations);
if (migrated) {
annotationItems = item.getAnnotations();
annotations = (await Promise.all(annotationItems.map(x => this._getAnnotation(x)))).filter(x => x);
}
this.annotationItemIDs = annotationItems.map(x => x.id);
state = state || await this._getState();
this._postMessage({
@ -75,6 +135,7 @@ class ReaderInstance {
buf,
annotations,
state,
secondViewState,
location,
readOnly: this._isReadOnly(),
authorName: item.library.libraryType === 'group' ? Zotero.Users.getCurrentName() : '',
@ -83,6 +144,7 @@ class ReaderInstance {
sidebarOpen: this._sidebarOpen,
bottomPlaceholderHeight: this._bottomPlaceholderHeight,
rtl: Zotero.rtl,
fontSize: Zotero.Prefs.get('fontSize'),
localizedStrings: {
...Zotero.Intl.getPrefixedStrings('general.'),
...Zotero.Intl.getPrefixedStrings('pdfReader.')
@ -90,8 +152,19 @@ class ReaderInstance {
}, [buf]);
// Set title once again, because `ReaderWindow` isn't loaded the first time
this.updateTitle();
this._prefObserverIDs = [
Zotero.Prefs.registerObserver('fontSize', this._handleFontSizeChange)
];
return true;
}
uninit() {
if (this._prefObserverIDs) {
this._prefObserverIDs.forEach(id => Zotero.Prefs.unregisterObserver(id));
}
}
get itemID() {
return this._itemID;
@ -201,6 +274,14 @@ class ReaderInstance {
isZoomPageHeightActive() {
return this._iframeWindow.eval('PDFViewerApplication.pdfViewer.currentScaleValue === "page-fit"');
}
isSplitVerticallyActive() {
return this._iframeWindow.wrappedJSObject.getSplitType() === 'vertical';
}
isSplitHorizontallyActive() {
return this._iframeWindow.wrappedJSObject.getSplitType() === 'horizontal';
}
allowNavigateFirstPage() {
return this._iframeWindow.eval('PDFViewerApplication.pdfViewer.currentPageNumber > 1');
@ -249,6 +330,33 @@ class ReaderInstance {
);
return !index;
}
promptToDeletePages(num) {
let ps = Services.prompt;
let buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING
+ ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL;
let index = ps.confirmEx(
null,
Zotero.getString('pdfReader.promptDeletePages.title'),
Zotero.getString(
'pdfReader.promptDeletePages.text',
new Intl.NumberFormat().format(num),
num
),
buttonFlags,
Zotero.getString('general.continue'),
null, null, null, {}
);
return !index;
}
async reload(data) {
let item = Zotero.Items.get(this._itemID);
let path = await item.getFilePathAsync();
let buf = await OS.File.read(path, {});
buf = new Uint8Array(buf).buffer;
this._postMessage({ action: 'reload', buf, data }, [buf]);
}
async menuCmd(cmd) {
if (cmd === 'transferFromPDF') {
@ -281,6 +389,24 @@ class ReaderInstance {
}
return;
}
else if (cmd === 'rotateLeft') {
await this._rotateCurrentPage(270);
return;
}
else if (cmd === 'rotateRight') {
await this._rotateCurrentPage(90);
return;
}
else if (cmd === 'rotate180') {
await this._rotateCurrentPage(180);
return;
}
else if (cmd === 'splitVertically') {
this._splitVertically();
}
else if (cmd === 'splitHorizontally') {
this._splitHorizontally();
}
let data = {
action: 'menuCmd',
@ -317,8 +443,8 @@ class ReaderInstance {
// If exporting with virtual "Markdown + Rich Text" translator, call Note Markdown
// and Note HTML translators instead
if (format.id === Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) {
let markdownFormat = { mode: 'export', id: Zotero.Translators.TRANSLATOR_ID_NOTE_MARKDOWN };
let htmlFormat = { mode: 'export', id: Zotero.Translators.TRANSLATOR_ID_NOTE_HTML };
let markdownFormat = { mode: 'export', id: Zotero.Translators.TRANSLATOR_ID_NOTE_MARKDOWN, options: format.markdownOptions };
let htmlFormat = { mode: 'export', id: Zotero.Translators.TRANSLATOR_ID_NOTE_HTML, options: format.htmlOptions };
Zotero.QuickCopy.getContentFromItems(items, markdownFormat, (obj, worked) => {
if (!worked) {
return;
@ -369,6 +495,54 @@ class ReaderInstance {
);
return !index;
};
this._iframeWindow.wrappedJSObject.zoteroCopyImage = async (dataURL) => {
let parts = dataURL.split(',');
if (!parts[0].includes('base64')) {
return;
}
let bstr = atob(parts[1]);
let n = bstr.length;
let u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
let imgTools = Components.classes["@mozilla.org/image/tools;1"]
.getService(Components.interfaces.imgITools);
let transferable = Components.classes['@mozilla.org/widget/transferable;1']
.createInstance(Components.interfaces.nsITransferable);
let clipboardService = Components.classes['@mozilla.org/widget/clipboard;1']
.getService(Components.interfaces.nsIClipboard);
let imgPtr = Components.classes["@mozilla.org/supports-interface-pointer;1"]
.createInstance(Components.interfaces.nsISupportsInterfacePointer);
let mimeType = `image/png`;
imgPtr.data = imgTools.decodeImageFromArrayBuffer(u8arr.buffer, mimeType);
transferable.init(null);
transferable.addDataFlavor(mimeType);
transferable.setTransferData(mimeType, imgPtr, 0);
clipboardService.setData(transferable, null, Components.interfaces.nsIClipboard.kGlobalClipboard);
};
this._iframeWindow.wrappedJSObject.zoteroSaveImageAs = async (dataURL) => {
let fp = new FilePicker();
fp.init(this._iframeWindow, Zotero.getString('pdfReader.saveImageAs'), fp.modeSave);
fp.appendFilter("PNG", "*.png");
fp.defaultString = Zotero.getString('fileTypes.image').toLowerCase() + '.png';
let rv = await fp.show();
if (rv === fp.returnOK || rv === fp.returnReplace) {
let outputPath = fp.file;
let parts = dataURL.split(',');
if (parts[0].includes('base64')) {
let bstr = atob(parts[1]);
let n = bstr.length;
let u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
await OS.File.writeAtomic(outputPath, u8arr);
}
}
};
}
async _setState(state) {
@ -424,6 +598,10 @@ class ReaderInstance {
|| item.parentItem && item.parentItem.deleted;
}
_handleFontSizeChange = () => {
this._postMessage({ action: 'setFontSize', fontSize: Zotero.Prefs.get('fontSize') });
};
_dataURLtoBlob(dataurl) {
let parts = dataurl.split(',');
let mime = parts[0].match(/:(.*?);/)[1];
@ -444,6 +622,33 @@ class ReaderInstance {
return `data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><rect shape-rendering="geometricPrecision" fill="${fill}" stroke-width="2" x="2" y="2" stroke="${stroke}" width="12" height="12" rx="3"/></svg>`;
}
async _rotateCurrentPage(degrees) {
let pageIndex = this._iframeWindow.wrappedJSObject.PDFViewerApplication.pdfViewer.currentPageNumber - 1;
this._postMessage({ action: 'reloading' });
await Zotero.PDFWorker.rotatePages(this._itemID, [pageIndex], degrees, true);
await this.reload({ rotatedPageIndexes: [pageIndex] });
}
_splitVertically() {
if (this.isSplitVerticallyActive()) {
this._iframeWindow.wrappedJSObject.unsplitView();
}
else {
this._iframeWindow.wrappedJSObject.splitView();
}
setTimeout(() => this._updateSecondViewState(), 500);
}
_splitHorizontally() {
if (this.isSplitHorizontallyActive()) {
this._iframeWindow.wrappedJSObject.unsplitView();
}
else {
this._iframeWindow.wrappedJSObject.splitView(true);
}
setTimeout(() => this._updateSecondViewState(), 500);
}
_openTagsPopup(item, selector) {
let menupopup = this._window.document.createElement('menupopup');
menupopup.className = 'tags-popup';
@ -462,7 +667,7 @@ class ReaderInstance {
}
}
_openPagePopup(data) {
_openPagePopup(data, secondView) {
let popup = this._window.document.createElement('menupopup');
this._popupset.appendChild(popup);
popup.addEventListener('popuphidden', function () {
@ -483,14 +688,14 @@ class ReaderInstance {
menuitem = this._window.document.createElement('menuitem');
menuitem.setAttribute('label', Zotero.getString('pdfReader.zoomIn'));
menuitem.addEventListener('command', () => {
this._postMessage({ action: 'popupCmd', cmd: 'zoomIn' });
this._postMessage({ action: 'popupCmd', cmd: 'zoomIn' }, [], secondView);
});
popup.appendChild(menuitem);
// Zoom out
menuitem = this._window.document.createElement('menuitem');
menuitem.setAttribute('label', Zotero.getString('pdfReader.zoomOut'));
menuitem.addEventListener('command', () => {
this._postMessage({ action: 'popupCmd', cmd: 'zoomOut' });
this._postMessage({ action: 'popupCmd', cmd: 'zoomOut' }, [], secondView);
});
popup.appendChild(menuitem);
// Zoom 'Auto'
@ -499,7 +704,7 @@ class ReaderInstance {
menuitem.setAttribute('type', 'checkbox');
menuitem.setAttribute('checked', data.isZoomAuto);
menuitem.addEventListener('command', () => {
this._postMessage({ action: 'popupCmd', cmd: 'zoomAuto' });
this._postMessage({ action: 'popupCmd', cmd: 'zoomAuto' }, [], secondView);
});
popup.appendChild(menuitem);
// Zoom 'Page Width'
@ -508,7 +713,7 @@ class ReaderInstance {
menuitem.setAttribute('type', 'checkbox');
menuitem.setAttribute('checked', data.isZoomPageWidth);
menuitem.addEventListener('command', () => {
this._postMessage({ action: 'popupCmd', cmd: 'zoomPageWidth' });
this._postMessage({ action: 'popupCmd', cmd: 'zoomPageWidth' }, [], secondView);
});
popup.appendChild(menuitem);
// Zoom 'Page Height'
@ -517,17 +722,33 @@ class ReaderInstance {
menuitem.setAttribute('type', 'checkbox');
menuitem.setAttribute('checked', data.isZoomPageHeight);
menuitem.addEventListener('command', () => {
this._postMessage({ action: 'popupCmd', cmd: 'zoomPageHeight' });
this._postMessage({ action: 'popupCmd', cmd: 'zoomPageHeight' }, [], secondView);
});
popup.appendChild(menuitem);
// Separator
popup.appendChild(this._window.document.createElement('menuseparator'));
// Split Horizontally
menuitem = this._window.document.createElement('menuitem');
menuitem.setAttribute('label', Zotero.getString('pdfReader.splitHorizontally'));
menuitem.setAttribute('type', 'checkbox');
menuitem.setAttribute('checked', this.isSplitHorizontallyActive());
menuitem.addEventListener('command', () => this._splitHorizontally());
popup.appendChild(menuitem);
// Split Vertically
menuitem = this._window.document.createElement('menuitem');
menuitem.setAttribute('label', Zotero.getString('pdfReader.splitVertically'));
menuitem.setAttribute('type', 'checkbox');
menuitem.setAttribute('checked', this.isSplitVerticallyActive());
menuitem.addEventListener('command', () => this._splitVertically());
popup.appendChild(menuitem);
// Separator
popup.appendChild(this._window.document.createElement('menuseparator'));
// Next page
menuitem = this._window.document.createElement('menuitem');
menuitem.setAttribute('label', Zotero.getString('pdfReader.nextPage'));
menuitem.setAttribute('disabled', !data.enableNextPage);
menuitem.addEventListener('command', () => {
this._postMessage({ action: 'popupCmd', cmd: 'nextPage' });
this._postMessage({ action: 'popupCmd', cmd: 'nextPage' }, [], secondView);
});
popup.appendChild(menuitem);
// Previous page
@ -535,7 +756,7 @@ class ReaderInstance {
menuitem.setAttribute('label', Zotero.getString('pdfReader.previousPage'));
menuitem.setAttribute('disabled', !data.enablePrevPage);
menuitem.addEventListener('command', () => {
this._postMessage({ action: 'popupCmd', cmd: 'prevPage' });
this._postMessage({ action: 'popupCmd', cmd: 'prevPage' }, [], secondView);
});
popup.appendChild(menuitem);
popup.openPopupAtScreen(data.x, data.y, true);
@ -614,6 +835,26 @@ class ReaderInstance {
}
// Separator
popup.appendChild(this._window.document.createElement('menuseparator'));
if (data.enableImageOptions) {
// Copy Image
menuitem = this._window.document.createElement('menuitem');
menuitem.setAttribute('label', Zotero.getString('pdfReader.copyImage'));
menuitem.addEventListener('command', () => {
this._postMessage({ action: 'popupCmd', cmd: 'copyImage', data });
});
popup.appendChild(menuitem);
// Save Image As…
menuitem = this._window.document.createElement('menuitem');
menuitem.setAttribute('label', Zotero.getString('pdfReader.saveImageAs'));
menuitem.addEventListener('command', () => {
this._postMessage({ action: 'popupCmd', cmd: 'saveImageAs', data });
});
popup.appendChild(menuitem);
// Separator
popup.appendChild(this._window.document.createElement('menuseparator'));
}
// Delete
menuitem = this._window.document.createElement('menuitem');
menuitem.setAttribute('label', Zotero.getString('general.delete'));
@ -661,6 +902,64 @@ class ReaderInstance {
popup.openPopup(element, 'after_start', 0, 0, true);
}
_openThumbnailPopup(data) {
let popup = this._window.document.createElement('menupopup');
this._popupset.appendChild(popup);
popup.addEventListener('popuphidden', function () {
popup.remove();
});
let menuitem;
// Rotate Left
menuitem = this._window.document.createElement('menuitem');
menuitem.setAttribute('label', Zotero.getString('pdfReader.rotateLeft'));
menuitem.setAttribute('disabled', this._isReadOnly());
menuitem.addEventListener('command', async () => {
this._postMessage({ action: 'reloading' });
await Zotero.PDFWorker.rotatePages(this._itemID, data.pageIndexes, 270, true);
await this.reload({ rotatedPageIndexes: data.pageIndexes });
});
popup.appendChild(menuitem);
// Rotate Right
menuitem = this._window.document.createElement('menuitem');
menuitem.setAttribute('label', Zotero.getString('pdfReader.rotateRight'));
menuitem.setAttribute('disabled', this._isReadOnly());
menuitem.addEventListener('command', async () => {
this._postMessage({ action: 'reloading' });
await Zotero.PDFWorker.rotatePages(this._itemID, data.pageIndexes, 90, true);
await this.reload({ rotatedPageIndexes: data.pageIndexes });
});
popup.appendChild(menuitem);
// Rotate 180
menuitem = this._window.document.createElement('menuitem');
menuitem.setAttribute('label', Zotero.getString('pdfReader.rotate180'));
menuitem.setAttribute('disabled', this._isReadOnly());
menuitem.addEventListener('command', async () => {
this._postMessage({ action: 'reloading' });
await Zotero.PDFWorker.rotatePages(this._itemID, data.pageIndexes, 180, true);
await this.reload({ rotatedPageIndexes: data.pageIndexes });
});
popup.appendChild(menuitem);
// Separator
popup.appendChild(this._window.document.createElement('menuseparator'));
// Delete
menuitem = this._window.document.createElement('menuitem');
menuitem.setAttribute('label', Zotero.getString('general.delete'));
menuitem.setAttribute('disabled', this._isReadOnly());
menuitem.addEventListener('command', async () => {
if (this.promptToDeletePages(data.pageIndexes.length)) {
this._postMessage({ action: 'reloading' });
try {
await Zotero.PDFWorker.deletePages(this._itemID, data.pageIndexes, true);
}
catch (e) {
}
await this.reload();
}
});
popup.appendChild(menuitem);
popup.openPopupAtScreen(data.x, data.y, true);
}
_openSelectorPopup(data) {
let popup = this._window.document.createElement('menupopup');
this._popupset.appendChild(popup);
@ -683,25 +982,52 @@ class ReaderInstance {
popup.openPopupAtScreen(data.x, data.y, true);
}
async _postMessage(message, transfer) {
async _postMessage(message, transfer, secondView) {
await this._waitForReader();
this._iframeWindow.postMessage({ itemID: this._itemID, message }, this._iframeWindow.origin, transfer);
this._iframeWindow.postMessage({ itemID: this._itemID, message, secondView }, this._iframeWindow.origin, transfer);
}
_updateSecondViewState() {
if (this.tabID) {
let win = Zotero.getMainWindow();
if (win) {
win.Zotero_Tabs.setSecondViewState(this.tabID, this.getSecondViewState());
}
}
}
_handleMessage = async (event) => {
let message;
let secondViewIframeWindow = this._iframeWindow.document.getElementById('secondViewIframe');
if (secondViewIframeWindow) {
secondViewIframeWindow = secondViewIframeWindow.contentWindow;
}
try {
if (event.source !== this._iframeWindow) {
if (event.source !== this._iframeWindow
&& event.source !== secondViewIframeWindow) {
return;
}
// Clone data to avoid the dead object error when the window is closed
let data = JSON.parse(JSON.stringify(event.data));
let { secondView } = data;
// Filter messages coming from previous reader instances,
// except for `setAnnotation` to still allow saving it
if (data.itemID !== this._itemID && data.message.action !== 'setAnnotation') {
return;
}
message = data.message;
if (secondView) {
switch (message.action) {
case 'openPagePopup': break;
case 'setState': {
this._updateSecondViewState();
return;
}
default: return;
}
}
switch (message.action) {
case 'initialized': {
this._resolveInitPromise();
@ -725,25 +1051,22 @@ class ReaderInstance {
saveOptions.notifierData.autoSyncDelay = Zotero.Notes.AUTO_SYNC_DELAY;
}
// Note: annotation.image is always saved separately from the rest
// of annotation properties
let item = Zotero.Items.getByLibraryAndKey(attachment.libraryID, annotation.key);
// Save image for read-only annotation.
if (item
&& !item.isEditable()
&& annotation.image
&& !await Zotero.Annotations.hasCacheImage(item)
) {
// If annotation isn't editable, only save image to cache.
// This is the only case when saving can be triggered for non-editable annotation
if (annotation.image && item && !item.isEditable()) {
let blob = this._dataURLtoBlob(annotation.image);
await Zotero.Annotations.saveCacheImage(item, blob);
continue;
}
let savedAnnotation = await Zotero.Annotations.saveFromJSON(attachment, annotation, saveOptions);
if (annotation.image && !await Zotero.Annotations.hasCacheImage(savedAnnotation)) {
let blob = this._dataURLtoBlob(annotation.image);
await Zotero.Annotations.saveCacheImage(savedAnnotation, blob);
// Save annotation, and save image to cache
else {
// Delete authorName to prevent setting annotationAuthorName unnecessarily
delete annotation.authorName;
let savedAnnotation = await Zotero.Annotations.saveFromJSON(attachment, annotation, saveOptions);
if (annotation.image) {
let blob = this._dataURLtoBlob(annotation.image);
await Zotero.Annotations.saveCacheImage(savedAnnotation, blob);
}
}
}
}
@ -788,7 +1111,7 @@ class ReaderInstance {
return;
}
case 'openPagePopup': {
this._openPagePopup(message.data);
this._openPagePopup(message.data, secondView);
return;
}
case 'openAnnotationPopup': {
@ -799,6 +1122,14 @@ class ReaderInstance {
this._openColorPopup(message.data);
return;
}
case 'openThumbnailPopup': {
this._openThumbnailPopup(message.data);
return;
}
case 'openSelectorPopup': {
this._openSelectorPopup(message.data);
return;
}
case 'closePopup': {
// Note: This currently only closes tags popup when annotations are
// disappearing from pdf-reader sidebar
@ -847,18 +1178,17 @@ class ReaderInstance {
return;
}
case 'focusSplitButton': {
let win = Zotero.getMainWindow();
if (win) {
win.document.getElementById('zotero-tb-toggle-item-pane').focus();
if (this instanceof ReaderTab) {
let win = Zotero.getMainWindow();
if (win) {
win.document.getElementById('zotero-tb-toggle-item-pane').focus();
}
}
return;
}
case 'focusContextPane': {
let win = Zotero.getMainWindow();
if (win) {
if (!this._window.ZoteroContextPane.focus()) {
this.focusFirst();
}
if (this instanceof ReaderWindow || !this._window.ZoteroContextPane.focus()) {
this.focusFirst();
}
return;
}
@ -925,7 +1255,7 @@ class ReaderInstance {
}
class ReaderTab extends ReaderInstance {
constructor({ itemID, title, sidebarWidth, sidebarOpen, bottomPlaceholderHeight, index, background }) {
constructor({ itemID, title, sidebarWidth, sidebarOpen, bottomPlaceholderHeight, index, tabID, background }) {
super();
this._itemID = itemID;
this._sidebarWidth = sidebarWidth;
@ -934,6 +1264,7 @@ class ReaderTab extends ReaderInstance {
this._showItemPaneToggle = true;
this._window = Services.wm.getMostRecentWindow('navigator:browser');
let { id, container } = this._window.Zotero_Tabs.add({
id: tabID,
type: 'reader',
title: title || '',
index,
@ -968,20 +1299,24 @@ class ReaderTab extends ReaderInstance {
// events in PDF reader iframe when mouse up happens over another iframe
// i.e. note-editor. There should be a better way to solve this
this._window.addEventListener('pointerup', (event) => {
if (this._window.Zotero_Tabs.selectedID === this.tabID
&& this._iframeWindow
&& event.target
&& event.target.closest
&& !event.target.closest('#outerContainer')) {
let evt = new this._iframeWindow.CustomEvent('mouseup', { bubbles: false });
evt.clientX = event.clientX;
evt.clientY = event.clientY;
this._iframeWindow.dispatchEvent(evt);
try {
if (this._window.Zotero_Tabs.selectedID === this.tabID
&& this._iframeWindow
&& event.target
&& event.target.closest
&& !event.target.closest('#outerContainer')) {
let evt = new this._iframeWindow.CustomEvent('mouseup', { bubbles: false });
evt.clientX = event.clientX;
evt.clientY = event.clientY;
this._iframeWindow.dispatchEvent(evt);
evt = new this._iframeWindow.CustomEvent('pointerup', { bubbles: false });
evt.clientX = event.clientX;
evt.clientY = event.clientY;
this._iframeWindow.dispatchEvent(evt);
evt = new this._iframeWindow.CustomEvent('pointerup', { bubbles: false });
evt.clientX = event.clientX;
evt.clientY = event.clientY;
this._iframeWindow.dispatchEvent(evt);
}
}
catch(e) {
}
});
}
@ -1080,6 +1415,8 @@ class ReaderWindow extends ReaderInstance {
this._window.document.getElementById('view-menuitem-zoom-auto').setAttribute('checked', this.isZoomAutoActive());
this._window.document.getElementById('view-menuitem-zoom-page-width').setAttribute('checked', this.isZoomPageWidthActive());
this._window.document.getElementById('view-menuitem-zoom-page-height').setAttribute('checked', this.isZoomPageHeightActive());
this._window.document.getElementById('view-menuitem-split-vertically').setAttribute('checked', this.isSplitVerticallyActive());
this._window.document.getElementById('view-menuitem-split-horizontally').setAttribute('checked', this.isSplitHorizontallyActive());
}
_onGoMenuOpen() {
@ -1139,7 +1476,7 @@ class Reader {
await Zotero.uiReadyPromise;
Zotero.Session.state.windows
.filter(x => x.type == 'reader' && Zotero.Items.exists(x.itemID))
.forEach(x => this.open(x.itemID, null, { title: x.title, openInWindow: true }));
.forEach(x => this.open(x.itemID, null, { title: x.title, openInWindow: true, secondViewState: x.secondViewState }));
}
_loadSidebarState() {
@ -1199,6 +1536,7 @@ class Reader {
for (let id of ids) {
let reader = Zotero.Reader.getByTabID(id);
if (reader) {
reader.uninit();
this._readers.splice(this._readers.indexOf(reader), 1);
}
}
@ -1261,7 +1599,12 @@ class Reader {
getWindowStates() {
return this._readers
.filter(r => r instanceof ReaderWindow)
.map(r => ({ type: 'reader', itemID: r._itemID, title: r._title }));
.map(r => ({
type: 'reader',
itemID: r._itemID,
title: r._title,
secondViewState: r.getSecondViewState()
}));
}
async openURI(itemURI, location, options) {
@ -1270,11 +1613,24 @@ class Reader {
await this.open(item.id, location, options);
}
async open(itemID, location, { title, tabIndex, openInBackground, openInWindow, allowDuplicate } = {}) {
async open(itemID, location, { title, tabIndex, tabID, openInBackground, openInWindow, allowDuplicate, secondViewState } = {}) {
this._loadSidebarState();
this.triggerAnnotationsImportCheck(itemID);
let reader;
// If duplicating is not allowed, and no reader instance is loaded for itemID,
// try to find an unloaded tab and select it. Zotero.Reader.open will then be called again
if (!allowDuplicate && !this._readers.find(r => r._itemID === itemID)) {
let win = Zotero.getMainWindow();
if (win) {
let existingTabID = win.Zotero_Tabs.getTabIDByItemID(itemID);
if (existingTabID) {
win.Zotero_Tabs.select(existingTabID, false, { location });
return;
}
}
}
if (openInWindow) {
reader = this._readers.find(r => r._itemID === itemID && (r instanceof ReaderWindow));
}
@ -1298,7 +1654,7 @@ class Reader {
bottomPlaceholderHeight: this._bottomPlaceholderHeight
});
this._readers.push(reader);
if (!(await reader.open({ itemID, location }))) {
if (!(await reader.open({ itemID, location, secondViewState }))) {
return;
}
Zotero.Session.debounceSave();
@ -1312,13 +1668,14 @@ class Reader {
itemID,
title,
index: tabIndex,
tabID,
background: openInBackground,
sidebarWidth: this._sidebarWidth,
sidebarOpen: this._sidebarOpen,
bottomPlaceholderHeight: this._bottomPlaceholderHeight
});
this._readers.push(reader);
if (!(await reader.open({ itemID, location }))) {
if (!(await reader.open({ itemID, location, secondViewState }))) {
return;
}
reader.onChangeSidebarWidth = (width) => {
@ -1340,6 +1697,7 @@ class Reader {
if (!openInBackground) {
reader._window.focus();
}
return reader;
}
/**

View file

@ -23,7 +23,7 @@
***** END LICENSE BLOCK *****
*/
Zotero.SingleFile = {
Zotero.SingleFile = Object.assign(Zotero.SingleFile || {}, {
// These are defaults from SingleFile
// Located in: zotero/resources/SingleFile/extension/core/bg/config.js
CONFIG: {
@ -32,7 +32,9 @@ Zotero.SingleFile = {
removeUnusedFonts: true,
removeFrames: true,
removeImports: true,
removeScripts: true,
blockScripts: true,
blockVideos: true,
blockAudios: true,
compressHTML: true,
compressCSS: false,
loadDeferredImages: true,
@ -40,6 +42,7 @@ Zotero.SingleFile = {
loadDeferredImagesBlockCookies: false,
loadDeferredImagesBlockStorage: false,
loadDeferredImagesKeepZoomLevel: false,
loadDeferredImagesNativeTimeout: true, // See https://github.com/gildas-lormeau/SingleFile/issues/1026
filenameTemplate: "{page-title} ({date-iso} {time-locale}).html",
infobarTemplate: "",
includeInfobar: false,
@ -58,8 +61,6 @@ Zotero.SingleFile = {
progressBarEnabled: true,
maxResourceSizeEnabled: false,
maxResourceSize: 10,
removeAudioSrc: true,
removeVideoSrc: true,
displayInfobar: true,
displayStats: false,
backgroundSave: true,
@ -95,4 +96,4 @@ Zotero.SingleFile = {
insertSingleFileComment: true,
blockMixedContent: false,
}
};
});

View file

@ -762,6 +762,8 @@ Zotero.Style.prototype.getCiteProc = function(locale, format, automaticJournalAb
var xml = this.getXML();
}
xml = this._eventToEventTitle(xml);
try {
var citeproc;
if (Zotero.Prefs.get('cite.useCiteprocRs')) {
@ -801,6 +803,39 @@ Zotero.Style.prototype.getCiteProc = function(locale, format, automaticJournalAb
}
};
/**
* Temporarily substitute `event-title` for `event`
*
* Until https://github.com/citation-style-language/styles/issues/6151
*/
Zotero.Style.prototype._eventToEventTitle = function (xml) {
var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
.createInstance(Components.interfaces.nsIDOMParser);
var doc = parser.parseFromString(xml, "text/xml");
// Ignore styles that already include `event-title`
if (doc.querySelector('[variable*="event-title"]')) {
return xml;
}
var elems = doc.querySelectorAll('[variable*="event"]');
if (!elems.length) {
return xml;
}
var changed = false;
for (let elem of elems) {
let variable = elem.getAttribute('variable');
// Must be "event" or "event foo", not, say, "event-place"
if (!/event( |$)/.test(variable)) {
continue;
}
elem.setAttribute('variable', variable.replace(/event(?= |$)/, 'event-title'));
changed = true;
}
if (changed) {
xml = doc.documentElement.outerHTML;
}
return xml;
};
Zotero.Style.prototype.__defineGetter__("class",
/**
* Retrieves the style class, either from the metadata that's already loaded or by loading the file

@ -1 +1 @@
Subproject commit 5595949fd1223802159fd1716a116da62fe8f917
Subproject commit 81991533dd3531acb9bfd6bb9457136a0af75f0f

View file

@ -668,6 +668,7 @@ Zotero.Translate.ItemSaver.prototype = {
title: attachment.title,
contentType: attachment.mimeType,
charset: attachment.charset,
libraryID: this._libraryID,
parentItemID,
collections: !parentItemID ? this._collections : undefined,
saveOptions: this._saveOptions,
@ -678,6 +679,7 @@ Zotero.Translate.ItemSaver.prototype = {
newItem = yield Zotero.Attachments.importFromFile({
file: file,
parentItemID,
libraryID: this._libraryID,
collections: !parentItemID ? this._collections : undefined,
saveOptions: this._saveOptions,
});

View file

@ -263,8 +263,9 @@ Zotero.Translators = new function() {
});
this.reinit = function (options = {}) {
return this.init(Object.assign({}, options, { reinit: true }));
this.reinit = async function (options = {}) {
await this.init(Object.assign({}, options, { reinit: true }));
await Zotero.QuickCopy.init();
};

@ -1 +1 @@
Subproject commit a0a145a4b534721148da362a3bae3befc8d65121
Subproject commit 4d8d7d3e92cbc3b4d62b82478e212a6f63b9a35f

View file

@ -405,6 +405,12 @@ Zotero.Utilities.Internal = {
return s;
},
isOnlyEmoji: function (str) {
// Remove emoji, Zero Width Joiner, and Variation Selector-16 and see if anything's left
const re = /\p{Extended_Pictographic}|\u200D|\uFE0F/gu;
return !str.replace(re, '');
},
/**
* Display a prompt from an error with custom buttons and a callback
*/
@ -564,11 +570,10 @@ Zotero.Utilities.Internal = {
const SCRIPTS = [
// This first script replace in the INDEX_SCRIPTS from the single file cli loader
"dist/single-file.js",
"lib/single-file.js",
// Web SCRIPTS
"dist/web/hooks/hooks-frames-web.js",
"dist/web/hooks/hooks-web.js",
"lib/single-file-hooks-frames.js",
];
const { loadSubScript } = Components.classes['@mozilla.org/moz/jssubscript-loader;1']
@ -594,8 +599,8 @@ Zotero.Utilities.Internal = {
// List of scripts from:
// resource/SingleFile/extension/lib/single-file/core/bg/scripts.js
const frameScripts = [
"dist/web/hooks/hooks-frames-web.js",
"dist/single-file-frames.js",
"lib/single-file-hooks-frames.js",
"lib/single-file-frames.js",
];
// Create sandboxes for all the frames we find
@ -647,7 +652,7 @@ Zotero.Utilities.Internal = {
// Mostly copied from:
// resources/SingleFile/extension/lib/single-file/fetch/bg/fetch.js::fetchResource
sandbox.coFetch = Components.utils.exportFunction(
function (url, onDone) {
function (url, options, onDone) {
const xhrRequest = new XMLHttpRequest();
xhrRequest.withCredentials = true;
xhrRequest.responseType = "arraybuffer";
@ -673,6 +678,11 @@ Zotero.Utilities.Internal = {
}
};
xhrRequest.open("GET", url, true);
if (options && options.headers) {
for (const entry of Object.entries(options.headers)) {
xhrRequest.setRequestHeader(entry[0], entry[1]);
}
}
xhrRequest.send();
},
sandbox
@ -683,14 +693,14 @@ Zotero.Utilities.Internal = {
// resources/SingleFile/extension/lib/single-file/fetch/content/content-fetch.js::fetch
Components.utils.evalInSandbox(
`
ZoteroFetch = async function (url) {
ZoteroFetch = async function (url, options) {
try {
let response = await fetch(url, { cache: "force-cache" });
let response = await fetch(url, { cache: "force-cache", headers: options.headers });
return response;
}
catch (error) {
let response = await new Promise((resolve, reject) => {
coFetch(url, (response) => {
coFetch(url, { headers: options.headers }, (response) => {
if (response.error) {
Zotero.debug("Error retrieving url: " + url);
Zotero.debug(response);
@ -1551,6 +1561,18 @@ Zotero.Utilities.Internal = {
},
/**
* Adds a localized colon to a string (which is usually just a colon, but, e.g., in French
* there's a space before it)
*
* @param {String}
* @return {String}
*/
stringWithColon: function (str) {
return Zotero.getString('punctuation.colon.withString', str);
},
resolveLocale: function (locale, locales) {
// If the locale exists as-is, use it
if (locales.includes(locale)) {

View file

@ -2098,7 +2098,7 @@ Zotero.Browser = new function() {
hiddenBrowser.docShell.allowJavascript = options.allowJavaScript !== false
hiddenBrowser.docShell.allowMetaRedirects = false;
hiddenBrowser.docShell.allowPlugins = false;
Zotero.debug("Created hidden browser (" + (nBrowsers++) + ")");
Zotero.debug("Created hidden browser (" + (++nBrowsers) + ")");
return hiddenBrowser;
}

View file

@ -99,6 +99,7 @@ var ZoteroPane = new function()
var zp = document.getElementById('zotero-pane');
Zotero.setFontSize(zp);
Zotero.setFontSize(document.getElementById('zotero-context-pane'));
ZoteroPane_Local.updateLayout();
ZoteroPane_Local.updateToolbarPosition();
this.updateWindow();
@ -148,6 +149,26 @@ var ZoteroPane = new function()
Zotero.hiDPI = window.devicePixelRatio > 1;
Zotero.hiDPISuffix = Zotero.hiDPI ? "@2x" : "";
// Show warning in toolbar for 'dev' channel builds
try {
let isDevBuild = Zotero.version.includes('-dev');
// Uncomment to test
//isDevBuild = isDevBuild || Zotero.version.includes('.SOURCE');
if (isDevBuild) {
let label = document.createElement('label');
label.setAttribute('value', 'TEST BUILD — DO NOT USE');
label.setAttribute('style', 'font-weight: bold; color: red; cursor: pointer');
label.onclick = function () {
Zotero.launchURL('https://www.zotero.org/support/kb/test_builds');
};
let syncStop = document.getElementById('zotero-tb-sync-stop');
syncStop.parentNode.insertBefore(label, syncStop);
}
}
catch (e) {
Zotero.logError(e);
}
Zotero_Tabs.init();
ZoteroContextPane.init();
await ZoteroPane.initCollectionsTree();
@ -1788,6 +1809,71 @@ var ZoteroPane = new function()
});
this.duplicateAndConvertSelectedItem = async function () {
if (this.getSelectedItems().length != 1
|| !['book', 'bookSection'].includes(this.getSelectedItems()[0].itemType)) {
throw new Error('duplicateAndConvertSelectedItem requires a single book or bookSection to be selected');
}
let authorCreatorType = Zotero.CreatorTypes.getID('author');
let bookAuthorCreatorType = Zotero.CreatorTypes.getID('bookAuthor');
let original = this.getSelectedItems()[0];
let duplicate = await this.duplicateSelectedItem();
if (!duplicate) return null;
// TODO: Move this logic to duplicateSelectedItem() with a `targetItemType` flag to avoid
// extra saves?
if (duplicate.itemType == 'book') {
duplicate.setType(Zotero.ItemTypes.getID('bookSection'));
for (let i = 0; i < duplicate.numCreators(); i++) {
let creator = duplicate.getCreator(i);
if (creator.creatorTypeID == authorCreatorType) {
creator.creatorTypeID = bookAuthorCreatorType;
}
duplicate.setCreator(i, creator);
}
// Remove related-item relations to other book sections of this book
for (let relItemKey of [...duplicate.relatedItems]) {
let relItem = await Zotero.Items.getByLibraryAndKeyAsync(
duplicate.libraryID, relItemKey
);
if (relItem.itemType == 'bookSection'
&& relItem.getField('bookTitle') == original.getField('title')) {
duplicate.removeRelatedItem(relItem);
relItem.removeRelatedItem(duplicate);
await relItem.saveTx();
}
}
}
else {
duplicate.setField('title', false); // So bookTitle becomes title
duplicate.setType(Zotero.ItemTypes.getID('book'));
// Get creators from the original item because setType() will have changed the types
let creators = original.getCreators()
// Remove authors of the individual book section
.filter(creator => creator.creatorTypeID !== authorCreatorType);
for (let creator of creators) {
if (creator.creatorTypeID == bookAuthorCreatorType) {
creator.creatorTypeID = authorCreatorType;
}
}
duplicate.setCreators(creators);
}
duplicate.setField('abstractNote', '');
duplicate.addRelatedItem(original);
original.addRelatedItem(duplicate);
await original.saveTx({ skipDateModifiedUpdate: true });
await duplicate.saveTx();
document.getElementById('zotero-editpane-item-box').focusField('title');
return duplicate;
};
/**
* Return whether every selected item can be deleted from the current
* collection context (library, trash, collection, etc.).
@ -1877,13 +1963,39 @@ var ZoteroPane = new function()
var prompt = (force && !fromMenu) ? false : toTrash;
}
else if (collectionTreeRow.isCollection()) {
// Ignore unmodified action if only child items are selected
if (!force && this.itemsView.getSelectedItems().every(item => !item.isTopLevelItem())) {
return;
if (force) {
var prompt = toTrash;
}
else {
// Ignore unmodified action if only child items are selected
if (this.itemsView.getSelectedItems().every(item => !item.isTopLevelItem())) {
return;
}
// If unmodified, recursiveCollections is true, and items are in
// descendant collections (even if also in the selected collection),
// prompt to remove from all
if (Zotero.Prefs.get('recursiveCollections')) {
let descendants = collectionTreeRow.ref.getDescendents(false, 'collection');
let inSubcollection = descendants
.some(({ id }) => this.itemsView.getSelectedItems()
.some(item => item.inCollection(id)));
if (inSubcollection) {
var prompt = {
title: Zotero.getString('pane.items.removeRecursive.title'),
text: Zotero.getString(
'pane.items.removeRecursive' + (this.itemsView.selection.count > 1 ? '.multiple' : '')
)
};
}
else {
var prompt = toRemove;
}
}
else {
var prompt = toRemove;
}
}
var prompt = force ? toTrash : toRemove;
}
else if (collectionTreeRow.isTrash() || collectionTreeRow.isBucket()) {
var prompt = toDelete;
@ -1920,8 +2032,9 @@ var ZoteroPane = new function()
this.deleteSelectedCollection = function (deleteItems) {
var collectionTreeRow = this.getCollectionTreeRow();
// Don't allow deleting libraries
if (collectionTreeRow.isLibrary(true) && !collectionTreeRow.isFeed()) {
// Don't allow deleting libraries or My Publications
if (collectionTreeRow.isLibrary(true) && !collectionTreeRow.isFeed()
|| collectionTreeRow.isPublications()) {
return;
}
@ -2288,7 +2401,7 @@ var ZoteroPane = new function()
return;
}
else {
Zotero_File_Interface.exportItemsToClipboard(items, format.id);
Zotero_File_Interface.exportItemsToClipboard(items, format);
}
}
}
@ -3038,7 +3151,6 @@ var ZoteroPane = new function()
'sep1',
'addNote',
'createNoteFromAnnotations',
'createNoteFromAnnotationsMenu',
'addAttachments',
'sep2',
'findPDF',
@ -3046,6 +3158,7 @@ var ZoteroPane = new function()
'toggleRead',
'addToCollection',
'removeItems',
'duplicateAndConvert',
'duplicateItem',
'restoreToLibrary',
'moveToTrash',
@ -3173,12 +3286,33 @@ var ZoteroPane = new function()
}
}
// "Add Notes from Annotation" and "Find Available PDFs"
// "Add/Create Note from Annotations" and "Find Available PDFs"
if (collectionTreeRow.filesEditable
&& !collectionTreeRow.isDuplicates()
&& !collectionTreeRow.isFeed()) {
if (items.some(item => attachmentsWithExtractableAnnotations(item).length)) {
if (items.some(item => attachmentsWithExtractableAnnotations(item).length)
|| items.some(item => isAttachmentWithExtractableAnnotations(item))) {
let menuitem = menu.childNodes[m.createNoteFromAnnotations];
show.add(m.createNoteFromAnnotations);
let key;
// If all from a single item, show "Add Note from Annotations"
if (Zotero.Items.getTopLevel(items).length == 1) {
key = 'addNoteFromAnnotations';
menuitem.onclick = async () => {
return this.addNoteFromAnnotationsFromSelected();
};
}
// Otherwise show "Create Note from Annotations"
else {
key = 'createNoteFromAnnotations';
menuitem.onclick = async () => {
return this.createStandaloneNoteFromAnnotationsFromSelected();
};
}
menuitem.setAttribute(
'label',
Zotero.getString('pane.items.menu.' + key)
);
show.add(m.sep3);
}
@ -3238,43 +3372,42 @@ var ZoteroPane = new function()
show.add(m.sep1);
}
// Show "Add Note from Annotations" on parent item with any extractable annotations
if (item.isRegularItem() && !item.isFeedItem) {
show.add(m.addNote);
show.add(m.addAttachments);
show.add(m.sep2);
// Create Note from Annotations
let popup = document.getElementById('create-note-from-annotations-popup');
popup.textContent = '';
let eligibleAttachments = Zotero.Items.get(item.getAttachments())
.filter(item => item.isPDFAttachment());
let attachmentsWithAnnotations = eligibleAttachments
let attachmentsWithAnnotations = Zotero.Items.get(item.getAttachments())
.filter(item => isAttachmentWithExtractableAnnotations(item));
if (attachmentsWithAnnotations.length) {
// Display submenu if there's more than one PDF attachment, even if
// there's only attachment with annotations, so it's clear which one
// the annotations are coming from
if (eligibleAttachments.length > 1) {
show.add(m.createNoteFromAnnotationsMenu);
for (let attachment of attachmentsWithAnnotations) {
let menuitem = document.createElement('menuitem');
menuitem.setAttribute('label', attachment.getDisplayTitle());
menuitem.onclick = () => {
ZoteroPane.createNoteFromAnnotationsForAttachment(attachment);
};
popup.appendChild(menuitem);
}
}
// Single attachment with annotations
else {
show.add(m.createNoteFromAnnotations);
}
show.add(m.createNoteFromAnnotations);
}
}
else if (isAttachmentWithExtractableAnnotations(item) && !item.isTopLevelItem()) {
// Show "(Create|Add) Note from Annotations" on attachment with extractable annotations
else if (isAttachmentWithExtractableAnnotations(item)) {
show.add(m.createNoteFromAnnotations);
show.add(m.sep2);
}
if (show.has(m.createNoteFromAnnotations)) {
let menuitem = menu.childNodes[m.createNoteFromAnnotations];
let str;
// Show "Create" on standalone attachments
if (item.isAttachment() && item.isTopLevelItem()) {
str = 'pane.items.menu.createNoteFromAnnotations';
menuitem.onclick = async () => {
return this.createStandaloneNoteFromAnnotationsFromSelected();
};
}
// And "Add" otherwise
else {
str = 'pane.items.menu.addNoteFromAnnotations';
menuitem.onclick = async () => {
return this.addNoteFromAnnotationsFromSelected();
};
}
menuitem.setAttribute('label', Zotero.getString(str));
}
if (Zotero.Attachments.canFindPDFForItem(item)) {
show.add(m.findPDF);
@ -3325,6 +3458,12 @@ var ZoteroPane = new function()
}
}
else if (!collectionTreeRow.isPublications()) {
if (item.itemType == 'book' || item.itemType == 'bookSection') {
menu.childNodes[m.duplicateAndConvert].setAttribute('label', Zotero.getString('pane.items.menu.duplicateAndConvert.'
+ (item.itemType == 'book' ? 'toBookSection' : 'toBook')));
show.add(m.duplicateAndConvert);
}
show.add(m.duplicateItem);
}
}
@ -3434,8 +3573,6 @@ var ZoteroPane = new function()
}
// Set labels, plural if necessary
menu.childNodes[m.createNoteFromAnnotations].setAttribute('label', Zotero.getString('pane.items.menu.addNoteFromAnnotations' + multiple));
menu.childNodes[m.createNoteFromAnnotationsMenu].setAttribute('label', Zotero.getString('pane.items.menu.addNoteFromAnnotations' + multiple));
menu.childNodes[m.findPDF].setAttribute('label', Zotero.getString('pane.items.menu.findAvailablePDF' + multiple));
menu.childNodes[m.moveToTrash].setAttribute('label', Zotero.getString('pane.items.menu.moveToTrash' + multiple));
menu.childNodes[m.deleteFromLibrary].setAttribute('label', Zotero.getString('pane.items.menu.delete'));
@ -3507,6 +3644,9 @@ var ZoteroPane = new function()
throw new Error('collection must be null if createNew is true');
}
let id = await this.newCollection();
if (!id) {
return;
}
collection = Zotero.Collections.get(id);
}
@ -4279,12 +4419,25 @@ var ZoteroPane = new function()
extraData && extraData.location,
{
openInWindow: (event && event.shiftKey)
|| (extraData && extraData.forceOpenPDFInWindow)
|| (extraData && extraData.forceOpenPDFInWindow),
allowDuplicate: event && event.shiftKey
}
);
return;
}
// Try to open external reader to page number if specified
else {
let pageIndex = extraData?.location?.position?.pageIndex;
if (pageIndex !== undefined) {
await Zotero.OpenPDF.openToPage(
item,
parseInt(pageIndex) + 1
);
return;
}
}
// Custom PDF handler
// TODO: Remove this and unify with Zotero.OpenPDF
if (pdfHandler != 'system') {
try {
if (await OS.File.exists(pdfHandler)) {
@ -4319,7 +4472,7 @@ var ZoteroPane = new function()
let path = item.getFilePath();
if (!path) {
ZoteroPane_Local.showAttachmentNotFoundDialog(
item.id,
item,
path,
{
noLocate: true,
@ -4408,7 +4561,7 @@ var ZoteroPane = new function()
if (isLinkedFile || !fileSyncingEnabled) {
this.showAttachmentNotFoundDialog(
itemID,
item,
path,
{
noLocate: noLocateOnMissing,
@ -4434,7 +4587,7 @@ var ZoteroPane = new function()
if (!await item.getFilePathAsync()) {
ZoteroPane_Local.showAttachmentNotFoundDialog(
item.id,
item,
path,
{
noLocate: noLocateOnMissing,
@ -4501,7 +4654,7 @@ var ZoteroPane = new function()
if (!fileExists) {
this.showAttachmentNotFoundDialog(
attachment.id,
attachment,
path,
{
noLocate: noLocateOnMissing,
@ -4647,9 +4800,13 @@ var ZoteroPane = new function()
}
this.showAttachmentNotFoundDialog = function (itemID, path, options = {}) {
this.showAttachmentNotFoundDialog = async function (item, path, options = {}) {
var { noLocate, notOnServer, linkedFile } = options;
if (item.isLinkedFileAttachment() && await this.checkForLinkedFilesToRelink(item)) {
return;
}
var title = Zotero.getString('pane.item.attachments.fileNotFound.title');
var text = Zotero.getString(
'pane.item.attachments.fileNotFound.text1' + (path ? '.path' : '')
@ -4664,7 +4821,7 @@ var ZoteroPane = new function()
),
[ZOTERO_CONFIG.CLIENT_NAME, ZOTERO_CONFIG.DOMAIN_NAME]
);
var supportURL = options.linkedFile
var supportURL = linkedFile
? 'https://www.zotero.org/support/kb/missing_linked_file'
: 'https://www.zotero.org/support/kb/files_not_syncing';
@ -4702,12 +4859,76 @@ var ZoteroPane = new function()
);
if (index == 0) {
this.relinkAttachment(itemID);
this.relinkAttachment(item.id);
}
else if (index == 2) {
this.loadURI(supportURL, { metaKey: true, shiftKey: true });
}
}
/**
* Prompt the user to relink one or all of the attachment files found in
* the LABD.
*
* @param {Zotero.Item} item
* @param {String} path Path to the file matching `item`
* @param {Number} numOthers If zero, "Relink All" option is not offered
* @return {'one' | 'all' | 'manual' | 'cancel'}
*/
this.showLinkedFileFoundAutomaticallyDialog = function (item, path, numOthers) {
let ps = Services.prompt;
let title = Zotero.getString('pane.item.attachments.autoRelink.title');
let text = Zotero.getString('pane.item.attachments.autoRelink.text1') + '\n\n'
+ Zotero.getString('pane.item.attachments.autoRelink.text2', item.getFilePath()) + '\n\n'
+ Zotero.getString('pane.item.attachments.autoRelink.text3', path) + '\n\n'
+ Zotero.getString('pane.item.attachments.autoRelink.text4', Zotero.appName);
let buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING
+ ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL
+ ps.BUTTON_POS_2 * ps.BUTTON_TITLE_IS_STRING;
let index = ps.confirmEx(null,
title,
text,
buttonFlags,
Zotero.getString('pane.item.attachments.autoRelink.relink'),
null,
Zotero.getString('pane.item.attachments.autoRelink.locateManually'),
null, {}
);
if (index == 1) {
// Cancel
return 'cancel';
}
else if (index == 2) {
// Locate Manually...
return 'manual';
}
// Relink
if (!numOthers) {
return 'one';
}
title = Zotero.getString('pane.item.attachments.autoRelinkOthers.title');
text = Zotero.getString('pane.item.attachments.autoRelinkOthers.text', numOthers, numOthers);
buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING
+ ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL;
index = ps.confirmEx(null,
title,
text,
buttonFlags,
Zotero.getString(
numOthers == 1
? 'pane.item.attachments.autoRelink.relink'
: 'pane.item.attachments.autoRelink.relinkAll'
),
null, null, null, {}
);
return index == 0 ? 'all' : 'one';
};
this.syncAlert = function (e) {
@ -4817,14 +5038,21 @@ var ZoteroPane = new function()
};
this.createNoteFromAnnotationsForAttachment = async function (attachment, { skipSelect } = {}) {
this.addNoteFromAnnotationsForAttachment = async function (attachment, { skipSelect } = {}) {
if (!this.canEdit()) {
this.displayCannotEditLibraryMessage();
return;
}
await Zotero.PDFWorker.import(attachment.id, true);
var annotations = attachment.getAnnotations().filter(x => x.annotationType != 'ink');
if (!annotations.length) {
return;
}
var note = await Zotero.EditorInstance.createNoteFromAnnotations(
attachment.getAnnotations().filter(x => x.annotationType != 'ink'), attachment.parentID
annotations,
{
parentID: attachment.parentID
}
);
if (!skipSelect) {
await this.selectItem(note.id);
@ -4833,7 +5061,81 @@ var ZoteroPane = new function()
};
this.createNoteFromAnnotationsFromSelected = async function () {
/**
* Add a single child note with the annotations from all selected items, including from all
* child attachments of a selected regular item
*
* Selected items must all have the same top-level item
*/
this.addNoteFromAnnotationsFromSelected = async function () {
if (!this.canEdit()) {
this.displayCannotEditLibraryMessage();
return;
}
var items = this.getSelectedItems();
var topLevelItems = [...new Set(Zotero.Items.getTopLevel(items))];
if (topLevelItems.length > 1) {
throw new Error("Can't create child attachment from different top-level items");
}
var topLevelItem = topLevelItems[0];
if (!topLevelItem.isRegularItem()) {
throw new Error("Can't add note to standalone attachment");
}
// Ignore top-level item if specific child items are also selected
if (items.length > 1) {
items = items.filter(item => !item.isRegularItem());
}
var attachments = [];
for (let item of items) {
if (item.isRegularItem()) {
// Find all child items with extractable annotations
attachments.push(
...Zotero.Items.get(item.getAttachments())
.filter(item => isAttachmentWithExtractableAnnotations(item))
);
}
else if (isAttachmentWithExtractableAnnotations(item)) {
attachments.push(item);
}
else {
continue;
}
}
if (!attachments.length) {
Zotero.debug("No attachments found", 2);
return;
}
var annotations = [];
for (let attachment of attachments) {
try {
await Zotero.PDFWorker.import(attachment.id, true);
}
catch (e) {
Zotero.logError(e);
}
annotations.push(...attachment.getAnnotations().filter(x => x.annotationType != 'ink'));
}
var note = await Zotero.EditorInstance.createNoteFromAnnotations(
annotations,
{
parentID: topLevelItem.id
}
);
await this.selectItem(note.id);
};
/**
* Create separate child notes for each selected item, including all child attachments of
* selected regular items
*
* No longer exposed via UI
*/
this.addNotesFromAnnotationsFromSelected = async function () {
if (!this.canEdit()) {
this.displayCannotEditLibraryMessage();
return;
@ -4859,16 +5161,77 @@ var ZoteroPane = new function()
continue;
}
for (let attachment of attachments) {
let note = await this.createNoteFromAnnotationsForAttachment(
let note = await this.addNoteFromAnnotationsForAttachment(
attachment,
{ skipSelect: true }
);
itemIDsToSelect.push(note.id);
if (note) {
itemIDsToSelect.push(note.id);
}
}
}
await this.selectItems(itemIDsToSelect);
};
this.createStandaloneNoteFromAnnotationsFromSelected = async function () {
if (!this.canEdit()) {
this.displayCannotEditLibraryMessage();
return;
}
var items = this.getSelectedItems();
// Ignore selected top-level items if any descendant items are also selected
var topLevelOfSelectedDescendants = new Set();
for (let item of items) {
if (!item.isTopLevelItem()) {
topLevelOfSelectedDescendants.add(item.topLevelItem);
}
}
items = items.filter(item => !topLevelOfSelectedDescendants.has(item));
var annotations = [];
for (let item of items) {
let attachments = [];
if (item.isRegularItem()) {
// Find all child attachments with extractable annotations
attachments.push(
...Zotero.Items.get(item.getAttachments())
.filter(item => isAttachmentWithExtractableAnnotations(item))
);
}
else if (isAttachmentWithExtractableAnnotations(item)) {
attachments.push(item);
}
else {
continue;
}
for (let attachment of attachments) {
try {
await Zotero.PDFWorker.import(attachment.id, true);
}
catch (e) {
Zotero.logError(e);
}
annotations.push(...attachment.getAnnotations().filter(x => x.annotationType != 'ink'));
}
}
if (!annotations.length) {
Zotero.debug("No annotations found", 2);
return;
}
var note = await Zotero.EditorInstance.createNoteFromAnnotations(
annotations,
{
collectionID: this.getSelectedCollection(true)
}
);
await this.selectItem(note.id);
};
this.createEmptyParent = async function (item) {
await Zotero.DB.executeTransaction(async function () {
// TODO: remove once there are no top-level web attachments
@ -5062,6 +5425,120 @@ var ZoteroPane = new function()
}
}
};
/**
* Attempt to find a file in the LABD matching the passed attachment
* by searching successive subdirectories. Prompt the user if a match is
* found and offer to relink one or all matching files in the directory.
* The user can also choose to relink manually, which opens a file picker.
*
* If the synced path is 'C:\Users\user\Documents\Dissertation\Files\Paper.pdf',
* the LABD is '/Users/user/Documents', and the (not yet known) correct local
* path is '/Users/user/Documents/Dissertation/Files/Paper.pdf', check:
*
* 1. /Users/user/Documents/Users/user/Documents/Dissertation/Files/Paper.pdf
* 2. /Users/user/Documents/user/Documents/Dissertation/Files/Paper.pdf
* 3. /Users/user/Documents/Documents/Dissertation/Files/Paper.pdf
* 4. /Users/user/Documents/Dissertation/Files/Paper.pdf
*
* If line 4 had not been the correct local path (in other words, if no file
* existed at that path), we would have continued on to check
* '/Users/user/Documents/Dissertation/Paper.pdf'. If that did not match,
* with no more segments in the synced path to drop, we would have given up.
*
* Once we find the file, check for other linked files beginning with
* C:\Users\user\Documents\Dissertation\Files and see if they exist relative
* to /Users/user/Documents/Dissertation/Files, and prompt to relink them
* all if so.
*
* @param {Zotero.Item} item
* @return {Promise<Boolean>} True if relinked successfully or canceled
*/
this.checkForLinkedFilesToRelink = async function (item) {
Zotero.debug('Attempting to relink automatically');
let basePath = Zotero.Prefs.get('baseAttachmentPath');
if (!basePath) {
Zotero.debug('No LABD');
return false;
}
Zotero.debug('LABD path: ' + basePath);
let syncedPath = item.getFilePath();
if (!syncedPath) {
Zotero.debug('No synced path');
return false;
}
syncedPath = Zotero.File.normalizeToUnix(syncedPath);
Zotero.debug('Synced path: ' + syncedPath);
if (Zotero.File.directoryContains(basePath, syncedPath)) {
// Already in the LABD - nothing to do
Zotero.debug('Synced path is already within LABD');
return false;
}
// We can't use OS.Path.dirname because that function expects paths valid for the current platform...
// but we can't normalize first because we're going to be comparing it to other un-normalized paths
let unNormalizedDirname = item.getFilePath();
let lastSlash = Math.max(
unNormalizedDirname.lastIndexOf('/'),
unNormalizedDirname.lastIndexOf('\\')
);
if (lastSlash != -1) {
unNormalizedDirname = unNormalizedDirname.substring(0, lastSlash + 1);
}
let parts = OS.Path.split(syncedPath).components;
for (let segmentsToDrop = 0; segmentsToDrop < parts.length; segmentsToDrop++) {
let correctedPath = OS.Path.join(basePath, ...parts.slice(segmentsToDrop));
if (!(await OS.File.exists(correctedPath))) {
Zotero.debug('Does not exist: ' + correctedPath);
continue;
}
Zotero.debug('Exists! ' + correctedPath);
let otherUnlinked = await Zotero.Items.findMissingLinkedFiles(
item.libraryID,
unNormalizedDirname
);
let othersToRelink = new Map();
for (let otherItem of otherUnlinked) {
if (otherItem.id === item.id) continue;
let otherParts = otherItem.getFilePath()
.split(/[/\\]/)
// Slice as much off the beginning as when creating correctedPath
.slice(segmentsToDrop);
if (!otherParts.length) continue;
let otherCorrectedPath = OS.Path.join(basePath, ...otherParts);
if (await OS.File.exists(otherCorrectedPath)) {
othersToRelink.set(otherItem, otherCorrectedPath);
}
}
let choice = this.showLinkedFileFoundAutomaticallyDialog(item, correctedPath, othersToRelink.size);
switch (choice) {
case 'one':
await item.relinkAttachmentFile(correctedPath);
return true;
case 'all':
await item.relinkAttachmentFile(correctedPath);
await Promise.all([...othersToRelink]
.map(([i, p]) => i.relinkAttachmentFile(p)));
return true;
case 'manual':
await this.relinkAttachment(item.id);
return true;
case 'cancel':
return true;
}
}
Zotero.debug('No segments left to drop; match not found in LABD');
return false;
};
this.relinkAttachment = async function (itemID) {
@ -5074,7 +5551,7 @@ var ZoteroPane = new function()
if (!item) {
throw new Error('Item ' + itemID + ' not found in ZoteroPane_Local.relinkAttachment()');
}
while (true) {
let fp = new FilePicker();
fp.init(window, Zotero.getString('pane.item.attachments.select'), fp.modeOpen);
@ -5087,7 +5564,13 @@ var ZoteroPane = new function()
var dir = await Zotero.File.getClosestDirectory(file);
if (dir) {
fp.displayDirectory = dir;
try {
fp.displayDirectory = dir;
}
catch (e) {
// Directory is invalid; ignore and go with the home directory
fp.displayDirectory = OS.Constants.Path.homeDir;
}
}
fp.appendFilters(fp.filterAll);

View file

@ -293,11 +293,7 @@
<!-- with icon: <menuitem class="menuitem-iconic" id="zotero-menuitem-note" label="&zotero.items.menu.attach.note;" oncommand="ZoteroPane_Local.newNote(false, this.parentNode.getAttribute('itemID'))"/>-->
<menuitem class="menuitem-iconic zotero-menuitem-attach-note" label="&zotero.items.menu.attach.note;" oncommand="ZoteroPane_Local.newNote(false, this.parentNode.getAttribute('itemKey'))"/>
<menuitem class="menuitem-iconic zotero-menuitem-create-note-from-annotations"
oncommand="ZoteroPane.createNoteFromAnnotationsFromSelected()"/>
<menu class="menuitem-iconic zotero-menuitem-create-note-from-annotations">
<menupopup id="create-note-from-annotations-popup"/>
</menu>
<menuitem class="menuitem-iconic zotero-menuitem-create-note-from-annotations"/>
<menu class="menu-iconic zotero-menuitem-attach" label="&zotero.items.menu.attach;">
<menupopup id="zotero-add-attachment-popup">
@ -317,6 +313,7 @@
</menupopup>
</menu>
<menuitem class="menuitem-iconic zotero-menuitem-remove-items" oncommand="ZoteroPane_Local.deleteSelectedItems();"/>
<menuitem class="menuitem-iconic zotero-menuitem-duplicate-and-convert" oncommand="ZoteroPane_Local.duplicateAndConvertSelectedItem();"/>
<menuitem class="menuitem-iconic zotero-menuitem-duplicate-item" label="&zotero.items.menu.duplicateItem;" oncommand="ZoteroPane_Local.duplicateSelectedItem().done();"/>
<menuitem class="menuitem-iconic zotero-menuitem-restore-to-library" label="&zotero.items.menu.restoreToLibrary;" oncommand="ZoteroPane_Local.restoreSelectedItems();"/>
<menuitem class="menuitem-iconic zotero-menuitem-move-to-trash" oncommand="ZoteroPane_Local.deleteSelectedItems(true, true);"/>

View file

@ -1,7 +1,7 @@
<!ENTITY styles.preview "Zotero Style Preview">
<!ENTITY styles.preview.citationFormat "Citation Format:">
<!ENTITY styles.preview.citationFormat.all "all">
<!ENTITY styles.preview.citationFormat.all "alles">
<!ENTITY styles.preview.citationFormat.author "author">
<!ENTITY styles.preview.citationFormat.authorDate "author-date">
<!ENTITY styles.preview.citationFormat.label "label">

View file

@ -58,7 +58,7 @@
<!ENTITY zotero.items.itemType "Tipe item">
<!ENTITY zotero.items.creator_column "Skepper">
<!ENTITY zotero.items.year_column "Jaar">
<!ENTITY zotero.items.dateModified_column "Date Modified">
<!ENTITY zotero.items.dateModified_column "Datum gewysig">
<!ENTITY zotero.items.moreColumns.label "More Columns">
<!ENTITY zotero.items.restoreColumnOrder.label "Restore Column Order">
@ -129,7 +129,7 @@
<!ENTITY zotero.toolbar.attachment.weblink "Save Link to Current Page">
<!ENTITY zotero.toolbar.attachment.snapshot "Take Snapshot of Current Page">
<!ENTITY zotero.toolbar.context.item "Item">
<!ENTITY zotero.toolbar.context.notes "Notes">
<!ENTITY zotero.toolbar.context.notes "Notas">
<!ENTITY zotero.tagSelector.noTagsToDisplay "No tags to display">
<!ENTITY zotero.tagSelector.loadingTags "Loading tags…">
@ -148,7 +148,7 @@
<!ENTITY zotero.tagColorChooser.removeColor "Remove Color">
<!ENTITY zotero.lookup.description "Enter ISBNs, DOIs, PMIDs, arXiv IDs, or ADS Bibcodes to add to your library:">
<!ENTITY zotero.lookup.button.search "Search">
<!ENTITY zotero.lookup.button.search "Soek">
<!ENTITY zotero.createParent.title "Create Parent Item">
<!ENTITY zotero.createParent.button.manual "Manual Entry">
@ -311,8 +311,6 @@
<!ENTITY zotero.pdfReader.lastPage "Last Page">
<!ENTITY zotero.pdfReader.back "Back">
<!ENTITY zotero.pdfReader.forward "Forward">
<!ENTITY zotero.pdfReader.rotateClockwise "Rotate Clockwise">
<!ENTITY zotero.pdfReader.rotateCounterclockwise "Rotate Counterclockwise">
<!ENTITY zotero.pdfReader.handTool "Hand Tool">
<!ENTITY zotero.pdfReader.verticalScrolling "Vertical Scrolling">
<!ENTITY zotero.pdfReader.horizontalScrolling "Horizontal Scrolling">
@ -325,4 +323,9 @@
<!ENTITY zotero.pdfReader.zoomAuto "Automatically Resize">
<!ENTITY zotero.pdfReader.zoomPageWidth "Zoom to Page Width">
<!ENTITY zotero.pdfReader.zoomPageHeight "Zoom to Page Height">
<!ENTITY zotero.pdfReader.splitVertically "Split Vertically">
<!ENTITY zotero.pdfReader.splitHorizontally "Split Horizontally">
<!ENTITY zotero.pdfReader.transferFromPDF "Import Annotations…">
<!ENTITY zotero.pdfReader.rotatePageLeft "Rotate Page Left">
<!ENTITY zotero.pdfReader.rotatePageRight "Rotate Page Right">
<!ENTITY zotero.pdfReader.rotatePage180 "Rotate Page 180°">

View file

@ -19,8 +19,8 @@ general.unknownErrorOccurred=An unknown error occurred.
general.invalidResponseServer=Invalid response from server.
general.tryAgainLater=Please try again in a few minutes.
general.serverError=The server returned an error. Please try again.
general.pleaseRestart=Please restart %S.
general.pleaseRestartAndTryAgain=Please restart %S and try again.
general.pleaseRestart=Please restart Firefox.
general.pleaseRestartAndTryAgain=Please restart Firefox and try again.
general.checkForUpdate=Check for Update
general.checkForUpdates=Check for Updates
general.actionCannotBeUndone=This action cannot be undone.
@ -40,10 +40,10 @@ general.and=and
general.etAl=et al.
general.accessDenied=Access Denied
general.permissionDenied=Permission Denied
general.character.singular=character
general.character.plural=characters
general.character.singular=karakter
general.character.plural=karakters
general.create=Create
general.delete=Delete
general.delete=Skrap
general.remove=Remove
general.import=Import
general.export=Export
@ -76,7 +76,7 @@ general.continue=Continue
general.copy=Copy
general.copyToClipboard=Copy to Clipboard
general.cancel=Cancel
general.clear=Clear
general.clear=Skoon
general.processing=Processing
general.finished=Finished
general.submitted=Submitted
@ -92,6 +92,7 @@ general.custom=Custom
general.loading=Loading…
general.richText=Rich Text
general.clearSelection=Clear Selection
general.insert=Insert
general.yellow=Yellow
general.red=Red
@ -114,6 +115,7 @@ about.getInvolved=Want to help? [Get involved] today!
punctuation.openingQMark=“
punctuation.closingQMark=”
punctuation.colon=:
punctuation.colon.withString=%S:
punctuation.ellipsis=…
install.quickStartGuide=Quick Start Guide
@ -318,7 +320,7 @@ pane.items.attach.link.uri.file=To attach a link to a file, please use “%S”.
pane.items.trash.title=Move to Trash
pane.items.trash=Are you sure you want to move the selected item to the Trash?
pane.items.trash.multiple=Are you sure you want to move the selected items to the Trash?
pane.items.delete.title=Delete
pane.items.delete.title=Skrap
pane.items.delete=Are you sure you want to delete the selected item?
pane.items.delete.multiple=Are you sure you want to delete the selected items?
pane.items.remove.title=Remove from Collection
@ -327,8 +329,11 @@ pane.items.remove.multiple=Are you sure you want to remove the selected items fr
pane.items.removeFromPublications.title=Remove from My Publications
pane.items.removeFromPublications=Are you sure you want to remove the selected item from My Publications?
pane.items.removeFromPublications.multiple=Are you sure you want to remove the selected items from My Publications?
pane.items.removeRecursive.title=Remove from Collection and Subcollections
pane.items.removeRecursive=Are you sure you want to remove the selected item from this collection and all subcollections?
pane.items.removeRecursive.multiple=Are you sure you want to remove the selected items from this collection and all subcollections?
pane.items.menu.addNoteFromAnnotations=Add Note from Annotations
pane.items.menu.addNoteFromAnnotations.multiple=Add Notes from Annotations
pane.items.menu.createNoteFromAnnotations=Create Note from Annotations
pane.items.menu.findAvailablePDF=Find Available PDF
pane.items.menu.findAvailablePDF.multiple=Find Available PDFs
pane.items.menu.addToCollection=Add to Collection
@ -343,6 +348,8 @@ pane.items.menu.export=Export Selected Item...
pane.items.menu.export.multiple=Export Selected Items...
pane.items.menu.exportNote=Export Note…
pane.items.menu.exportNote.multiple=Export Notes…
pane.items.menu.exportPDF=Export PDF…
pane.items.menu.exportPDF.multiple=Export PDFs…
pane.items.menu.createBib=Create Bibliography from Selected Item...
pane.items.menu.createBib.multiple=Create Bibliography from Selected Items...
pane.items.menu.generateReport=Generate Report from Selected Item...
@ -355,6 +362,8 @@ pane.items.menu.createParent=Create Parent Item…
pane.items.menu.createParent.multiple=Create Parent Items
pane.items.menu.renameAttachments=Rename File from Parent Metadata
pane.items.menu.renameAttachments.multiple=Rename Files from Parent Metadata
pane.items.menu.duplicateAndConvert.toBookSection=Create Book Section
pane.items.menu.duplicateAndConvert.toBook=Create Book from Book Section
pane.items.showItemInLibrary=Show Item in Library
pane.items.letter.oneParticipant=Letter to %S
@ -408,6 +417,16 @@ pane.item.attachments.fileNotFound.text2.stored=It may have been moved or delete
pane.item.attachments.fileNotFound.text2.stored.notOnServer=It may have been moved or deleted outside of %1$S, or, if the file was added on another computer, it may not yet have been synced to %2$S.
pane.item.attachments.fileNotFound.text2.linked=It may have been moved or deleted outside of %1$S, or a Linked Attachment Base Directory may be set incorrectly on one of your computers.
pane.item.attachments.delete.confirm=Are you sure you want to delete this attachment?
pane.item.attachments.autoRelink.title=File Located Automatically
pane.item.attachments.autoRelink.text1=The file could not be found at the specified location, but a file with the same name was found within your Linked Attachment Base Directory:
pane.item.attachments.autoRelink.text2=Old Location: %S
pane.item.attachments.autoRelink.text3=New Location: %S
pane.item.attachments.autoRelink.text4=%1$S can automatically relink this attachment.
pane.item.attachments.autoRelink.relink=Relink
pane.item.attachments.autoRelinkOthers.title=Additional Files Located
pane.item.attachments.autoRelinkOthers.text=One other unlinked attachment in this library was found within the same directory. Relink this attachment as well?;%S other unlinked attachments in this library were found within the same directory. Relink all located attachments?
pane.item.attachments.autoRelink.locateManually=Locate Manually…
pane.item.attachments.autoRelink.relinkAll=Relink All
pane.item.attachments.count.zero=%S attachments:
pane.item.attachments.count.singular=%S attachment:
pane.item.attachments.count.plural=%S attachments:
@ -478,34 +497,34 @@ itemTypes.preprint=Preprint
itemFields.itemType=Type
itemFields.title=Title
itemFields.dateAdded=Date Added
itemFields.dateAdded=Datum bygevoeg
itemFields.dateModified=Modified
itemFields.source=Source
itemFields.notes=Notes
itemFields.tags=Tags
itemFields.attachments=Attachments
itemFields.related=Related
itemFields.notes=Notas
itemFields.tags=Etikette
itemFields.attachments=Aanhegsels
itemFields.related=Verwant
itemFields.url=URL
itemFields.rights=Rights
itemFields.rights=Regte
itemFields.series=Series
itemFields.volume=Volume
itemFields.issue=Issue
itemFields.edition=Edition
itemFields.place=Place
itemFields.publisher=Publisher
itemFields.publisher=Uitgewer
itemFields.pages=Pages
itemFields.ISBN=ISBN
itemFields.publicationTitle=Publication
itemFields.publicationTitle=Publikasie
itemFields.ISSN=ISSN
itemFields.date=Date
itemFields.date=Datum
itemFields.section=Section
itemFields.callNumber=Call Number
itemFields.callNumber=Telefoonnommer
itemFields.archiveLocation=Loc. in Archive
itemFields.distributor=Distributor
itemFields.extra=Extra
itemFields.journalAbbreviation=Journal Abbr
itemFields.journalAbbreviation=Joernaalafkorting
itemFields.DOI=DOI
itemFields.accessDate=Accessed
itemFields.accessDate=Gebruik/Toegang verkry
itemFields.seriesTitle=Series Title
itemFields.seriesText=Series Text
itemFields.seriesNumber=Series Number
@ -529,7 +548,7 @@ itemFields.codeNumber=Code Number
itemFields.artworkMedium=Medium
itemFields.number=Number
itemFields.artworkSize=Artwork Size
itemFields.libraryCatalog=Library Catalog
itemFields.libraryCatalog=Biblioteekkatalogus
itemFields.videoRecordingFormat=Format
itemFields.interviewMedium=Medium
itemFields.letterType=Type
@ -719,6 +738,7 @@ zotero.preferences.export.quickCopy.citationStyles=Citation Styles
zotero.preferences.export.quickCopy.exportFormats=Export Formats
zotero.preferences.export.quickCopy.instructions=Quick Copy allows you to quickly export items in a given format. You can copy selected items to the clipboard by pressing %S or drag items directly into a text box in another program.
zotero.preferences.export.quickCopy.citationInstructions=For citation styles, you can copy citations or footnotes by pressing %S or holding down Shift before dragging items.
zotero.preferences.export.quickCopy.note.htmlOptions.label=Rich Text/HTML
zotero.preferences.wordProcessors.installationSuccess=Installation was successful.
zotero.preferences.wordProcessors.installationError=Installation could not be completed because an error occurred. Please ensure that %1$S is closed, and then restart %2$S.
@ -812,11 +832,11 @@ searchOperator.isInTheLast=is in the last
searchConditions.tooltip.fields=Fields:
searchConditions.collection=Collection
searchConditions.savedSearch=Saved Search
searchConditions.itemTypeID=Item Type
searchConditions.itemTypeID=Tipe item
searchConditions.tag=Tag
searchConditions.note=Note
searchConditions.childNote=Child Note
searchConditions.creator=Creator
searchConditions.creator=Skepper
searchConditions.type=Type
searchConditions.thesisType=Thesis Type
searchConditions.reportType=Report Type
@ -830,7 +850,7 @@ searchConditions.presentationType=Presentation Type
searchConditions.mapType=Map Type
searchConditions.medium=Medium
searchConditions.artworkMedium=Artwork Medium
searchConditions.dateModified=Date Modified
searchConditions.dateModified=Datum gewysig
searchConditions.fulltextContent=Attachment Content
searchConditions.programmingLanguage=Programming Language
searchConditions.fileTypeID=Attachment File Type
@ -863,23 +883,8 @@ citation.singleSource=Single Source...
citation.showEditor=Show Editor...
citation.hideEditor=Hide Editor...
citation.citations=Citations
citation.notes=Notes
citation.locator.page=Page
citation.locator.book=Book
citation.locator.chapter=Chapter
citation.locator.column=Column
citation.locator.figure=Figure
citation.locator.folio=Folio
citation.locator.issue=Issue
citation.locator.line=Line
citation.locator.note=Note
citation.locator.opus=Opus
citation.locator.paragraph=Paragraph
citation.locator.part=Part
citation.locator.section=Section
citation.locator.subverbo=Sub verbo
citation.locator.volume=Volume
citation.locator.verse=Verse
citation.notes=Notas
citation.locator.timestamp=Timestamp
report.title.default=Zotero Report
report.parentItem=Parent Item:
@ -940,7 +945,7 @@ integration.error.misconfiguredWordStartupFolder.some=%S could not install the W
integration.error.misconfiguredWordStartupFolder.all=%S could not install the Word plugin because the Word Startup folder location is misconfigured.
integration.error.misconfiguredWordStartupFolder.fix=The plugin may not appear in Word until you reset your Startup folder to the default location.
integration.error.macWordSBPermissionsMissing.title=Missing Permission
integration.error.macWordSBPermissionsMissing=Zotero does not have permission to control Word. To grant this permission:\n\n1) Open System Preferences\n2) Click on “Security & Privacy”\n3) Select the “Privacy” tab\n4) Find and select “Automation” on the left\n5) Check the checkbox for “Microsoft Word” under “Zotero”\n6) Restart Word
integration.error.macWordSBPermissionsMissing=Zotero does not have permission to control Word. To grant this permission:\n\n1) Open System Settings\n2) Select “Privacy & Security” in the left column\n3) Select “Automation”\n4) Find “Zotero” and click the arrow to expand it\n5) Make sure “Microsoft Word” is enabled under “Zotero”\n6) Restart Word
integration.error.macWordSBPermissionsMissing.pre2016=If “Microsoft Word” does not appear under “Automation”, make sure you are running Word 2011 version 14.7.7 or later.
integration.error.m1UpgradeOS.title=macOS Upgrade Required
integration.error.m1UpgradeOS=Citing in Word on an Apple Silicon Mac with macOS 11.3 or earlier is not supported and may cause %S to freeze. Please upgrade to macOS 11.4 or later.
@ -1098,8 +1103,8 @@ sync.storage.mbRemaining=%SMB remaining
sync.storage.kbRemaining=%SKB remaining
sync.storage.filesRemaining=%1$S/%2$S files
sync.storage.none=None
sync.storage.downloads=Downloads:
sync.storage.uploads=Uploads:
sync.storage.downloads=Aflaaie:
sync.storage.uploads=Oplaaie:
sync.storage.localFile=Local File
sync.storage.remoteFile=Remote File
sync.storage.savedFile=Saved File
@ -1218,6 +1223,8 @@ locate.externalViewer.label=Open in External Viewer
locate.internalViewer.label=Open in Internal Viewer
locate.showFile.label=Show File
locate.libraryLookup.label=Library Lookup
locate.libraryLookup.noResolver.title=No OpenURL Resolver
locate.libraryLookup.noResolver.text=You must choose an OpenURL resolver from the Advanced pane of the %S preferences.
locate.manageLocateEngines=Manage Lookup Engines...
locate.locateEngineDescription=Lookup engines help you find resources in your %S library on the web. Engines enabled in this list appear in the Locate drop-down in the toolbar.
@ -1303,20 +1310,21 @@ noteEditor.enterLink=Enter Link
noteEditor.heading1=Heading 1
noteEditor.heading2=Heading 2
noteEditor.heading3=Heading 3
noteEditor.paragraph=Paragraph
noteEditor.paragraph=Paragraaf
noteEditor.monospaced=Monospaced
noteEditor.bulletList=Bulleted List
noteEditor.orderedList=Numbered List
noteEditor.blockquote=Block Quote
noteEditor.mathBlock=Math Block
noteEditor.formatText=Format Text
noteEditor.highlightText=Highlight Text
noteEditor.highlightText=Verligmerk teks
noteEditor.removeColor=Remove Color
noteEditor.bold=Bold
noteEditor.italic=Italic
noteEditor.underline=Underline
noteEditor.bold=Vetdruk
noteEditor.italic=Kursief
noteEditor.underline=Onderstreep
noteEditor.strikethrough=Strikethrough
noteEditor.subscript=Subscript
noteEditor.superscript=Superscript
noteEditor.subscript=Onderskrif
noteEditor.superscript=Boskrif
noteEditor.returnToNotesList=Return to Notes List
noteEditor.insertLink=Insert Link
noteEditor.clearFormatting=Clear Formatting
@ -1338,7 +1346,7 @@ noteEditor.editCitation=Edit Citation
noteEditor.showOnPage=Show on Page
noteEditor.unlink=Unlink
noteEditor.set=Set
noteEditor.edit=Edit
noteEditor.edit=Redigeer
noteEditor.addCitation=Add Citation
noteEditor.removeCitation=Hide Citation
noteEditor.findAndReplace=Find and Replace
@ -1347,7 +1355,11 @@ noteEditor.applyAnnotationColors=Show Annotation Colors
noteEditor.removeAnnotationColors=Hide Annotation Colors
noteEditor.addCitations=Show Annotation Citations
noteEditor.removeCitations=Hide Annotation Citations
noteEditor.insertTable=Insert Table
noteEditor.image=Image
noteEditor.math=Math
noteEditor.table=Table
noteEditor.copyImage=Copy Image
noteEditor.saveImageAs=Save Image As…
noteEditor.insertRowBefore=Insert Row Above
noteEditor.insertRowAfter=Insert Row Below
noteEditor.insertColumnBefore=Insert Column Left
@ -1360,10 +1372,10 @@ pdfReader.annotations=Annotations
pdfReader.showAnnotations=Show Annotations
pdfReader.searchAnnotations=Search Annotations
pdfReader.noAnnotations=Create an annotation to see it in the sidebar
pdfReader.addText=Add extracted text
pdfReader.noExtractedText=No extracted text
pdfReader.addComment=Add comment
pdfReader.addTags=Add tags…
pdfReader.highlightText=Highlight Text
pdfReader.highlightText=Verligmerk teks
pdfReader.addNote=Add Note
pdfReader.selectArea=Select Area
pdfReader.pickColor=Pick a Color
@ -1373,17 +1385,26 @@ pdfReader.zoomOut=Zoom Out
pdfReader.zoomAuto=Automatically Resize
pdfReader.zoomPageWidth=Zoom to Page Width
pdfReader.zoomPageHeight=Zoom to Page Height
pdfReader.splitVertically=Split Vertically
pdfReader.splitHorizontally=Split Horizontally
pdfReader.nextPage=Next Page
pdfReader.previousPage=Previous Page
pdfReader.page=Page
pdfReader.page=Bladsy
pdfReader.readOnly=Read-only
pdfReader.promptTransferFromPDF.title=Import Annotations
pdfReader.promptTransferFromPDF.text=Annotations stored in the PDF file will be moved to %1$S.
pdfReader.promptTransferToPDF.title=Store Annotations in File
pdfReader.promptTransferToPDF.text=Annotations will be transferred to the PDF file and will no longer be editable in %S.
pdfReader.promptPasswordProtected=The operation is not supported for password-protected PDF files.
pdfReader.promptDeletePages.title=Delete Pages
pdfReader.promptDeletePages.text=Are you sure you want to delete %1$S page from the PDF file?;Are you sure you want to delete %1$S pages from the PDF file?
pdfReader.rotateLeft=Rotate Left
pdfReader.rotateRight=Rotate Right
pdfReader.rotate180=Rotate 180°
pdfReader.editPageNumber=Edit Page Number…
pdfReader.editHighlightedText=Edit Highlighted Text
pdfReader.copyImage=Copy Image
pdfReader.saveImageAs=Save Image As…
pdfReader.pageNumberPopupHeader=Change page number for:
pdfReader.thisAnnotation=This annotation
pdfReader.selectedAnnotations=Selected annotations

View file

@ -5,6 +5,12 @@
"general_done": {
"message": "تم"
},
"general_yes": {
"message": "نعم"
},
"general_no": {
"message": "لا"
},
"general_tryAgain": {
"message": "حاول مرة أخرى"
},
@ -14,6 +20,9 @@
"general_cancel": {
"message": "إلغاء"
},
"general_copyToClipboard": {
"message": "انسخ إلى الحافظة"
},
"progressWindow_savingTo": {
"message": "الحفظ في"
},

View file

@ -6,7 +6,7 @@
<!ENTITY zotero.general.deselectAll "إلغاء تحديد الكل">
<!ENTITY zotero.general.edit "تحرير">
<!ENTITY zotero.general.delete "حذف">
<!ENTITY zotero.general.moveToTrash "Move to Trash">
<!ENTITY zotero.general.moveToTrash "نقل لسلة المحذوفات">
<!ENTITY zotero.general.ok "موافق">
<!ENTITY zotero.general.cancel "الغاء">
<!ENTITY zotero.general.refresh "اعادة تحميل">
@ -307,12 +307,10 @@
<!ENTITY zotero.context.addStandaloneNoteFromAnnotations "Add Standalone Note from Annotations">
<!ENTITY zotero.context.editInWindow "Edit in a Separate Window">
<!ENTITY zotero.pdfReader.firstPage "First Page">
<!ENTITY zotero.pdfReader.firstPage "الصفحة الأولى">
<!ENTITY zotero.pdfReader.lastPage "Last Page">
<!ENTITY zotero.pdfReader.back "Back">
<!ENTITY zotero.pdfReader.forward "Forward">
<!ENTITY zotero.pdfReader.rotateClockwise "Rotate Clockwise">
<!ENTITY zotero.pdfReader.rotateCounterclockwise "Rotate Counterclockwise">
<!ENTITY zotero.pdfReader.handTool "Hand Tool">
<!ENTITY zotero.pdfReader.verticalScrolling "Vertical Scrolling">
<!ENTITY zotero.pdfReader.horizontalScrolling "Horizontal Scrolling">
@ -325,4 +323,9 @@
<!ENTITY zotero.pdfReader.zoomAuto "Automatically Resize">
<!ENTITY zotero.pdfReader.zoomPageWidth "Zoom to Page Width">
<!ENTITY zotero.pdfReader.zoomPageHeight "Zoom to Page Height">
<!ENTITY zotero.pdfReader.splitVertically "Split Vertically">
<!ENTITY zotero.pdfReader.splitHorizontally "Split Horizontally">
<!ENTITY zotero.pdfReader.transferFromPDF "Import Annotations…">
<!ENTITY zotero.pdfReader.rotatePageLeft "Rotate Page Left">
<!ENTITY zotero.pdfReader.rotatePageRight "Rotate Page Right">
<!ENTITY zotero.pdfReader.rotatePage180 "Rotate Page 180°">

View file

@ -89,9 +89,10 @@ general.back=Back
general.languages=Languages
general.default=Default
general.custom=Custom
general.loading=Loading…
general.loading=التحميل
general.richText=Rich Text
general.clearSelection=Clear Selection
general.insert=Insert
general.yellow=Yellow
general.red=Red
@ -114,6 +115,7 @@ about.getInvolved=هل تريد المساعدة ؟ [شارك] اليوم!
punctuation.openingQMark=“
punctuation.closingQMark=”
punctuation.colon=:
punctuation.colon.withString=%S:
punctuation.ellipsis=…
install.quickStartGuide=دليل الاستخدام المبسط لزوتيرو
@ -183,7 +185,7 @@ dataDir.unsafeLocation.existing.cloud=Your %S data directory appears to be withi
dataDir.unsafeLocation.existing.chooseDifferent=Would you like to choose a different location now?
dataDir.selectedDirNonEmpty.title=المجلد غير فارغ
dataDir.selectedDirNonEmpty.text=المجلد المحدد غير فارغ، ولا يبدو انه مجلد بيانات زوتيرو.\n\nهل ترغب بإنشاء ملفات زوتيرو في هذا الدليل؟
dataDir.mustSelectEmpty.title=Directory Not Empty
dataDir.mustSelectEmpty.title=المجلد غير فارغ
dataDir.mustSelectEmpty.text=The directory you selected is not empty. You must select an empty directory to continue.
dataDir.selectedDirEmpty.title=مجلد فارغ
dataDir.selectedDirEmpty.text=الدليل الذي حددته فارغ. لنقل دليل بيانات زوتيرو موجود ، ستحتاج إلى نقل الملفات يدوياً من دليل البيانات الموجود إلى الموقع الجديد بعد إغلاق %1$S.
@ -255,10 +257,10 @@ pane.collections.savedSearchName=أدخل اسماً لهذا البحث الم
pane.collections.rename=إعادة تسمية مجموعة العناصر:
pane.collections.library=مكتبتي
pane.collections.publications=منشوراتي
pane.collections.feeds=Feeds
pane.collections.feeds=الملقمات
pane.collections.libraryAndFeeds=My Library & Feeds
pane.collections.groupLibraries=مكتبات المجموعات
pane.collections.feedLibraries=Feeds
pane.collections.feedLibraries=الملقمات
pane.collections.trash=سلة المحذوفات
pane.collections.untitled=بدون عنوان
pane.collections.unfiled=العناصر الغير مصنفة
@ -270,7 +272,7 @@ pane.collections.removeLibrary.text=Are you sure you want to permanently remove
pane.collections.menu.rename.collection=إعادة تسمية مجموعة العناصر...
pane.collections.menu.duplicate.savedSearch=تكرار البحث المحفوظ
pane.collections.menu.edit.savedSearch=تحرير البحث المحفوظ
pane.collections.menu.edit.feed=Edit Feed…
pane.collections.menu.edit.feed=تحرير الخلاصة...
pane.collections.menu.remove.library=إزالة المكتبة...
pane.collections.menu.delete.collection=امسح مجموعة العناصر...
pane.collections.menu.delete.collectionAndItems=Delete Collection and Items…
@ -288,7 +290,7 @@ pane.collections.menu.generateReport.collection=انتاج تقرير من مج
pane.collections.menu.generateReport.savedSearch=انتاج تقرير من البحث المخزن...
pane.collections.menu.generateReport.feed=Generate Report from Feed…
pane.collections.menu.refresh.feed=Refresh Feed
pane.collections.menu.refresh.feed=تحديث الخلاصة
pane.tagSelector.rename.title=إعادة تسمية الوسم
pane.tagSelector.rename.message=من فضلك ادخل إسماً لهذا الوسم الجديد.\n\nسيتم إعادة تسمية الوسم في جميع العناصر المرتبطة.
@ -327,8 +329,11 @@ pane.items.remove.multiple=Are you sure you want to remove the selected items fr
pane.items.removeFromPublications.title=أزل من منشوراتي
pane.items.removeFromPublications=Are you sure you want to remove the selected item from My Publications?
pane.items.removeFromPublications.multiple=Are you sure you want to remove the selected items from My Publications?
pane.items.removeRecursive.title=Remove from Collection and Subcollections
pane.items.removeRecursive=Are you sure you want to remove the selected item from this collection and all subcollections?
pane.items.removeRecursive.multiple=Are you sure you want to remove the selected items from this collection and all subcollections?
pane.items.menu.addNoteFromAnnotations=Add Note from Annotations
pane.items.menu.addNoteFromAnnotations.multiple=Add Notes from Annotations
pane.items.menu.createNoteFromAnnotations=Create Note from Annotations
pane.items.menu.findAvailablePDF=ابحث عن ملف PDF متاح
pane.items.menu.findAvailablePDF.multiple=ابحث عن ملفات PDF متاحة
pane.items.menu.addToCollection=Add to Collection
@ -338,11 +343,13 @@ pane.items.menu.removeFromPublications=أزل العنصر من منشوراتي
pane.items.menu.removeFromPublications.multiple=أزل العناصر من منشوراتي...
pane.items.menu.moveToTrash=انقل العنصر لسلة المحذوفات...
pane.items.menu.moveToTrash.multiple=انقل العناصر لسلة المحذوفات...
pane.items.menu.delete=Delete Permanently…
pane.items.menu.delete=حذف نهائي...
pane.items.menu.export=تصدير العنصر المحدد...
pane.items.menu.export.multiple=تصدير العناصر المحددة...
pane.items.menu.exportNote=Export Note…
pane.items.menu.exportNote.multiple=Export Notes…
pane.items.menu.exportPDF=Export PDF…
pane.items.menu.exportPDF.multiple=Export PDFs…
pane.items.menu.createBib=إنشاء ببليوجرافية من العنصر المحدد...
pane.items.menu.createBib.multiple=إنشاء ببليوجرافية من العناصر المحددة...
pane.items.menu.generateReport=انتاج تقرير من العنصر المحدد...
@ -355,6 +362,8 @@ pane.items.menu.createParent=انشئ عنصر رئيسي…
pane.items.menu.createParent.multiple=انشئ عناصر رئيسية
pane.items.menu.renameAttachments=إعادة تسمية الملف وفقا للبيانات الوصفية للعنصر الرئيسي
pane.items.menu.renameAttachments.multiple=إعادة تسمية الملفات وفقا للبيانات الوصفية للعنصر الرئيسي
pane.items.menu.duplicateAndConvert.toBookSection=Create Book Section
pane.items.menu.duplicateAndConvert.toBook=Create Book from Book Section
pane.items.showItemInLibrary=عرض العنصر في المكتبة
pane.items.letter.oneParticipant=خطاب إلى %S
@ -408,6 +417,16 @@ pane.item.attachments.fileNotFound.text2.stored=It may have been moved or delete
pane.item.attachments.fileNotFound.text2.stored.notOnServer=It may have been moved or deleted outside of %1$S, or, if the file was added on another computer, it may not yet have been synced to %2$S.
pane.item.attachments.fileNotFound.text2.linked=It may have been moved or deleted outside of %1$S, or a Linked Attachment Base Directory may be set incorrectly on one of your computers.
pane.item.attachments.delete.confirm=هل ترغب في حذف هذا المرفق؟
pane.item.attachments.autoRelink.title=File Located Automatically
pane.item.attachments.autoRelink.text1=The file could not be found at the specified location, but a file with the same name was found within your Linked Attachment Base Directory:
pane.item.attachments.autoRelink.text2=Old Location: %S
pane.item.attachments.autoRelink.text3=New Location: %S
pane.item.attachments.autoRelink.text4=%1$S can automatically relink this attachment.
pane.item.attachments.autoRelink.relink=Relink
pane.item.attachments.autoRelinkOthers.title=Additional Files Located
pane.item.attachments.autoRelinkOthers.text=One other unlinked attachment in this library was found within the same directory. Relink this attachment as well?;%S other unlinked attachments in this library were found within the same directory. Relink all located attachments?
pane.item.attachments.autoRelink.locateManually=Locate Manually…
pane.item.attachments.autoRelink.relinkAll=Relink All
pane.item.attachments.count.zero=لا توجد مرفقات:
pane.item.attachments.count.singular=%S مرفقات:
pane.item.attachments.count.plural=%S مرفق:
@ -430,11 +449,11 @@ pane.item.related.count.zero=لا توجد ارتباطات:
pane.item.related.count.singular=%S ارتباطات:
pane.item.related.count.plural=%S ارتباط:
pane.item.parentItem=عنصر رئيسي:
pane.item.viewOnline.tooltip=Go to this item online
pane.item.viewOnline.tooltip=الذهاب لهذا العنصر على الانترنت
pane.context.noParent=No parent item
pane.context.itemNotes=Item Notes
pane.context.allNotes=All Notes
pane.context.allNotes=كل الملاحظات
pane.context.noNotes=No notes
itemTypes.note=ملاحظة
@ -547,7 +566,7 @@ itemFields.runningTime=وقت التشغيل
itemFields.network=الشبكة
itemFields.postType=نوع الموضوع
itemFields.audioFileType=نوع الملف الصوتي
itemFields.versionNumber=Version
itemFields.versionNumber=الإصدارة
itemFields.system=النظام
itemFields.company=الشركة
itemFields.conferenceName=اسم المؤتمر
@ -719,6 +738,7 @@ zotero.preferences.export.quickCopy.citationStyles=أنماط الإستشهاد
zotero.preferences.export.quickCopy.exportFormats=صيغ التصدير
zotero.preferences.export.quickCopy.instructions=النسخ السريع يسمح لك بتصدير العناصر في صيغة معينة. يمكن أن تنسخ العناصر المختارة إلى الحافظة بضغط %S أو سحب العناصر مباشرة داخل مربع نص في برنامج آخر.
zotero.preferences.export.quickCopy.citationInstructions=لأنماط الاستشهاد، يمكنك نسخ الاستشهادات أو الحواشي السفلية عن طريق الضغط على %S أو الضغط باستمرار على Shift قبل سحب العناصر.
zotero.preferences.export.quickCopy.note.htmlOptions.label=Rich Text/HTML
zotero.preferences.wordProcessors.installationSuccess=تم اكتمال التنصيب
zotero.preferences.wordProcessors.installationError=Installation could not be completed because an error occurred. Please ensure that %1$S is closed, and then restart %2$S.
@ -757,7 +777,7 @@ zotero.debugOutputLogging.enabledAfterRestart=Debug output logging will be enabl
dragAndDrop.existingFiles=لم يتم نسخ الملفات التالية لأنها موجودة بالفعل في المجلد الهدف:
dragAndDrop.filesNotFound=لم يتم نسخ الملفات التالية لأنه لم يتم العثور عليها:
fileInterface.importing=Importing…
fileInterface.importing=استيراد...
fileInterface.importComplete=Import Complete
fileInterface.itemsWereImported=%1$S item was imported;%1$S items were imported
fileInterface.itemsExported=تصدير العناصر...
@ -864,22 +884,7 @@ citation.showEditor=عرض المحرر...
citation.hideEditor=إخفاء المحرر...
citation.citations=استشهادات
citation.notes=ملاحظات
citation.locator.page=صفحة
citation.locator.book=كتاب
citation.locator.chapter=فصل
citation.locator.column=عمود
citation.locator.figure=شكل
citation.locator.folio=Folio
citation.locator.issue=اصدار
citation.locator.line=سطر
citation.locator.note=ملاحظة
citation.locator.opus=Opus
citation.locator.paragraph=فقرة
citation.locator.part=جزء
citation.locator.section=قسم
citation.locator.subverbo=Sub verbo
citation.locator.volume=مجلد
citation.locator.verse=Verse
citation.locator.timestamp=Timestamp
report.title.default=تقرير زوتيرو
report.parentItem=عنصر رئيسي:
@ -940,7 +945,7 @@ integration.error.misconfiguredWordStartupFolder.some=%S could not install the W
integration.error.misconfiguredWordStartupFolder.all=%S could not install the Word plugin because the Word Startup folder location is misconfigured.
integration.error.misconfiguredWordStartupFolder.fix=The plugin may not appear in Word until you reset your Startup folder to the default location.
integration.error.macWordSBPermissionsMissing.title=إذن مفقود
integration.error.macWordSBPermissionsMissing=Zotero does not have permission to control Word. To grant this permission:\n\n1) Open System Preferences\n2) Click on “Security & Privacy”\n3) Select the “Privacy” tab\n4) Find and select “Automation” on the left\n5) Check the checkbox for “Microsoft Word” under “Zotero”\n6) Restart Word
integration.error.macWordSBPermissionsMissing=Zotero does not have permission to control Word. To grant this permission:\n\n1) Open System Settings\n2) Select “Privacy & Security” in the left column\n3) Select “Automation”\n4) Find “Zotero” and click the arrow to expand it\n5) Make sure “Microsoft Word” is enabled under “Zotero”\n6) Restart Word
integration.error.macWordSBPermissionsMissing.pre2016=If “Microsoft Word” does not appear under “Automation”, make sure you are running Word 2011 version 14.7.7 or later.
integration.error.m1UpgradeOS.title=macOS Upgrade Required
integration.error.m1UpgradeOS=Citing in Word on an Apple Silicon Mac with macOS 11.3 or earlier is not supported and may cause %S to freeze. Please upgrade to macOS 11.4 or later.
@ -1005,7 +1010,7 @@ sync.skipGroup=Skip Group
sync.removeGroupsAndSync=ازالة مجموعات المشاركة و المزامنة
sync.reminder.setUp.message=Back up your library with %S syncing.
sync.reminder.setUp.action=Set Up Syncing
sync.reminder.setUp.action=إعداد المزامنة
sync.reminder.autoSync.message=%S hasn’t synced in a while. Do you want to enable automatic syncing?
sync.error.usernameNotSet=لم يتم تعيين اسم المستخدم
@ -1072,8 +1077,8 @@ sync.conflict.tag.addedToLocal=It has been added to the following local items:
sync.conflict.localItem=Local Item
sync.conflict.remoteItem=Remote Item
sync.conflict.mergedItem=Merged Item
sync.conflict.localFile=Local File
sync.conflict.remoteFile=Remote File
sync.conflict.localFile=ملف محلي
sync.conflict.remoteFile=ملف عن بعد
sync.conflict.resolveAllLocal=Use the local version for all remaining conflicts
sync.conflict.resolveAllRemote=Use the remote version for all remaining conflicts
sync.conflict.itemChanged=The following item has been changed in multiple locations. Click the version to use for resolving conflicting fields, and then click %S.
@ -1124,7 +1129,7 @@ sync.storage.error.createNow=هل تريد إنشاؤه الآن؟
sync.storage.error.webdav.default=A WebDAV file sync error occurred. Please try syncing again.\n\nIf you receive this message repeatedly, check your WebDAV server settings in the Sync pane of the Zotero preferences.
sync.storage.error.webdav.defaultRestart=A WebDAV file sync error occurred. Please restart %S and try syncing again.\n\nIf you receive this message repeatedly, check your WebDAV server settings in the Sync pane of the Zotero preferences.
sync.storage.error.webdav.enterURL=Please enter a WebDAV URL.
sync.storage.error.webdav.enterURL=برجاء ادخال موقع WebDAV.
sync.storage.error.webdav.invalidURL=%S موقع WebDAV غير صالح.
sync.storage.error.webdav.invalidLogin=لم يقبل خادم WebDAV اسم المستخدم وكلمة السر التي قمت بإدخالهما.
sync.storage.error.webdav.permissionDenied=ليس لديك الصلاحية للوصول الى %S على خادم WebDAV.
@ -1218,6 +1223,8 @@ locate.externalViewer.label=فتح في عارض خارجي
locate.internalViewer.label=فتح في العارض الداخلي
locate.showFile.label=عرض الملف
locate.libraryLookup.label=البحث في المكتبة
locate.libraryLookup.noResolver.title=No OpenURL Resolver
locate.libraryLookup.noResolver.text=You must choose an OpenURL resolver from the Advanced pane of the %S preferences.
locate.manageLocateEngines=إدارة محرك البحث...
locate.locateEngineDescription=Lookup engines help you find resources in your %S library on the web. Engines enabled in this list appear in the Locate drop-down in the toolbar.
@ -1293,9 +1300,9 @@ retraction.citationWarning=سُحِب أحد الاقتباسات في مستن
retraction.citationWarning.dontWarn=لا تحذرني حول هذا العنصر ثانية
noteEditor.annotationsDateLine=(%S)
noteEditor.cut=Cut
noteEditor.copy=Copy
noteEditor.paste=Paste
noteEditor.cut=قص
noteEditor.copy=نسخ
noteEditor.paste=لصق
noteEditor.rightToLeft=Right to Left
noteEditor.leftToRight=Left to Right
noteEditor.updateNotice=This note was edited with a newer version of %1$S.\nPlease update %1$S to make changes.
@ -1303,20 +1310,21 @@ noteEditor.enterLink=Enter Link
noteEditor.heading1=Heading 1
noteEditor.heading2=Heading 2
noteEditor.heading3=Heading 3
noteEditor.paragraph=Paragraph
noteEditor.paragraph=فقرة
noteEditor.monospaced=Monospaced
noteEditor.bulletList=Bulleted List
noteEditor.orderedList=Numbered List
noteEditor.blockquote=Block Quote
noteEditor.mathBlock=Math Block
noteEditor.formatText=Format Text
noteEditor.highlightText=Highlight Text
noteEditor.removeColor=Remove Color
noteEditor.bold=Bold
noteEditor.italic=Italic
noteEditor.underline=Underline
noteEditor.highlightText=تمييز النص
noteEditor.removeColor=ازالة اللون
noteEditor.bold=غامق
noteEditor.italic=مائل
noteEditor.underline=تسطير
noteEditor.strikethrough=Strikethrough
noteEditor.subscript=Subscript
noteEditor.superscript=Superscript
noteEditor.subscript=منخفض
noteEditor.superscript=مرتفع
noteEditor.returnToNotesList=Return to Notes List
noteEditor.insertLink=Insert Link
noteEditor.clearFormatting=Clear Formatting
@ -1325,8 +1333,8 @@ noteEditor.alignLeft=Align Left
noteEditor.alignCenter=Align Center
noteEditor.alignRight=Align Right
noteEditor.insertCitation=Insert Citation
noteEditor.more=More
noteEditor.find=Find
noteEditor.more=المزيد
noteEditor.find=اعثر
noteEditor.replace=Replace
noteEditor.previous=Previous
noteEditor.next=Next
@ -1338,7 +1346,7 @@ noteEditor.editCitation=Edit Citation
noteEditor.showOnPage=Show on Page
noteEditor.unlink=Unlink
noteEditor.set=Set
noteEditor.edit=Edit
noteEditor.edit=تحرير
noteEditor.addCitation=Add Citation
noteEditor.removeCitation=Hide Citation
noteEditor.findAndReplace=Find and Replace
@ -1347,7 +1355,11 @@ noteEditor.applyAnnotationColors=Show Annotation Colors
noteEditor.removeAnnotationColors=Hide Annotation Colors
noteEditor.addCitations=Show Annotation Citations
noteEditor.removeCitations=Hide Annotation Citations
noteEditor.insertTable=Insert Table
noteEditor.image=صورة
noteEditor.math=Math
noteEditor.table=Table
noteEditor.copyImage=Copy Image
noteEditor.saveImageAs=Save Image As…
noteEditor.insertRowBefore=Insert Row Above
noteEditor.insertRowAfter=Insert Row Below
noteEditor.insertColumnBefore=Insert Column Left
@ -1360,11 +1372,11 @@ pdfReader.annotations=Annotations
pdfReader.showAnnotations=عرض التعليقات
pdfReader.searchAnnotations=بحث في التعليقات
pdfReader.noAnnotations=Create an annotation to see it in the sidebar
pdfReader.addText=Add extracted text
pdfReader.noExtractedText=No extracted text
pdfReader.addComment=Add comment
pdfReader.addTags=Add tags…
pdfReader.highlightText=Highlight Text
pdfReader.addNote=Add Note
pdfReader.highlightText=تمييز النص
pdfReader.addNote=اضافة ملاحظة
pdfReader.selectArea=Select Area
pdfReader.pickColor=Pick a Color
pdfReader.addToNote=Add to Note
@ -1373,17 +1385,26 @@ pdfReader.zoomOut=Zoom Out
pdfReader.zoomAuto=Automatically Resize
pdfReader.zoomPageWidth=Zoom to Page Width
pdfReader.zoomPageHeight=Zoom to Page Height
pdfReader.splitVertically=Split Vertically
pdfReader.splitHorizontally=Split Horizontally
pdfReader.nextPage=Next Page
pdfReader.previousPage=Previous Page
pdfReader.page=Page
pdfReader.page=صفحة
pdfReader.readOnly=Read-only
pdfReader.promptTransferFromPDF.title=Import Annotations
pdfReader.promptTransferFromPDF.text=Annotations stored in the PDF file will be moved to %1$S.
pdfReader.promptTransferToPDF.title=Store Annotations in File
pdfReader.promptTransferToPDF.text=Annotations will be transferred to the PDF file and will no longer be editable in %S.
pdfReader.promptPasswordProtected=The operation is not supported for password-protected PDF files.
pdfReader.promptDeletePages.title=Delete Pages
pdfReader.promptDeletePages.text=Are you sure you want to delete %1$S page from the PDF file?;Are you sure you want to delete %1$S pages from the PDF file?
pdfReader.rotateLeft=Rotate Left
pdfReader.rotateRight=Rotate Right
pdfReader.rotate180=Rotate 180°
pdfReader.editPageNumber=Edit Page Number…
pdfReader.editHighlightedText=Edit Highlighted Text
pdfReader.copyImage=Copy Image
pdfReader.saveImageAs=Save Image As…
pdfReader.pageNumberPopupHeader=Change page number for:
pdfReader.thisAnnotation=هذا التعليق
pdfReader.selectedAnnotations=Selected annotations

View file

@ -5,12 +5,24 @@
"general_done": {
"message": "Готово"
},
"general_yes": {
"message": "Да"
},
"general_no": {
"message": "Не"
},
"general_tryAgain": {
"message": "Опитайте отново"
},
"general_warning": {
"message": "Предупреждение"
},
"general_cancel": {
"message": "Отказ"
},
"general_copyToClipboard": {
"message": "Копиране в клипборда"
},
"progressWindow_savingTo": {
"message": "Записване в"
},

View file

@ -6,7 +6,7 @@
<!ENTITY zotero.general.deselectAll "Отказ на всички">
<!ENTITY zotero.general.edit "Редактиране">
<!ENTITY zotero.general.delete "Изтриване">
<!ENTITY zotero.general.moveToTrash "Move to Trash">
<!ENTITY zotero.general.moveToTrash "Премества в кошчето за боклук">
<!ENTITY zotero.general.ok "OK">
<!ENTITY zotero.general.cancel "Отказ">
<!ENTITY zotero.general.refresh "Опресняване">
@ -21,7 +21,7 @@
<!ENTITY zotero.general.other "Други...">
<!ENTITY zotero.general.bigger "Bigger">
<!ENTITY zotero.general.smaller "Smaller">
<!ENTITY zotero.general.reset "Reset">
<!ENTITY zotero.general.reset "Нулиране">
<!ENTITY zotero.errorReport.title "Зотеро доклад за проблем">
@ -93,7 +93,7 @@
<!ENTITY zotero.toolbar.import.label "Внасяне...">
<!ENTITY zotero.toolbar.importFromClipboard "Внасяне от клипборда">
<!ENTITY zotero.toolbar.export.label "Изнасяне на библиотека...">
<!ENTITY zotero.toolbar.rtfScan.label "RTF Scan">
<!ENTITY zotero.toolbar.rtfScan.label "Сканиране на RTF">
<!ENTITY zotero.toolbar.timeline.label "Създаване на хронология">
<!ENTITY zotero.toolbar.preferences.label "Предпочитания...">
<!ENTITY zotero.toolbar.supportAndDocumentation "Подръжка и документация">
@ -128,7 +128,7 @@
<!ENTITY zotero.toolbar.attachment.add "Запазване на копие от файл">
<!ENTITY zotero.toolbar.attachment.weblink "Запазване на връзка към настоящата страница">
<!ENTITY zotero.toolbar.attachment.snapshot "Направи &quot;моментна снимка&quot; на настоящата страница">
<!ENTITY zotero.toolbar.context.item "Item">
<!ENTITY zotero.toolbar.context.item "Елемент">
<!ENTITY zotero.toolbar.context.notes "Notes">
<!ENTITY zotero.tagSelector.noTagsToDisplay "Няма маркери, които да бъдат показани">
@ -185,7 +185,7 @@
<!ENTITY zotero.import.lastModified "Променено за последно">
<!ENTITY zotero.import.size "Размер">
<!ENTITY zotero.import.createCollection "Поместване на внесените колекции и записи в нова колекция">
<!ENTITY zotero.import.fileHandling "File Handling">
<!ENTITY zotero.import.fileHandling "Обработка на файловете">
<!ENTITY zotero.exportOptions.title "Изнасяне...">
<!ENTITY zotero.exportOptions.format.label "Формат:">
@ -307,12 +307,10 @@
<!ENTITY zotero.context.addStandaloneNoteFromAnnotations "Add Standalone Note from Annotations">
<!ENTITY zotero.context.editInWindow "Edit in a Separate Window">
<!ENTITY zotero.pdfReader.firstPage "First Page">
<!ENTITY zotero.pdfReader.firstPage "Първа страница">
<!ENTITY zotero.pdfReader.lastPage "Last Page">
<!ENTITY zotero.pdfReader.back "Back">
<!ENTITY zotero.pdfReader.forward "Forward">
<!ENTITY zotero.pdfReader.rotateClockwise "Rotate Clockwise">
<!ENTITY zotero.pdfReader.rotateCounterclockwise "Rotate Counterclockwise">
<!ENTITY zotero.pdfReader.handTool "Hand Tool">
<!ENTITY zotero.pdfReader.verticalScrolling "Vertical Scrolling">
<!ENTITY zotero.pdfReader.horizontalScrolling "Horizontal Scrolling">
@ -325,4 +323,9 @@
<!ENTITY zotero.pdfReader.zoomAuto "Automatically Resize">
<!ENTITY zotero.pdfReader.zoomPageWidth "Zoom to Page Width">
<!ENTITY zotero.pdfReader.zoomPageHeight "Zoom to Page Height">
<!ENTITY zotero.pdfReader.splitVertically "Split Vertically">
<!ENTITY zotero.pdfReader.splitHorizontally "Split Horizontally">
<!ENTITY zotero.pdfReader.transferFromPDF "Import Annotations…">
<!ENTITY zotero.pdfReader.rotatePageLeft "Rotate Page Left">
<!ENTITY zotero.pdfReader.rotatePageRight "Rotate Page Right">
<!ENTITY zotero.pdfReader.rotatePage180 "Rotate Page 180°">

View file

@ -52,7 +52,7 @@ general.moreInformation=Повече информация
general.learnMore=Learn More
general.seeForMoreInformation=Вижте %S за повече информация.
general.open=Отваряне на %S
general.close=Close
general.close=Затвори
general.enable=Включване
general.disable=Изключване
general.reset=Нулиране
@ -71,7 +71,7 @@ general.fix=Поправяне…
general.tryAgain=Опит отново
general.tryLater=Опит по-късно
general.showDirectory=Показване на директорията
general.showInLibrary=Show in Library
general.showInLibrary=Показване в библиотеката
general.continue=Продължаване
general.copy=Copy
general.copyToClipboard=Копиране в клипборда
@ -89,9 +89,10 @@ general.back=Back
general.languages=Languages
general.default=Default
general.custom=Custom
general.loading=Loading…
general.loading=Зареждане...
general.richText=Rich Text
general.clearSelection=Clear Selection
general.insert=Insert
general.yellow=Yellow
general.red=Red
@ -114,6 +115,7 @@ about.getInvolved=Искате да помогнете? [Включете се]
punctuation.openingQMark=“
punctuation.closingQMark=”
punctuation.colon=:
punctuation.colon.withString=%S:
punctuation.ellipsis=…
install.quickStartGuide=Кратко ръководство за начинаещи
@ -327,8 +329,11 @@ pane.items.remove.multiple=Are you sure you want to remove the selected items fr
pane.items.removeFromPublications.title=Remove from My Publications
pane.items.removeFromPublications=Are you sure you want to remove the selected item from My Publications?
pane.items.removeFromPublications.multiple=Are you sure you want to remove the selected items from My Publications?
pane.items.removeRecursive.title=Remove from Collection and Subcollections
pane.items.removeRecursive=Are you sure you want to remove the selected item from this collection and all subcollections?
pane.items.removeRecursive.multiple=Are you sure you want to remove the selected items from this collection and all subcollections?
pane.items.menu.addNoteFromAnnotations=Add Note from Annotations
pane.items.menu.addNoteFromAnnotations.multiple=Add Notes from Annotations
pane.items.menu.createNoteFromAnnotations=Create Note from Annotations
pane.items.menu.findAvailablePDF=Find Available PDF
pane.items.menu.findAvailablePDF.multiple=Find Available PDFs
pane.items.menu.addToCollection=Add to Collection
@ -338,11 +343,13 @@ pane.items.menu.removeFromPublications=Remove Item from My Publications…
pane.items.menu.removeFromPublications.multiple=Remove Items from My Publications…
pane.items.menu.moveToTrash=Move Item to Trash…
pane.items.menu.moveToTrash.multiple=Move Items to Trash…
pane.items.menu.delete=Delete Permanently…
pane.items.menu.delete=Изтриване окончателно...
pane.items.menu.export=Експорт на избрания запис...
pane.items.menu.export.multiple=Експорт на избраните записи...
pane.items.menu.exportNote=Export Note…
pane.items.menu.exportNote.multiple=Export Notes…
pane.items.menu.exportPDF=Export PDF…
pane.items.menu.exportPDF.multiple=Export PDFs…
pane.items.menu.createBib=Създава библиография от избрания запис...
pane.items.menu.createBib.multiple=Създава библиография от избраните записи...
pane.items.menu.generateReport=Създава отчет от избрания запис...
@ -355,6 +362,8 @@ pane.items.menu.createParent=Create Parent Item…
pane.items.menu.createParent.multiple=Create Parent Items
pane.items.menu.renameAttachments=Преименува файла въз основа на родителските метадани
pane.items.menu.renameAttachments.multiple=Преименува файловете въз основа на родителските метадани
pane.items.menu.duplicateAndConvert.toBookSection=Create Book Section
pane.items.menu.duplicateAndConvert.toBook=Create Book from Book Section
pane.items.showItemInLibrary=Show Item in Library
pane.items.letter.oneParticipant=Писмо до %S
@ -408,6 +417,16 @@ pane.item.attachments.fileNotFound.text2.stored=It may have been moved or delete
pane.item.attachments.fileNotFound.text2.stored.notOnServer=It may have been moved or deleted outside of %1$S, or, if the file was added on another computer, it may not yet have been synced to %2$S.
pane.item.attachments.fileNotFound.text2.linked=It may have been moved or deleted outside of %1$S, or a Linked Attachment Base Directory may be set incorrectly on one of your computers.
pane.item.attachments.delete.confirm=Сигурни ли сте, че искате да изтриете това приложение?
pane.item.attachments.autoRelink.title=File Located Automatically
pane.item.attachments.autoRelink.text1=The file could not be found at the specified location, but a file with the same name was found within your Linked Attachment Base Directory:
pane.item.attachments.autoRelink.text2=Old Location: %S
pane.item.attachments.autoRelink.text3=New Location: %S
pane.item.attachments.autoRelink.text4=%1$S can automatically relink this attachment.
pane.item.attachments.autoRelink.relink=Relink
pane.item.attachments.autoRelinkOthers.title=Additional Files Located
pane.item.attachments.autoRelinkOthers.text=One other unlinked attachment in this library was found within the same directory. Relink this attachment as well?;%S other unlinked attachments in this library were found within the same directory. Relink all located attachments?
pane.item.attachments.autoRelink.locateManually=Locate Manually…
pane.item.attachments.autoRelink.relinkAll=Relink All
pane.item.attachments.count.zero=%S приложения:
pane.item.attachments.count.singular=%S приложение:
pane.item.attachments.count.plural=%S приложения:
@ -438,7 +457,7 @@ pane.context.allNotes=All Notes
pane.context.noNotes=No notes
itemTypes.note=Бележка
itemTypes.annotation=Annotation
itemTypes.annotation=Анотация
itemTypes.attachment=Приложение
itemTypes.book=Книга
itemTypes.bookSection=Глава от книга
@ -719,6 +738,7 @@ zotero.preferences.export.quickCopy.citationStyles=Citation Styles
zotero.preferences.export.quickCopy.exportFormats=Формати за износ
zotero.preferences.export.quickCopy.instructions=Quick Copy allows you to quickly export items in a given format. You can copy selected items to the clipboard by pressing %S or drag items directly into a text box in another program.
zotero.preferences.export.quickCopy.citationInstructions=For citation styles, you can copy citations or footnotes by pressing %S or holding down Shift before dragging items.
zotero.preferences.export.quickCopy.note.htmlOptions.label=Rich Text/HTML
zotero.preferences.wordProcessors.installationSuccess=Installation was successful.
zotero.preferences.wordProcessors.installationError=Installation could not be completed because an error occurred. Please ensure that %1$S is closed, and then restart %2$S.
@ -864,22 +884,7 @@ citation.showEditor=Показва редактора...
citation.hideEditor=Скрива редактора...
citation.citations=Citations
citation.notes=Notes
citation.locator.page=Page
citation.locator.book=Book
citation.locator.chapter=Chapter
citation.locator.column=Column
citation.locator.figure=Figure
citation.locator.folio=Folio
citation.locator.issue=Issue
citation.locator.line=Line
citation.locator.note=Note
citation.locator.opus=Opus
citation.locator.paragraph=Paragraph
citation.locator.part=Part
citation.locator.section=Section
citation.locator.subverbo=Sub verbo
citation.locator.volume=Volume
citation.locator.verse=Verse
citation.locator.timestamp=Timestamp
report.title.default=Зотеро отчет
report.parentItem=Родителски запис:
@ -940,7 +945,7 @@ integration.error.misconfiguredWordStartupFolder.some=%S could not install the W
integration.error.misconfiguredWordStartupFolder.all=%S could not install the Word plugin because the Word Startup folder location is misconfigured.
integration.error.misconfiguredWordStartupFolder.fix=The plugin may not appear in Word until you reset your Startup folder to the default location.
integration.error.macWordSBPermissionsMissing.title=Missing Permission
integration.error.macWordSBPermissionsMissing=Zotero does not have permission to control Word. To grant this permission:\n\n1) Open System Preferences\n2) Click on “Security & Privacy”\n3) Select the “Privacy” tab\n4) Find and select “Automation” on the left\n5) Check the checkbox for “Microsoft Word” under “Zotero”\n6) Restart Word
integration.error.macWordSBPermissionsMissing=Zotero does not have permission to control Word. To grant this permission:\n\n1) Open System Settings\n2) Select “Privacy & Security” in the left column\n3) Select “Automation”\n4) Find “Zotero” and click the arrow to expand it\n5) Make sure “Microsoft Word” is enabled under “Zotero”\n6) Restart Word
integration.error.macWordSBPermissionsMissing.pre2016=If “Microsoft Word” does not appear under “Automation”, make sure you are running Word 2011 version 14.7.7 or later.
integration.error.m1UpgradeOS.title=macOS Upgrade Required
integration.error.m1UpgradeOS=Citing in Word on an Apple Silicon Mac with macOS 11.3 or earlier is not supported and may cause %S to freeze. Please upgrade to macOS 11.4 or later.
@ -1005,7 +1010,7 @@ sync.skipGroup=Skip Group
sync.removeGroupsAndSync=Отстранява групите и синхронизация
sync.reminder.setUp.message=Back up your library with %S syncing.
sync.reminder.setUp.action=Set Up Syncing
sync.reminder.setUp.action=Настройване на синхронизирането
sync.reminder.autoSync.message=%S hasn’t synced in a while. Do you want to enable automatic syncing?
sync.error.usernameNotSet=Потребителското име не е въведено
@ -1176,10 +1181,10 @@ recognizePDF.couldNotRead=Текста на този PDF файл не може
recognizePDF.noMatches=No matching references found
recognizePDF.fileNotFound=File not found
recognizePDF.error=An unexpected error occurred
recognizePDF.recognizing.label=Retrieving Metadata…
recognizePDF.recognizing.label=Извлича на метаданите...
recognizePDF.complete.label=Metadata Retrieval Complete
recognizePDF.pdfName.label=PDF Name
recognizePDF.itemName.label=Item Name
recognizePDF.pdfName.label=Име на PDF
recognizePDF.itemName.label=Име на запис
rtfScan.openTitle=Изберете файл, който да бъде сканиран
rtfScan.scanning.label=Сканира RTF документ...
@ -1218,6 +1223,8 @@ locate.externalViewer.label=Open in External Viewer
locate.internalViewer.label=Open in Internal Viewer
locate.showFile.label=Show File
locate.libraryLookup.label=Library Lookup
locate.libraryLookup.noResolver.title=No OpenURL Resolver
locate.libraryLookup.noResolver.text=You must choose an OpenURL resolver from the Advanced pane of the %S preferences.
locate.manageLocateEngines=Manage Lookup Engines...
locate.locateEngineDescription=Lookup engines help you find resources in your %S library on the web. Engines enabled in this list appear in the Locate drop-down in the toolbar.
@ -1308,15 +1315,16 @@ noteEditor.monospaced=Monospaced
noteEditor.bulletList=Bulleted List
noteEditor.orderedList=Numbered List
noteEditor.blockquote=Block Quote
noteEditor.mathBlock=Math Block
noteEditor.formatText=Format Text
noteEditor.highlightText=Highlight Text
noteEditor.removeColor=Remove Color
noteEditor.bold=Bold
noteEditor.italic=Italic
noteEditor.underline=Underline
noteEditor.highlightText=Акцентиране на текст
noteEditor.removeColor=Премахване на цвят
noteEditor.bold=Удебелен
noteEditor.italic=Наклонен
noteEditor.underline=Подчертан
noteEditor.strikethrough=Strikethrough
noteEditor.subscript=Subscript
noteEditor.superscript=Superscript
noteEditor.subscript=Субскрипт / Долни букви
noteEditor.superscript=Суперскрипт / Горни букви
noteEditor.returnToNotesList=Return to Notes List
noteEditor.insertLink=Insert Link
noteEditor.clearFormatting=Clear Formatting
@ -1325,7 +1333,7 @@ noteEditor.alignLeft=Align Left
noteEditor.alignCenter=Align Center
noteEditor.alignRight=Align Right
noteEditor.insertCitation=Insert Citation
noteEditor.more=More
noteEditor.more=Още
noteEditor.find=Find
noteEditor.replace=Replace
noteEditor.previous=Previous
@ -1338,7 +1346,7 @@ noteEditor.editCitation=Edit Citation
noteEditor.showOnPage=Show on Page
noteEditor.unlink=Unlink
noteEditor.set=Set
noteEditor.edit=Edit
noteEditor.edit=Редактиране
noteEditor.addCitation=Add Citation
noteEditor.removeCitation=Hide Citation
noteEditor.findAndReplace=Find and Replace
@ -1347,7 +1355,11 @@ noteEditor.applyAnnotationColors=Show Annotation Colors
noteEditor.removeAnnotationColors=Hide Annotation Colors
noteEditor.addCitations=Show Annotation Citations
noteEditor.removeCitations=Hide Annotation Citations
noteEditor.insertTable=Insert Table
noteEditor.image=Образ
noteEditor.math=Math
noteEditor.table=Table
noteEditor.copyImage=Copy Image
noteEditor.saveImageAs=Save Image As…
noteEditor.insertRowBefore=Insert Row Above
noteEditor.insertRowAfter=Insert Row Below
noteEditor.insertColumnBefore=Insert Column Left
@ -1360,11 +1372,11 @@ pdfReader.annotations=Annotations
pdfReader.showAnnotations=Show Annotations
pdfReader.searchAnnotations=Search Annotations
pdfReader.noAnnotations=Create an annotation to see it in the sidebar
pdfReader.addText=Add extracted text
pdfReader.noExtractedText=No extracted text
pdfReader.addComment=Add comment
pdfReader.addTags=Add tags…
pdfReader.highlightText=Highlight Text
pdfReader.addNote=Add Note
pdfReader.highlightText=Акцентиране на текст
pdfReader.addNote=Добавяне на бележка
pdfReader.selectArea=Select Area
pdfReader.pickColor=Pick a Color
pdfReader.addToNote=Add to Note
@ -1373,6 +1385,8 @@ pdfReader.zoomOut=Zoom Out
pdfReader.zoomAuto=Automatically Resize
pdfReader.zoomPageWidth=Zoom to Page Width
pdfReader.zoomPageHeight=Zoom to Page Height
pdfReader.splitVertically=Split Vertically
pdfReader.splitHorizontally=Split Horizontally
pdfReader.nextPage=Next Page
pdfReader.previousPage=Previous Page
pdfReader.page=Page
@ -1382,8 +1396,15 @@ pdfReader.promptTransferFromPDF.text=Annotations stored in the PDF file will be
pdfReader.promptTransferToPDF.title=Store Annotations in File
pdfReader.promptTransferToPDF.text=Annotations will be transferred to the PDF file and will no longer be editable in %S.
pdfReader.promptPasswordProtected=The operation is not supported for password-protected PDF files.
pdfReader.promptDeletePages.title=Delete Pages
pdfReader.promptDeletePages.text=Are you sure you want to delete %1$S page from the PDF file?;Are you sure you want to delete %1$S pages from the PDF file?
pdfReader.rotateLeft=Rotate Left
pdfReader.rotateRight=Rotate Right
pdfReader.rotate180=Rotate 180°
pdfReader.editPageNumber=Edit Page Number…
pdfReader.editHighlightedText=Edit Highlighted Text
pdfReader.copyImage=Copy Image
pdfReader.saveImageAs=Save Image As…
pdfReader.pageNumberPopupHeader=Change page number for:
pdfReader.thisAnnotation=This annotation
pdfReader.selectedAnnotations=Selected annotations

View file

@ -6,7 +6,7 @@
<!ENTITY zotero.developers "Diorroerien:">
<!ENTITY zotero.alumni "Kendaolerien gozh:">
<!ENTITY zotero.about.localizations "Lec'heladurioù:">
<!ENTITY zotero.about.additionalSoftware "Third-Party Software:">
<!ENTITY zotero.about.additionalSoftware "Meziantoù tredeel:">
<!ENTITY zotero.thanks "Trugarez vras da:">
<!ENTITY zotero.about.close "Serriñ">
<!ENTITY zotero.moreCreditsAndAcknowledgements "Muioc'h a greditoù &amp; trugarekadurioù">

View file

@ -2,6 +2,12 @@
"general_more": {
"message": "Muioc'h..."
},
"general_moreInfo": {
"message": "Muioc'h a ditouroù..."
},
"general_needHelp": {
"message": "Ezhomm ho peus sikour?"
},
"general_done": {
"message": "Graet"
},
@ -23,6 +29,9 @@
"general_pleaseWait": {
"message": "Gortozit mar plij..."
},
"general_copyToClipboard": {
"message": "Eilañ er golver"
},
"progressWindow_savingTo": {
"message": "Enrollañ e-barzh"
},
@ -32,6 +41,9 @@
"progressWindow_error_translation": {
"message": "Ur fazi a zo c'hoarvezet en ur enrollañ an elfenn-mañ. Sellit ouzh $1 evit kaout muioc'h a ditouroù."
},
"progressWindow_error_siteAccessLimitsError": {
"message": "Ur fazi a zo c'hoarvezet en ur enrollañ an elfenn-mañ. $1 a c'hall bevenniñ an niver a elfennoù a c'hellit enrollañ war ar memes tro. Sellit ouzh $2 evit kaout muioc'h a ditouroù."
},
"progressWindow_error_troubleshootingTranslatorIssues": {
"message": "Distankañ ar c'hudennoù troidigezh"
},

View file

@ -6,7 +6,7 @@
<!ENTITY zotero.general.deselectAll "Diziuzañ pep-tra">
<!ENTITY zotero.general.edit "Aozañ">
<!ENTITY zotero.general.delete "Dilemel">
<!ENTITY zotero.general.moveToTrash "Move to Trash">
<!ENTITY zotero.general.moveToTrash "Kas d'ar pod-lastez">
<!ENTITY zotero.general.ok "Mat eo">
<!ENTITY zotero.general.cancel "Nullañ">
<!ENTITY zotero.general.refresh "Freskaat">
@ -45,7 +45,7 @@
<!ENTITY zotero.upgrade.changeLogLink "renabl ar cheñchamantoù">
<!ENTITY zotero.upgrade.changeLogAfterLink "evit dizoloiñ ar pezh a zo nevez.">
<!ENTITY zotero.tabs.info.label "Titou">
<!ENTITY zotero.tabs.info.label "Titou.">
<!ENTITY zotero.tabs.notes.label "Notennoù">
<!ENTITY zotero.tabs.attachments.label "Pezhioù-stag">
<!ENTITY zotero.tabs.tags.label "Balizoù">
@ -129,7 +129,7 @@
<!ENTITY zotero.toolbar.attachment.weblink "Enrollañ al liamm d'ar bajenn a-vremañ">
<!ENTITY zotero.toolbar.attachment.snapshot "Kemer un dapadenn-skramm eus ar bajenn a-vremañ">
<!ENTITY zotero.toolbar.context.item "Elfenn">
<!ENTITY zotero.toolbar.context.notes "Notes">
<!ENTITY zotero.toolbar.context.notes "Notennoù">
<!ENTITY zotero.tagSelector.noTagsToDisplay "Baliz ebet da ziskouez">
<!ENTITY zotero.tagSelector.loadingTags "O kargañ ar balizoù...">
@ -147,7 +147,7 @@
<!ENTITY zotero.tagColorChooser.setColor "Termeniñ al liv">
<!ENTITY zotero.tagColorChooser.removeColor "Dilemel al liv">
<!ENTITY zotero.lookup.description "Enter ISBNs, DOIs, PMIDs, arXiv IDs, or ADS Bibcodes to add to your library:">
<!ENTITY zotero.lookup.description "Ebarzhiñ anaouadurioù ISBNoù, DOIoù, PMIDs, arXiv pe c'hoazh bibcodes ADS evit gellout ouzhpennañ d'ho levraoueg:">
<!ENTITY zotero.lookup.button.search "Klask">
<!ENTITY zotero.createParent.title "Krouiñ un elfenn kar">
@ -196,7 +196,7 @@
<!ENTITY zotero.citation.keepSorted.label "Mirout ar mammennoù rummet">
<!ENTITY zotero.citation.suppressAuthor.label "Omit Author">
<!ENTITY zotero.citation.suppressAuthor.label "Lezel a-gostez an Aozer">
<!ENTITY zotero.citation.prefix.label "Rakger:">
<!ENTITY zotero.citation.suffix.label "Lostger:">
<!ENTITY zotero.citation.editorWarning.label "Kemenn-diwall: Ma aozit un arroudenn en aozer, ne vo ket gouest ken da velezouriñ ar cheñchadurioù en ho tiaz-roadennoù pe ar stil arroudenniñ.">
@ -307,12 +307,10 @@
<!ENTITY zotero.context.addStandaloneNoteFromAnnotations "Add Standalone Note from Annotations">
<!ENTITY zotero.context.editInWindow "Edit in a Separate Window">
<!ENTITY zotero.pdfReader.firstPage "First Page">
<!ENTITY zotero.pdfReader.firstPage "Pajenn gentañ">
<!ENTITY zotero.pdfReader.lastPage "Last Page">
<!ENTITY zotero.pdfReader.back "Back">
<!ENTITY zotero.pdfReader.back "Distreiñ">
<!ENTITY zotero.pdfReader.forward "Forward">
<!ENTITY zotero.pdfReader.rotateClockwise "Rotate Clockwise">
<!ENTITY zotero.pdfReader.rotateCounterclockwise "Rotate Counterclockwise">
<!ENTITY zotero.pdfReader.handTool "Hand Tool">
<!ENTITY zotero.pdfReader.verticalScrolling "Vertical Scrolling">
<!ENTITY zotero.pdfReader.horizontalScrolling "Horizontal Scrolling">
@ -320,9 +318,14 @@
<!ENTITY zotero.pdfReader.noSpreads "No Spreads">
<!ENTITY zotero.pdfReader.oddSpreads "Odd Spreads">
<!ENTITY zotero.pdfReader.evenSpreads "Even Spreads">
<!ENTITY zotero.pdfReader.zoomIn "Zoom In">
<!ENTITY zotero.pdfReader.zoomOut "Zoom Out">
<!ENTITY zotero.pdfReader.zoomIn "Zoumañ">
<!ENTITY zotero.pdfReader.zoomOut "Dizoumañ">
<!ENTITY zotero.pdfReader.zoomAuto "Automatically Resize">
<!ENTITY zotero.pdfReader.zoomPageWidth "Zoom to Page Width">
<!ENTITY zotero.pdfReader.zoomPageHeight "Zoom to Page Height">
<!ENTITY zotero.pdfReader.splitVertically "Split Vertically">
<!ENTITY zotero.pdfReader.splitHorizontally "Split Horizontally">
<!ENTITY zotero.pdfReader.transferFromPDF "Import Annotations…">
<!ENTITY zotero.pdfReader.rotatePageLeft "Rotate Page Left">
<!ENTITY zotero.pdfReader.rotatePageRight "Rotate Page Right">
<!ENTITY zotero.pdfReader.rotatePage180 "Rotate Page 180°">

View file

@ -71,9 +71,9 @@ general.fix=Kempenn...
general.tryAgain=Klask en-dro
general.tryLater=Klask diwezhatoc'h
general.showDirectory=Diskouez ar c'havlec'h
general.showInLibrary=Show in Library
general.showInLibrary=Diskouez el levraoueg:
general.continue=Kenderc'hel
general.copy=Copy
general.copy=Eilañ
general.copyToClipboard=Eilañ er golver
general.cancel=Nullañ
general.clear=Diverkañ
@ -88,10 +88,11 @@ general.pdf=PDF
general.back=Distreiñ
general.languages=Yezhoù
general.default=Default
general.custom=Custom
general.custom=Personelaet
general.loading=O kargañ...
general.richText=Rich Text
general.clearSelection=Clear Selection
general.insert=Insert
general.yellow=Melen
general.red=Ruz
@ -114,6 +115,7 @@ about.getInvolved=C'hoant ho peus reiñ an dorn? [Kemerit-perzh] bremañ!
punctuation.openingQMark=“
punctuation.closingQMark=”
punctuation.colon=:
punctuation.colon.withString=%S:
punctuation.ellipsis=…
install.quickStartGuide=Heñcher dillo evit stagañ e-barzh
@ -327,8 +329,11 @@ pane.items.remove.multiple=Ha sur oc'h ho peus c'hoant da zilemel an elfennoù d
pane.items.removeFromPublications.title=Tennañ diwar Ma Embannadennoù
pane.items.removeFromPublications=Ha sur ho peus c'hoant da zilemel an elfenn diuzet diwar Ma Embannadennoù?
pane.items.removeFromPublications.multiple=Ha sur ho peus c'hoant da zilemel an elfennoù diuzet diwar Ma Embannadennoù?
pane.items.removeRecursive.title=Remove from Collection and Subcollections
pane.items.removeRecursive=Are you sure you want to remove the selected item from this collection and all subcollections?
pane.items.removeRecursive.multiple=Are you sure you want to remove the selected items from this collection and all subcollections?
pane.items.menu.addNoteFromAnnotations=Add Note from Annotations
pane.items.menu.addNoteFromAnnotations.multiple=Add Notes from Annotations
pane.items.menu.createNoteFromAnnotations=Create Note from Annotations
pane.items.menu.findAvailablePDF=Kavout ur PDF hegerz
pane.items.menu.findAvailablePDF.multiple=Kavout PDFoù hegerz
pane.items.menu.addToCollection=Add to Collection
@ -338,11 +343,13 @@ pane.items.menu.removeFromPublications=Tennañ an elfenn diwar Ma Embannadennoù
pane.items.menu.removeFromPublications.multiple=Tennañ an elfennoù diwar Ma Embannadennoù...
pane.items.menu.moveToTrash=Kas an elfenn d'ar pod-lastez...
pane.items.menu.moveToTrash.multiple=Kas an elfennoù d'ar pod-lastez...
pane.items.menu.delete=Delete Permanently…
pane.items.menu.delete=Dilemel da virviken...
pane.items.menu.export=Ezporzhiañ an elfenn...
pane.items.menu.export.multiple=Ezporzhiañ an elfennoù...
pane.items.menu.exportNote=Export Note…
pane.items.menu.exportNote.multiple=Export Notes…
pane.items.menu.exportPDF=Export PDF…
pane.items.menu.exportPDF.multiple=Export PDFs…
pane.items.menu.createBib=Krouiñ ul levrlennadur diwar an elfenn...
pane.items.menu.createBib.multiple=Krouiñ ul levrlennadur diwar an elfennoù...
pane.items.menu.generateReport=Genel un danevell diwar an elfenn...
@ -355,6 +362,8 @@ pane.items.menu.createParent=Krouiñ un elfenn kar...
pane.items.menu.createParent.multiple=Krouiñ elfennoù kar
pane.items.menu.renameAttachments=Adenvel ar restr diwar ar meta-roadenn kar
pane.items.menu.renameAttachments.multiple=Adenvel ar restroù diwar ar meta-roadenn kar
pane.items.menu.duplicateAndConvert.toBookSection=Create Book Section
pane.items.menu.duplicateAndConvert.toBook=Create Book from Book Section
pane.items.showItemInLibrary=Diskouez an elfenn el levraoueg
pane.items.letter.oneParticipant=Lizher da %S
@ -408,6 +417,16 @@ pane.item.attachments.fileNotFound.text2.stored=Dleout a ra bezañ fiñvet pe di
pane.item.attachments.fileNotFound.text2.stored.notOnServer=Dleout a ra bezañ fiñvet pe dilamet en diavaez eus %1$S, pe, m'eo bet ouzhpennet war un urzhiataer all, posupl eo ne vefe ket bet sinkroet da %2$S.
pane.item.attachments.fileNotFound.text2.linked=Dleout a ra bezañ fiñvet pe dilamet er maez eus %1$S, pe ur Stagadenn Kavlec'h Diazez Liammet dleout a ra bezañ diazezet en un doare fall e-diabarzh unan hoc'h urzhiataer.
pane.item.attachments.delete.confirm=Ha fellout a ra deoc'h dilemel da vat ar pezh-stag-mañ?
pane.item.attachments.autoRelink.title=File Located Automatically
pane.item.attachments.autoRelink.text1=The file could not be found at the specified location, but a file with the same name was found within your Linked Attachment Base Directory:
pane.item.attachments.autoRelink.text2=Old Location: %S
pane.item.attachments.autoRelink.text3=New Location: %S
pane.item.attachments.autoRelink.text4=%1$S can automatically relink this attachment.
pane.item.attachments.autoRelink.relink=Relink
pane.item.attachments.autoRelinkOthers.title=Additional Files Located
pane.item.attachments.autoRelinkOthers.text=One other unlinked attachment in this library was found within the same directory. Relink this attachment as well?;%S other unlinked attachments in this library were found within the same directory. Relink all located attachments?
pane.item.attachments.autoRelink.locateManually=Locate Manually…
pane.item.attachments.autoRelink.relinkAll=Relink All
pane.item.attachments.count.zero=%S a bezhioù-stag:
pane.item.attachments.count.singular=%S pezh-stag:
pane.item.attachments.count.plural=%S a bezhioù-stag:
@ -430,7 +449,7 @@ pane.item.related.count.zero=%S kevreet:
pane.item.related.count.singular=%S kevreet:
pane.item.related.count.plural=%S kevreet:
pane.item.parentItem=Elfenn kar:
pane.item.viewOnline.tooltip=Go to this item online
pane.item.viewOnline.tooltip=Mont d'an elfenn-mañ enlinenn
pane.context.noParent=Elfenn kar ebet
pane.context.itemNotes=Item Notes
@ -550,7 +569,7 @@ itemFields.audioFileType=Doare restr
itemFields.versionNumber=Stumm
itemFields.system=Sistem
itemFields.company=Kumpaniezh
itemFields.conferenceName=Anv prezegenn
itemFields.conferenceName=Anv koñferañs
itemFields.encyclopediaTitle=Titl hollouiziegezh
itemFields.dictionaryTitle=Titl ar geriadur
itemFields.language=Yezh
@ -604,7 +623,7 @@ creatorTypes.scriptwriter=Skriver-skriptoù
creatorTypes.producer=Produer
creatorTypes.castMember=Ezel ar c'homedianeta
creatorTypes.sponsor=Sponsor
creatorTypes.counsel=Kuzul
creatorTypes.counsel=Kuzulier
creatorTypes.inventor=Ijiner
creatorTypes.attorneyAgent=Dileuriad/Ajant
creatorTypes.recipient=Resever
@ -619,7 +638,7 @@ creatorTypes.presenter=Kinniger
creatorTypes.guest=Den pedet
creatorTypes.podcaster=Podskigner
creatorTypes.reviewedAuthor=Aozer niverennet
creatorTypes.cosponsor=Ken-tad-paeron
creatorTypes.cosponsor=Ken-sponsor
creatorTypes.bookAuthor=Aozer al levr
fileTypes.webpage=Pajenn web
@ -719,6 +738,7 @@ zotero.preferences.export.quickCopy.citationStyles=Doare arroudenn
zotero.preferences.export.quickCopy.exportFormats=Ezporzhiañ ar stummoù
zotero.preferences.export.quickCopy.instructions=An Eilañ prim a ro an tu deoc'h da ezporzhiañ buan-tre an elfennoù en ur stumm roet. Gellout a rit eilañ an elfennoù diuzet er golver en ur bouezañ war %S pe rinklit an elfennoù war-eeun e-barzh ur voest testenn e-barzh ur program all.
zotero.preferences.export.quickCopy.citationInstructions=Evit ar stiloù arroudenn e c'hallit eilañ an arroudennoù pe an notennoù-traoñ pajenn en ur bouezañ war %S pe en ur zerc'hel war Linkad a-raok rinklañ-stlejañ elfennoù.
zotero.preferences.export.quickCopy.note.htmlOptions.label=Rich Text/HTML
zotero.preferences.wordProcessors.installationSuccess=Berzh en deus graet ar staliadur.
zotero.preferences.wordProcessors.installationError=N'hall ket bezañ klokaet ar staliadur rak ur fazi a zo c'hoarvezet. Bezit asur eo sarr %1$S, hag adloc'hit goude-se %2$S.
@ -864,22 +884,7 @@ citation.showEditor=Diskouez an aozer...
citation.hideEditor=Kuzhat an aozer...
citation.citations=Arroudennoù
citation.notes=Notennoù
citation.locator.page=Pajenn
citation.locator.book=Levr
citation.locator.chapter=Chabistr
citation.locator.column=Bann
citation.locator.figure=Lun
citation.locator.folio=Folio
citation.locator.issue=Niverenn
citation.locator.line=Linenn
citation.locator.note=Notenn
citation.locator.opus=Opus
citation.locator.paragraph=Rannbennad
citation.locator.part=Lodenn
citation.locator.section=Kevrenn
citation.locator.subverbo=Sub verbo
citation.locator.volume=Levrenn
citation.locator.verse=Gwerzad
citation.locator.timestamp=Timestamp
report.title.default=Danevell Zotero
report.parentItem=Elfenn kar:
@ -1005,7 +1010,7 @@ sync.skipGroup=Tremen ar strollad
sync.removeGroupsAndSync=Tennañ ar strolladoù ha sinkro
sync.reminder.setUp.message=Back up your library with %S syncing.
sync.reminder.setUp.action=Set Up Syncing
sync.reminder.setUp.action=Termeniñ ar sinkro
sync.reminder.autoSync.message=%S hasn’t synced in a while. Do you want to enable automatic syncing?
sync.error.usernameNotSet=Anv-implijer didermenet:
@ -1210,7 +1215,7 @@ lookup.failureTooMany.description=Re a anaouadurioù. Ebarzhit un anaouader mar
createParent.prompt=Enter a DOI, ISBN, PMID, arXiv ID, or ADS Bibcode to identify this file
locate.online.label=Diskouez enlinenn
locate.pdf.label=Open PDF
locate.pdf.label=Digeriñ ar PDF
locate.pdfNewWindow.label=Open PDF in New Window
locate.snapshot.label=Diskouez an dapadenn
locate.file.label=Diskouez ar restr
@ -1218,6 +1223,8 @@ locate.externalViewer.label=Digeriñ gant ul lenner diavaez
locate.internalViewer.label=Digeriñ e-barzh ul lenner diabarzh
locate.showFile.label=Diskouez ar restr
locate.libraryLookup.label=Klask el levraoueg
locate.libraryLookup.noResolver.title=No OpenURL Resolver
locate.libraryLookup.noResolver.text=You must choose an OpenURL resolver from the Advanced pane of the %S preferences.
locate.manageLocateEngines=Merañ al luskerioù enklask...
locate.locateEngineDescription=Lookup engines help you find resources in your %S library on the web. Engines enabled in this list appear in the Locate drop-down in the toolbar.
@ -1308,15 +1315,16 @@ noteEditor.monospaced=Monospaced
noteEditor.bulletList=Bulleted List
noteEditor.orderedList=Numbered List
noteEditor.blockquote=Block Quote
noteEditor.mathBlock=Math Block
noteEditor.formatText=Format Text
noteEditor.highlightText=Highlight Text
noteEditor.removeColor=Remove Color
noteEditor.bold=Bold
noteEditor.italic=Italic
noteEditor.highlightText=Usskediñ an destenn
noteEditor.removeColor=Dilemel al liv
noteEditor.bold=Druz
noteEditor.italic=Stouet
noteEditor.underline=Islinennet
noteEditor.strikethrough=Strikethrough
noteEditor.subscript=Subscript
noteEditor.superscript=Superscript
noteEditor.subscript=Isskrid
noteEditor.superscript=Usskrid
noteEditor.returnToNotesList=Return to Notes List
noteEditor.insertLink=Insert Link
noteEditor.clearFormatting=Clear Formatting
@ -1327,7 +1335,7 @@ noteEditor.alignRight=Align Right
noteEditor.insertCitation=Insert Citation
noteEditor.more=Muioc'h
noteEditor.find=Find
noteEditor.replace=Replace
noteEditor.replace=Erlec'hiañ
noteEditor.previous=Previous
noteEditor.next=Da-heul
noteEditor.replaceNext=Erlec'hiañ
@ -1347,7 +1355,11 @@ noteEditor.applyAnnotationColors=Show Annotation Colors
noteEditor.removeAnnotationColors=Hide Annotation Colors
noteEditor.addCitations=Show Annotation Citations
noteEditor.removeCitations=Hide Annotation Citations
noteEditor.insertTable=Insert Table
noteEditor.image=Skeudenn
noteEditor.math=Math
noteEditor.table=Table
noteEditor.copyImage=Copy Image
noteEditor.saveImageAs=Save Image As…
noteEditor.insertRowBefore=Insert Row Above
noteEditor.insertRowAfter=Insert Row Below
noteEditor.insertColumnBefore=Insert Column Left
@ -1360,10 +1372,10 @@ pdfReader.annotations=Ennotadurioù
pdfReader.showAnnotations=Show Annotations
pdfReader.searchAnnotations=Search Annotations
pdfReader.noAnnotations=Create an annotation to see it in the sidebar
pdfReader.addText=Add extracted text
pdfReader.noExtractedText=No extracted text
pdfReader.addComment=Add comment
pdfReader.addTags=Add tags…
pdfReader.highlightText=Highlight Text
pdfReader.highlightText=Usskediñ an destenn
pdfReader.addNote=Ouzhpennañ un notenn
pdfReader.selectArea=Select Area
pdfReader.pickColor=Pick a Color
@ -1373,17 +1385,26 @@ pdfReader.zoomOut=Dizoumañ
pdfReader.zoomAuto=Automatically Resize
pdfReader.zoomPageWidth=Zoom to Page Width
pdfReader.zoomPageHeight=Zoom to Page Height
pdfReader.splitVertically=Split Vertically
pdfReader.splitHorizontally=Split Horizontally
pdfReader.nextPage=Pajenn da-heul
pdfReader.previousPage=Previous Page
pdfReader.page=Page
pdfReader.page=Pajenn
pdfReader.readOnly=Read-only
pdfReader.promptTransferFromPDF.title=Import Annotations
pdfReader.promptTransferFromPDF.text=Annotations stored in the PDF file will be moved to %1$S.
pdfReader.promptTransferToPDF.title=Store Annotations in File
pdfReader.promptTransferToPDF.text=Annotations will be transferred to the PDF file and will no longer be editable in %S.
pdfReader.promptPasswordProtected=The operation is not supported for password-protected PDF files.
pdfReader.promptDeletePages.title=Delete Pages
pdfReader.promptDeletePages.text=Are you sure you want to delete %1$S page from the PDF file?;Are you sure you want to delete %1$S pages from the PDF file?
pdfReader.rotateLeft=Rotate Left
pdfReader.rotateRight=Rotate Right
pdfReader.rotate180=Rotate 180°
pdfReader.editPageNumber=Edit Page Number…
pdfReader.editHighlightedText=Edit Highlighted Text
pdfReader.copyImage=Copy Image
pdfReader.saveImageAs=Save Image As…
pdfReader.pageNumberPopupHeader=Change page number for:
pdfReader.thisAnnotation=This annotation
pdfReader.selectedAnnotations=Selected annotations

View file

@ -5,9 +5,18 @@
"general_moreInfo": {
"message": "Més informació…"
},
"general_needHelp": {
"message": "Us cal ajuda?"
},
"general_done": {
"message": "Fet"
},
"general_yes": {
"message": "Sí"
},
"general_no": {
"message": "No"
},
"general_tryAgain": {
"message": "Torna-ho a provar"
},
@ -32,9 +41,15 @@
"progressWindow_error_translation": {
"message": "S'ha produït un error desar l'element. Vegeu $1 per a més informació. "
},
"progressWindow_error_siteAccessLimitsError": {
"message": "S'ha produït un error en desar l'element. $1 pot limitar el nombre d'elements que podeu desar cada vegada. Vegeu $2 per a més informació."
},
"progressWindow_error_troubleshootingTranslatorIssues": {
"message": "Resolució d'incidències dels tradutors"
},
"progressWindow_error_siteAccessLimits": {
"message": "Límits d'accés al lloc"
},
"progressWindow_error_fallback": {
"message": "S'ha produït un error desant $1. Es provarà de desar-ho fent servir $2."
},
@ -50,6 +65,18 @@
"upgradeApp": {
"message": "Actualitza $1"
},
"firstRun_title": {
"message": "Heu instal·lat el connector de $1!"
},
"firstRun_text1": {
"message": "El connector de $1 us permet <a href=\"$2\">desar elements al $1</a> des del vostre navegador amb un sol clic."
},
"firstRun_text2": {
"message": "Si no ho heu fet encara, <a href=\"$2\">baixeu</a> la versió d'escriptori del $1 per a gaudir d'una millor experiència."
},
"firstRun_acceptButton": {
"message": "Entesos"
},
"error_connection_isAppRunning": {
"message": "Està executant-se el $1?"
},
@ -92,6 +119,15 @@
"integration_googleDocs_docxAlert": {
"message": "La integració de $1 no és disponible quan s'editen fitxers .docx de Microsoft Word directament a Google Docs. Trieu Fitxer → Desa com a Google Docs per a habilitar aquesta funcionalitat. <br/><br/>Si voleu transferir un document de Word amb citacions de $1 a Google Docs, consulteu <a href=\"https://www.zotero.org/support/kb/moving_documents_between_word_processors\">Moure documents entre processadors de text</a>."
},
"integration_googleDocs_documentLocked": {
"message": "Les cites del document estan sent editades per un altre usuari del $1. Torneu-ho a provar més endavant."
},
"integration_googleDocs_documentLocked_moreInfo": {
"message": "El $1 bloqueja el vostre document per a evitar que diferents usuaris puguin editar-hi cites al mateix moment. L'edició concurrent de cites en un mateix document pot portar a la seva corrupció. Circumstàncies no esperades, com ara problemes de xarxa, poden provocar que el document quedi bloquejat de forma permanent. Si creieu que ha passat això, podeu sobreescriure el bloqueig. <br/><br/>Voleu sobreescriure el bloqueig del document?"
},
"integration_googleDocs_documentPermissionError": {
"message": "El compte de Google que heu seleccionat no té permisos per a editar el document. Torneu-ho a provar i assegureu-vos que trieu el compte amb accessos d'edició."
},
"integration_googleDocs_orphanedCitations_buttonTooltip": {
"message": "Mostra cites de $1 no enllaçades"
},

View file

@ -30,7 +30,7 @@
<!ENTITY zotero.preferences.groups.whenCopyingInclude "Quan copiïs elements entre les biblioteques, inclou-hi:">
<!ENTITY zotero.preferences.groups.childNotes "notes descendents">
<!ENTITY zotero.preferences.groups.childFiles "instantànies descendents i fitxers importats">
<!ENTITY zotero.preferences.groups.annotations "annotations">
<!ENTITY zotero.preferences.groups.annotations "anotacions">
<!ENTITY zotero.preferences.groups.childLinks "enllaços descendents">
<!ENTITY zotero.preferences.groups.tags "etiquetes">
@ -40,7 +40,7 @@
<!ENTITY zotero.preferences.feeds.sorting.oldest "Els elements més antics primer">
<!ENTITY zotero.preferences.feeds.feedDefaults "Paràmetres per defecte dels canals">
<!ENTITY zotero.preferences.openurl.choose "Choose a resolver…">
<!ENTITY zotero.preferences.openurl.choose "Trieu un resolutor:">
<!ENTITY zotero.preferences.openurl.custom "Personalitza…">
<!ENTITY zotero.preferences.openurl.server "Resolutor:">
<!ENTITY zotero.preferences.openurl.version "Versió:">
@ -96,7 +96,7 @@
<!ENTITY zotero.preferences.search.indexStats.words "Paraules:">
<!ENTITY zotero.preferences.fulltext.textMaxLength "Nombre màxim de caràcters indexats per fitxer:">
<!ENTITY zotero.preferences.fulltext.pdfMaxPages "Nombe màxim de pàgines indexades per fitxer:">
<!ENTITY zotero.preferences.fulltext.pdfMaxPages "Nombre màxim de pàgines indexades per fitxer:">
<!ENTITY zotero.preferences.prefpane.export "Exporta">
@ -105,8 +105,8 @@
<!ENTITY zotero.preferences.export.citePaperJournalArticleURL.description "Quan aquesta opció està deshabilitada, el Zotero inclou les URL quan se citen articles de premsa, revistes i revistes acadèmiques només si l'article no té un ran dge pàgines assignat.">
<!ENTITY zotero.preferences.quickCopy.caption "Còpia ràpida">
<!ENTITY zotero.preferences.quickCopy.itemFormat "Item Format:">
<!ENTITY zotero.preferences.quickCopy.noteFormat "Note Format:">
<!ENTITY zotero.preferences.quickCopy.itemFormat "Format d'element:">
<!ENTITY zotero.preferences.quickCopy.noteFormat "Format de nota:">
<!ENTITY zotero.preferences.quickCopy.copyAsHTML "Copia com a HTML">
<!ENTITY zotero.preferences.quickCopy.siteEditor.setings "Configuració específica per a llocs web:">
<!ENTITY zotero.preferences.quickCopy.siteEditor.domainPath "Domini/Ruta">

View file

@ -36,7 +36,7 @@
<!ENTITY copyCitationCmd.label "Copia la cita">
<!ENTITY copyBibliographyCmd.label "Copia la bibliografia">
<!ENTITY copyNoteCmd.label "Copy Note">
<!ENTITY copyNoteCmd.label "Copia la nota">
<!ENTITY bidiSwitchPageDirectionItem.label "Canvia la direcció de la pàgina">
<!ENTITY bidiSwitchPageDirectionItem.accesskey "g">

View file

@ -147,7 +147,7 @@
<!ENTITY zotero.tagColorChooser.setColor "Estableix el color">
<!ENTITY zotero.tagColorChooser.removeColor "Elimina el color">
<!ENTITY zotero.lookup.description "Enter ISBNs, DOIs, PMIDs, arXiv IDs, or ADS Bibcodes to add to your library:">
<!ENTITY zotero.lookup.description "Introduïu codis ISBN, DOI, PMID, arXiv ID o ADS Bibcodes per afegir a la vostra biblioteca:">
<!ENTITY zotero.lookup.button.search "Cerca">
<!ENTITY zotero.createParent.title "Crea un element pare">
@ -196,7 +196,7 @@
<!ENTITY zotero.citation.keepSorted.label "Mantingues les fonts ordenades">
<!ENTITY zotero.citation.suppressAuthor.label "Omit Author">
<!ENTITY zotero.citation.suppressAuthor.label "Omet l'autor">
<!ENTITY zotero.citation.prefix.label "Prefix:">
<!ENTITY zotero.citation.suffix.label "Sufix:">
<!ENTITY zotero.citation.editorWarning.label "Avís: Si editeu la cita a l'editor de cites no s'actualitzarà més i no reflectirà els canvis en la base de dades o de l'estil de cita.">
@ -311,18 +311,21 @@
<!ENTITY zotero.pdfReader.lastPage "Darrera pàgina">
<!ENTITY zotero.pdfReader.back "Enrere">
<!ENTITY zotero.pdfReader.forward "Endavant">
<!ENTITY zotero.pdfReader.rotateClockwise "Gira en les agulles del rellotge">
<!ENTITY zotero.pdfReader.rotateCounterclockwise "Gira al revés de les agulles del rellotge">
<!ENTITY zotero.pdfReader.handTool "Eina de mà">
<!ENTITY zotero.pdfReader.verticalScrolling "Desplaçament vertical">
<!ENTITY zotero.pdfReader.horizontalScrolling "Desplaçament horitzontal">
<!ENTITY zotero.pdfReader.wrappedScrolling "Wrapped Scrolling">
<!ENTITY zotero.pdfReader.noSpreads "No Spreads">
<!ENTITY zotero.pdfReader.oddSpreads "Odd Spreads">
<!ENTITY zotero.pdfReader.evenSpreads "Even Spreads">
<!ENTITY zotero.pdfReader.zoomIn "Zoom In">
<!ENTITY zotero.pdfReader.zoomOut "Zoom Out">
<!ENTITY zotero.pdfReader.zoomAuto "Automatically Resize">
<!ENTITY zotero.pdfReader.zoomPageWidth "Zoom to Page Width">
<!ENTITY zotero.pdfReader.zoomPageHeight "Zoom to Page Height">
<!ENTITY zotero.pdfReader.transferFromPDF "Import Annotations…">
<!ENTITY zotero.pdfReader.wrappedScrolling "Desplaçament ajustat">
<!ENTITY zotero.pdfReader.noSpreads "Cap disposició">
<!ENTITY zotero.pdfReader.oddSpreads "Disposició en senars">
<!ENTITY zotero.pdfReader.evenSpreads "Disposició en parells">
<!ENTITY zotero.pdfReader.zoomIn "Amplia">
<!ENTITY zotero.pdfReader.zoomOut "Redueix">
<!ENTITY zotero.pdfReader.zoomAuto "Redimensiona automàticament">
<!ENTITY zotero.pdfReader.zoomPageWidth "Ajusta a l'amplada de la pàgina">
<!ENTITY zotero.pdfReader.zoomPageHeight "Ajusta a l'alçada de la pàgina">
<!ENTITY zotero.pdfReader.splitVertically "Divideix verticalment">
<!ENTITY zotero.pdfReader.splitHorizontally "Divideix horitzontalment">
<!ENTITY zotero.pdfReader.transferFromPDF "Importa anotacions…">
<!ENTITY zotero.pdfReader.rotatePageLeft "Rotate Page Left">
<!ENTITY zotero.pdfReader.rotatePageRight "Rotate Page Right">
<!ENTITY zotero.pdfReader.rotatePage180 "Rotate Page 180°">

View file

@ -26,8 +26,8 @@ general.checkForUpdates=Comprova si hi ha actualitzacions
general.actionCannotBeUndone=Aquesta acció no es pot desfer.
general.install=Instal·la
general.updateAvailable=Hi ha una actualització disponible
general.downloading=Downloading %S…
general.downloading.quoted=Downloading “%S”…
general.downloading=S'està baixant %S…
general.downloading.quoted=S'està baixant «%S»…
general.noUpdatesFound=No s'han trobat actualitzacions
general.isUpToDate=%S està actualitzat.
general.upgrade=Actualitza
@ -47,12 +47,12 @@ general.delete=Suprimeix
general.remove=Elimina
general.import=Importa
general.export=Exporta
general.update=Update
general.update=Actualitza
general.moreInformation=Més informació
general.learnMore=Learn More
general.learnMore=Més informació
general.seeForMoreInformation=Mira %S per a més informació.
general.open=Obre %S
general.close=Close
general.close=Tanca
general.enable=Habilita
general.disable=Deshabilita
general.reset=Reinicia
@ -65,13 +65,13 @@ general.openPreferences=Obre les preferències
general.keys.ctrlShift=Ctrl+Shift+
general.keys.cmdShift=Cmd+Shift+
general.dontShowAgain=No ho tornis a mostrar
general.dontAskAgain=Don’t Ask Again
general.remindMeLater=Remind Me Later
general.dontAskAgain=No m'ho tornis a demanar
general.remindMeLater=Recorda-m'ho més tard
general.fix=Arregla…
general.tryAgain=Torna-ho a provar
general.tryLater=Prova-ho després
general.showDirectory=Mostra el directori
general.showInLibrary=Show in Library
general.showInLibrary=Mostra a la biblioteca
general.continue=Continua
general.copy=Copia
general.copyToClipboard=Copia al portaretalls
@ -86,18 +86,19 @@ general.nMegabytes=%S MB
general.item=Element
general.pdf=PDF
general.back=Enrere
general.languages=Languages
general.default=Default
general.custom=Custom
general.loading=Loading…
general.richText=Rich Text
general.clearSelection=Clear Selection
general.languages=Llengües
general.default=Per defecte
general.custom=Personalitzat
general.loading=S'està carregant…
general.richText=Text enriquit
general.clearSelection=Buida la selecció
general.insert=Insereix
general.yellow=Yellow
general.red=Red
general.green=Green
general.blue=Blue
general.purple=Purple
general.yellow=Groc
general.red=Vermell
general.green=Verd
general.blue=Blau
general.purple=Lila
general.operationInProgress=Una operació del Zotero està actualment en curs.
general.operationInProgress.waitUntilFinished=Espereu fins que hagi acabat.
@ -105,15 +106,16 @@ general.operationInProgress.waitUntilFinishedAndTryAgain=Si us plau, espereu fin
networkError.connectionNotSecure=El %S no ha pogut establir una connexió segura.
networkError.errorViaProxy=S'ha produït un error en connectar-se a través del servidor intermediari
networkError.connectionMonitored=Your connection is likely being monitored, and %S has not been configured to trust the intermediate server.
networkError.connectionMonitored=La vostra connexió possiblement està monitorada, i el %S no està configurat per a confiar en el servidor intermediari.
about.createdBy=El %1$S és un projecte de %2$S i està desenvolupat per una [comunitat global].
about.openSource=El %S és [programari de codi obert] i depèn de molts altres [projectes de codi obert excepcionals].
about.getInvolved=Voleu ajudar-hi? [Impliqueu-vos-hi] avui mateix!
punctuation.openingQMark=“
punctuation.closingQMark=”
punctuation.openingQMark=«
punctuation.closingQMark=»
punctuation.colon=:
punctuation.colon.withString=%S:
punctuation.ellipsis=…
install.quickStartGuide=Guia d'inici ràpid
@ -175,7 +177,7 @@ dataDir.selectDir=Selecciona el directori de dades per al Zotero
dataDir.selectNewDir=Seleccioneu un nou directori de dades del %S
dataDir.changeDataDirectory=Canvia el directori de dades…
dataDir.chooseNewDataDirectory=Trieu un directori de dades nou…
dataDir.cannotBeSetWithAlternative=The data directory cannot be set to a “%S” directory. Did you mean %S?
dataDir.cannotBeSetWithAlternative=El directori de dades no es pot definir en un directori del «%S». Volíeu dir %S?
dataDir.cannotBeLinkedAttachmentBaseDirectory=El directori de dades no pot definir-se en la mateixa ubicació que el directori base d'adjuncions enllaçades.
dataDir.unsafeLocation.selected.cloud=Emmagatzemar el directori de dades en una carpeta d'emmagatzematge al núvol molt probablement corromprà la vostra base de dades.
dataDir.unsafeLocation.selected.areYouSure=Esteu segur que voleu utilitzar aquesta ubicació?
@ -236,7 +238,7 @@ date.relative.daysAgo.multiple=Fa %S dies
date.relative.yearsAgo.one=Fa 1 any
date.relative.yearsAgo.multiple=Fa %S anys
pane.collections.title=Collections
pane.collections.title=Col·leccions
pane.collections.delete.title=Suprimeix la col·lecció
pane.collections.delete=Segur que voleu eliminar la col·lecció seleccionada?
pane.collections.delete.keepItems=Els elements d'aquesta col·lecció no s'esborraran
@ -308,7 +310,7 @@ pane.items.intro.text1=Us donem la benvinguda a %S!
pane.items.intro.text2=Consulteu la [guia de primers passos] per a aprendre a confeccionar la vostra biblioteca, i assegureu-vos d'[instal·lar un %S] per a poder afegir elements al %S a mesura que navegueu.
pane.items.intro.text3=Utilitzeu el %S en un altre ordinador? [Configureu la sincronització] per a reprendre-ho allà on ho hàgiu deixat.
pane.items.title=Items
pane.items.title=Elements
pane.items.loading=S'estan carregant els elements…
pane.items.loadError=S'ha produït un error en carregar la llista d'elements
pane.items.columnChooser.moreColumns=Més columnes
@ -327,22 +329,27 @@ pane.items.remove.multiple=Segur que voleu eliminar els elements seleccionats d'
pane.items.removeFromPublications.title=Suprimeix de les meves publicacions
pane.items.removeFromPublications=Esteu segur que voleu suprimir l'element seleccionat de 'Les meves publicacions'?
pane.items.removeFromPublications.multiple=Esteu segur que voleu suprimir els elements seleccionats de 'Les meves publicacions'?
pane.items.menu.addNoteFromAnnotations=Add Note from Annotations
pane.items.menu.addNoteFromAnnotations.multiple=Add Notes from Annotations
pane.items.removeRecursive.title=Suprimeix de la col·lecció i de les subcol·leccions
pane.items.removeRecursive=Esteu segur que voleu eliminar l'element seleccionat d'aquesta col·lecció i de totes les subcol·leccions?
pane.items.removeRecursive.multiple=Esteu segur que voleu eliminar els elements seleccionats d'aquesta col·lecció i de totes les subcol·leccions?
pane.items.menu.addNoteFromAnnotations=Afegeix nota de les anotacions
pane.items.menu.createNoteFromAnnotations=Crea una nota de les anotacions
pane.items.menu.findAvailablePDF=Cerca els PDF disponibles
pane.items.menu.findAvailablePDF.multiple=Cerca els PDF disponibles
pane.items.menu.addToCollection=Add to Collection
pane.items.menu.addToCollection=Afegeix a la col·lecció
pane.items.menu.remove=Elimina l'element de la col·lecció...
pane.items.menu.remove.multiple=Elimina els elements de la col·lecció...
pane.items.menu.removeFromPublications=Suprimeix l'element de les meves publicacions…
pane.items.menu.removeFromPublications.multiple=Suprimeix els elements de les meves publicacions…
pane.items.menu.moveToTrash=Desplaça l'element a la paperera.
pane.items.menu.moveToTrash.multiple=Desplaça els elements a la paperera...
pane.items.menu.delete=Delete Permanently…
pane.items.menu.delete=Elimina permanentment…
pane.items.menu.export=Exporta l'element seleccionat...
pane.items.menu.export.multiple=Exporta els elements seleccionats...
pane.items.menu.exportNote=Export Note…
pane.items.menu.exportNote.multiple=Export Notes…
pane.items.menu.exportNote=Exporta la nota…
pane.items.menu.exportNote.multiple=Exporta les notes…
pane.items.menu.exportPDF=Exporta el PDF…
pane.items.menu.exportPDF.multiple=Exporta els PDF…
pane.items.menu.createBib=Crea una bibliografia a partir de l'element seleccionat...
pane.items.menu.createBib.multiple=Crea una bibliografia a partir dels elements seleccionats...
pane.items.menu.generateReport=Genera un informe a partir de l'element seleccionat...
@ -355,6 +362,8 @@ pane.items.menu.createParent=Crea un element pare…
pane.items.menu.createParent.multiple=Crea elements pare
pane.items.menu.renameAttachments=Canvia el nom del fitxer amb les metadades ascendents
pane.items.menu.renameAttachments.multiple=Canvia els noms del fitxers amb les metadades ascendents
pane.items.menu.duplicateAndConvert.toBookSection=Crea una secció de llibre
pane.items.menu.duplicateAndConvert.toBook=Crea un llibre d'una secció de llibre
pane.items.showItemInLibrary=Mostra l'element a la biblioteca
pane.items.letter.oneParticipant=Carta a %S
@ -397,7 +406,7 @@ pane.item.notes.allNotes=Totes les notes
pane.item.notes.untitled=Nota sense títol
pane.item.notes.delete.confirm=Segur que voleu eliminar aquesta nota?
pane.item.notes.count=%1$S nota;%1$S notes
pane.item.notes.ignoreMissingImage=Some note images are missing and cannot be copied.
pane.item.notes.ignoreMissingImage=Falten imatges de notes i no poden copiar-se.
pane.item.attachments.rename.title=Nou títol:
pane.item.attachments.rename.renameAssociatedFile=Canvia el nom del fitxer associat
pane.item.attachments.rename.error=S'ha produït un error quan es canviava el nom del fitxer.
@ -408,13 +417,23 @@ pane.item.attachments.fileNotFound.text2.stored=Potser s'ha mogut o eliminat for
pane.item.attachments.fileNotFound.text2.stored.notOnServer=Potser s'ha mogut o eliminat fora del %1$S o, si s'ha afegit el fitxer en un altre ordinador, podria no haver-se sincronitzat amb el %2$S.
pane.item.attachments.fileNotFound.text2.linked=Potser s'ha mogut o eliminat fora del %1$S o potser s'ha configurat incorrectament un directori base d'adjuncions enllaçades en un dels vostres ordinadors.
pane.item.attachments.delete.confirm=Estàs segur que vols eliminar aquest arxiu adjunt?
pane.item.attachments.count.zero=Cap fitxer adjunt:
pane.item.attachments.count.singular=%S arxiu adjunt:
pane.item.attachments.count.plural=%S fitxers adjunts:
pane.item.attachments.has=Has attachment
pane.item.attachments.hasPDF=Has PDF attachment
pane.item.attachments.hasSnapshot=Has snapshot
pane.item.attachments.hasLink=Has link
pane.item.attachments.autoRelink.title=Fitxer ubicat automàticament
pane.item.attachments.autoRelink.text1=No s'ha pogut trobar el fitxer a la ubicació especificada, però s'ha trobat un fitxer amb el mateix nom en el directori base d'adjuncions enllaçades:
pane.item.attachments.autoRelink.text2=Antiga ubicació: %S
pane.item.attachments.autoRelink.text3=Nova ubicació: %S
pane.item.attachments.autoRelink.text4=El %1$S pot tornar a enllaçar aquesta adjunció.
pane.item.attachments.autoRelink.relink=Torna a enllaçar
pane.item.attachments.autoRelinkOthers.title=S'han trobat fitxers addicionals
pane.item.attachments.autoRelinkOthers.text=S'ha trobat una altra adjunció no enllaçada en la biblioteca del mateix directori. Voleu tornar a enllaçar l'adjunció també?; S'han trobat %S altres adjuncions no enllaçades en la biblioteca del mateix directori. Voleu tornar a enllaçar les adjuncions ubicades?
pane.item.attachments.autoRelink.locateManually=Ubica manualment…
pane.item.attachments.autoRelink.relinkAll=Torna a enllaçar tot
pane.item.attachments.count.zero=Cap adjunció:
pane.item.attachments.count.singular=%S adjunció:
pane.item.attachments.count.plural=%S adjuncions:
pane.item.attachments.has=Té una adjunció
pane.item.attachments.hasPDF=Té una adjunció PDF
pane.item.attachments.hasSnapshot=Té una captura
pane.item.attachments.hasLink=Té un enllaç
pane.item.attachments.select=Selecciona un fitxer
pane.item.attachments.PDF.installTools.title=PDF Tools no es troba instal·lat
pane.item.attachments.PDF.installTools.text=Per a utilitzar aquesta funcionalitat, heu d'instal·lar primer les eines PDF en la subfinestra Cerca de les preferències del Zotero.
@ -430,7 +449,7 @@ pane.item.related.count.zero=Cap element relacionat:
pane.item.related.count.singular=%S element relacionat:
pane.item.related.count.plural=%S elements relacionats:
pane.item.parentItem=Element ascendent
pane.item.viewOnline.tooltip=Go to this item online
pane.item.viewOnline.tooltip=Vés a aquest element en línia
pane.context.noParent=No hi ha cap element pare
pane.context.itemNotes=Notes dels elements
@ -439,7 +458,7 @@ pane.context.noNotes=Cap nota
itemTypes.note=Nota
itemTypes.annotation=Anotació
itemTypes.attachment=Fitxer adjunt
itemTypes.attachment=Adjunció
itemTypes.book=Llibre
itemTypes.bookSection=Capítol d'un llibre
itemTypes.journalArticle=Article de revista acadèmica
@ -474,7 +493,7 @@ itemTypes.conferencePaper=Text d'una conferència
itemTypes.document=Document
itemTypes.encyclopediaArticle=Article enciclopèdic
itemTypes.dictionaryEntry=Entrada de diccionari
itemTypes.preprint=Preprint
itemTypes.preprint=Prepublicació
itemFields.itemType=Tipus
itemFields.title=Títol
@ -483,7 +502,7 @@ itemFields.dateModified=Modificat
itemFields.source=Font
itemFields.notes=Notes
itemFields.tags=Etiquetes
itemFields.attachments=Fitxers adjunts
itemFields.attachments=Adjuncions
itemFields.related=Elements relacionats
itemFields.url=URL
itemFields.rights=Drets
@ -587,10 +606,10 @@ itemFields.issuingAuthority=Autoritat emissora
itemFields.filingDate=Data de presentació
itemFields.genre=Gènere
itemFields.archive=Arxiu
itemFields.attachmentPDF=PDF Attachment
itemFields.repository=Repository
itemFields.archiveID=Archive ID
itemFields.citationKey=Citation Key
itemFields.attachmentPDF=Adjunció PDF
itemFields.repository=Repositori
itemFields.archiveID=ID d'arxiu
itemFields.citationKey=Clau de cita
creatorTypes.author=Autor
creatorTypes.contributor=Col·laborador
@ -662,7 +681,7 @@ attachment.fullText=Text complet
attachment.acceptedVersion=Versió acceptada
attachment.submittedVersion=Versió enviada
attachment.convertToStored.title=Converteix a fitxer emmagatzemat;Converteix a fitxers emmagatzemats
attachment.convertToStored.text=%1$S adjunció es convertirà de fitxer adjunt a fitxer emmagatzemat.;%1$S adjuncions es convertiran de fitxers adjunts a fitxers emmagatzemats.
attachment.convertToStored.text=%1$S adjunció es convertirà de fitxer enllaçat a fitxer emmagatzemat.;%1$S adjuncions es convertiran de fitxers enllaçats a fitxers emmagatzemats.
attachment.convertToStored.deleteOriginal=Elimina el fitxer original després d'emmagatzemar-lo;Elimina els fitxers originals després d'emmagatzemar-los
db.dbCorrupted=Sembla que la base de dades de %1$S «%2$S» s'ha corromput.
@ -709,7 +728,7 @@ zotero.preferences.sync.reset.restoreToServer.button=Reemplaça les dades en la
zotero.preferences.sync.reset.fileSyncHistory=En la sincronització següent, el %1$S comprovarà tots els fitxers adjunts en el «%2$S» respecte al dispositu d'emmagatzematge. Si hi ha cap fitxer d'adjunt remot que no hi sigui localment, es baixarà; i els fitxers adjunts locals que no hi siguin remotament, es pujaran.\n\nAquesta opció no és necessària durant l'ús normal.
zotero.preferences.sync.reset.fileSyncHistory.cleared=S'ha esborrat l'historial de sincronització de fitxers de «%S».
zotero.preferences.search.rebuildIndex=Reconstruir índex
zotero.preferences.search.rebuildIndex=Reconstrueix l'índex
zotero.preferences.search.rebuildWarning=Voleu reconstruir l'índex sencer? Això pot tardar una mica.\n\nPer indexar només els elements que no han estat indexats feu servir %S.
zotero.preferences.search.clearIndex=Neteja l'índex
zotero.preferences.search.clearWarning=Després de netejar l'índex el contingut dels fitxers adjunts no serà cercable.\n\nEls enllaços web no es poden reindexar sense tornar a visitar la pàgina. Per a deixar els enllaços web indexats escull %S.
@ -719,10 +738,11 @@ zotero.preferences.export.quickCopy.citationStyles=Estils de cita
zotero.preferences.export.quickCopy.exportFormats=Formats d'exportació
zotero.preferences.export.quickCopy.instructions=La còpia ràpida us permet exportar elements ràpidament en un format donat. Podeu copiar elements seleccionats al porta-retalls prement %S o arrossegant elements directament en un quatre de text d'un altre programa.
zotero.preferences.export.quickCopy.citationInstructions=Per als estils de cita, podeu copiar les cites o les notes al peu prement %S o la tecla Maj abans d'arrossegar els elements.
zotero.preferences.export.quickCopy.note.htmlOptions.label=Rich Text/HTML
zotero.preferences.wordProcessors.installationSuccess=La instal·lació s'ha realitzat amb èxit.
zotero.preferences.wordProcessors.installationError=No s'ha pogut completar la instal·lació perquè s'ha produït un error. Assegureu-vos que el %1$S estigui tancant i llavors reinicieu-lo.
zotero.preferences.wordProcessors.manualInstallation.button=Manual Installation
zotero.preferences.wordProcessors.manualInstallation.button=Instal·lació manual
zotero.preferences.wordProcessors.installed=El complement %S està instal·lat.
zotero.preferences.wordProcessors.notInstalled=El complement %S no està instal·lat.
zotero.preferences.wordProcessors.install=Instal·la el connector %S
@ -773,7 +793,7 @@ fileInterface.viewSupportedFormats=Mostra els formats compatibles...
fileInterface.untitledBibliography=Bibliografia sense títol
fileInterface.bibliographyHTMLTitle=Bibliografia
fileInterface.importError=S'ha produït un error quan s'intentava importar el fitxer seleccionat. Assegureu-vos que el fitxer és correcte i torneu-ho a intentar.
fileInterface.importError.translator=An error occurred importing the selected file with “%S”. Please ensure that the file is valid and try again.
fileInterface.importError.translator=S'ha produït un error en importar el fitxer seleccionat amb el «%S». Assegureu-vos que el fitxer és vàlid i torneu-ho a provar.
fileInterface.importClipboardNoDataError=No s'han pogut llegir dades importables des del porta-retalls.
fileInterface.noReferencesError=Els elements seleccionats no contenen cap referència. Seleccioneu una o més referències i tornar-ho a intentar.
fileInterface.bibliographyGenerationError=S'ha produït un error quan es generava la bibliografia. Torneu-ho a intentar.
@ -831,9 +851,9 @@ searchConditions.mapType=Tipus de mapa
searchConditions.medium=Mitjà
searchConditions.artworkMedium=Mitjà artístic
searchConditions.dateModified=Data de modificació
searchConditions.fulltextContent=Contingut de l'arxiu adjunt
searchConditions.fulltextContent=Contingut de l'adjunció
searchConditions.programmingLanguage=Llengua de programació
searchConditions.fileTypeID=Tipus de fitxer adjunt
searchConditions.fileTypeID=Tipus de fitxer de l'adjunció
searchConditions.annotationText=Text d'anotació
searchConditions.annotationComment=Comentari d'anotació
@ -845,7 +865,7 @@ fulltext.indexState.queued=En cua
exportOptions.exportNotes=Exporta les notes
exportOptions.exportFileData=Exporta els fitxers adjunts
exportOptions.includeAnnotations=Inclou anotacions
exportOptions.includeAppLinks=Include %S Links
exportOptions.includeAppLinks=Inclou enllaços %S
exportOptions.useJournalAbbreviation=Utilitza l'abreviació de la publicació
charset.UTF8withoutBOM=Unicode (UTF-8 sense BOM)
charset.autoDetect=(Detecció automàtica)
@ -864,22 +884,7 @@ citation.showEditor=Mostra editor...
citation.hideEditor=Oculta l'editor...
citation.citations=Cites
citation.notes=Notes
citation.locator.page=Pàgina
citation.locator.book=Llibre
citation.locator.chapter=Capítol
citation.locator.column=Columna
citation.locator.figure=Figura
citation.locator.folio=Infoli
citation.locator.issue=Número
citation.locator.line=Línia
citation.locator.note=Nota
citation.locator.opus=Opus
citation.locator.paragraph=Paràgraf
citation.locator.part=Part
citation.locator.section=Secció
citation.locator.subverbo=Sub verbo
citation.locator.volume=Volum
citation.locator.verse=Vers
citation.locator.timestamp=Marca horària
report.title.default=Informe del Zotero
report.parentItem=Element pare:
@ -942,10 +947,10 @@ integration.error.misconfiguredWordStartupFolder.fix=El connector no pot aparèi
integration.error.macWordSBPermissionsMissing.title=Hi manca el permís
integration.error.macWordSBPermissionsMissing=El Zotero no té permisos per a controlar el Word. Per a atorgar-li:\n\n1) Obriu les preferències del sistema\n2) Feu clic a «Seguretat i privadesa»\n3) Seleccioneu la pestanya de «Privadesa»\n4) Trobeu i seleccioneu «Automatització» a l'esquerra\n5) Marqueu la casella de «Microsoft Word» sota «Zotero»\n6) Reinicieu el Word
integration.error.macWordSBPermissionsMissing.pre2016=Si «Microsoft Word» no apareix sota «Automatització», assegureu-vos que esteu executant el Word 2011 versió 14.7.7 o superior.
integration.error.m1UpgradeOS.title=macOS Upgrade Required
integration.error.m1UpgradeOS=Citing in Word on an Apple Silicon Mac with macOS 11.3 or earlier is not supported and may cause %S to freeze. Please upgrade to macOS 11.4 or later.
integration.error.m1UpgradeOS.title=Cal una actualització del macOS
integration.error.m1UpgradeOS=No hi ha suport per a citar en el Word amb l'Apple Silicon Mac fent servir un macOS 11.3 o anterior, i pot provocar que el %S es pengi. Actualitzeu-vos al macOS 11.4 o posterior.
integration.error.viewTroubleshootingInfo=Voleu veure les instruccions de resolució de problemes?
integration.error.tabUnavailable=The operation cannot be completed because the %S tab is no longer available.
integration.error.tabUnavailable=No es pot completar l'operació perquè la pestanya %S ja no és disponible.
integration.replace=Substitueix aquest camp del Zotero?
integration.missingItem.single=Aquest element ja no existeix a la base de dades del Zotero. Voleu seleccionar un altre element?
@ -985,7 +990,7 @@ styles.installStyle=Instal·la estil "%1$S" des de %2$S?
styles.updateStyle=Actualitza l'estil existent "%1$S" amb "%2$S" des de %3$S?
styles.installed=L'estil "%S" s'ha instal·lat correctament
styles.installError=%S no sembla un fitxer d'estil vàlid.
styles.validationWarning="%S" is not a valid CSL 1.0.2 style file, and may not work properly with %S.\n\nAre you sure you want to continue?
styles.validationWarning=«%S» no és un fitxer d'estil CSL 1.0.2 vàlid i podria no funcionar correctament amb el %S.\n\nEsteu segur que voleu continuar?
styles.installSourceError=%1$S fa referència a un fitxer inexistent o no vàlid a %2$S com la seva font.
styles.deleteStyle=Segur que voleu suprimir l'estil "%1$S"?
styles.deleteStyles=Segur que voleu suprimir els estils seleccionats?
@ -1004,9 +1009,9 @@ sync.resetGroupFilesAndSync=Reinicia els fitxers de grup i sincronitza
sync.skipGroup=Omet el grup
sync.removeGroupsAndSync=Elimina el grup i sincronitza
sync.reminder.setUp.message=Back up your library with %S syncing.
sync.reminder.setUp.action=Set Up Syncing
sync.reminder.autoSync.message=%S hasn’t synced in a while. Do you want to enable automatic syncing?
sync.reminder.setUp.message=Feu una còpia de seguretat de la biblioteca amb la sincronització del %S.
sync.reminder.setUp.action=Configura la sincronització
sync.reminder.autoSync.message=El %S fa temps que no se sincronitza. Voleu activar-ne la sincronització automàtica?
sync.error.usernameNotSet=No s'ha establit un nom d'usuari
sync.error.usernameNotSet.text=Cal que introduïu el vostre nom d'usuari i contrasenya de zotero.org en les preferències de Zotero per a sincronitzar amb el servidor de Zotero.
@ -1042,8 +1047,8 @@ sync.error.invalidDataError.otherData=Altres dades continuaran sincronitzant-se.
account.unlinkWarning=Si es desvincula el vostre compte, s'evitarà que %S sincronitzi les vostres dades.
account.unlinkWarning.removeData=Suprimeix les meves dades de %S d'aquest ordinador
account.unlinkWarning.button=Desvincula el compte
account.warning.emptyLibrary=You are about to sync to an empty %1$S database. This could happen if you accidentally deleted the %2$S file in your %1$S data directory or the location of your data directory changed.
account.warning.emptyLibrary.dataWillBeDownloaded=If you continue, data in the ‘%1$S’ account will be downloaded to this computer. Your online library won’t be affected.
account.warning.emptyLibrary=Esteu a punt de sincronitzar una base de dades buida de %1$S. Això pot passar perquè heu suprimit accidentalment el fitxer %2$S del vostre directori de dades de %1$S o la ubicació del vostre directori de dades ha canviat.
account.warning.emptyLibrary.dataWillBeDownloaded=Si continueu, les dades del compte «%1$S» es baixaran en aquest ordinador. La biblioteca en línia no es veurà afectada.
account.warning.existingDataElsewhere=Si les dades del %S existeixen en un altre lloc de l'ordinador, caldria que les mogueu al directori de dades actual o bé canvieu la ubicació del directori de dades perquè apunti on es troben les dades que ja existeixen.
account.lastSyncWithDifferentAccount=La base de dades del %1$S s'ha sincronitzat per darrera vegada amb un compte diferent («%2$S») del que esteu provant de sincronitzar-vos ara («%3$S»). Si continueu, totes les biblioteques de %1$S associades amb el compte «%2$S» se suprimiran d'aquest ordinador.
account.lastSyncWithDifferentAccount.beforeContinuing=Abans de continuar, assegureu-vos que totes les dades i fitxers que voleu conservar estiguin sincronitzats amb el compte «%S» o que tingueu una còpia de seguretat del directori de dades de %S.
@ -1207,19 +1212,21 @@ lookup.failure.description=El Zotero no ha pogut trobar un registre per a l'iden
lookup.failureToID.description=El Zotero no ha pogut trobar cap dels identificadors de l'entrada. Verifiqueu l'entrada i torneu-ho a intentar.
lookup.failureTooMany.description=Massa identificadors. Introduïu-ne un només i torneu-ho a provar.
createParent.prompt=Enter a DOI, ISBN, PMID, arXiv ID, or ADS Bibcode to identify this file
createParent.prompt=Introduïu un DOI, ISBN, PMID, ID d'arXiv or Bibcode ADS per a identificar aquest fitxer
locate.online.label=Veure en línia
locate.pdf.label=Open PDF
locate.pdfNewWindow.label=Open PDF in New Window
locate.pdf.label=Obre el PDF
locate.pdfNewWindow.label=Obre el PDF en una finestra nova
locate.snapshot.label=Veure l'instantània
locate.file.label=Veure el fitxer
locate.externalViewer.label=Obre en un visor extern
locate.internalViewer.label=Obre en el visor intern
locate.showFile.label=Mostra el fitxer
locate.libraryLookup.label=Cerca la biblioteca
locate.libraryLookup.noResolver.title=No hi ha cap resolutor OpenURL
locate.libraryLookup.noResolver.text=Heu de triar un resolutor OpenURL del tauler Avançat de les preferències del %S
locate.manageLocateEngines=Manage Lookup Engines...
locate.locateEngineDescription=Lookup engines help you find resources in your %S library on the web. Engines enabled in this list appear in the Locate drop-down in the toolbar.
locate.locateEngineDescription=Els motors de consulta us ajuden a trobar recursos de la vostra biblioteca de %S al web. Els motors s'habiliten en la llista que apareix en el desplegable 'Ubica' de la barra d'eines.
standalone.corruptInstallation=La instal·lació del Zotero sembla que està corrupta per una actualització automàtica que ha fallat. Tot i que el Zotero pot continuar funcionant, per a evitar potencials errades, baixeu-ne la darrera versió de https://www.zotero.org/download tan aviat com sigui possible.
standalone.addonInstallationFailed.title=La intal·lació del complement ha fallat
@ -1298,112 +1305,126 @@ noteEditor.copy=Copia
noteEditor.paste=Enganxa
noteEditor.rightToLeft=De dreta a esquerra
noteEditor.leftToRight=D'esquerra a dreta
noteEditor.updateNotice=This note was edited with a newer version of %1$S.\nPlease update %1$S to make changes.
noteEditor.enterLink=Enter Link
noteEditor.heading1=Heading 1
noteEditor.heading2=Heading 2
noteEditor.heading3=Heading 3
noteEditor.paragraph=Paragraph
noteEditor.monospaced=Monospaced
noteEditor.bulletList=Bulleted List
noteEditor.orderedList=Numbered List
noteEditor.blockquote=Block Quote
noteEditor.formatText=Format Text
noteEditor.highlightText=Highlight Text
noteEditor.removeColor=Remove Color
noteEditor.bold=Bold
noteEditor.italic=Italic
noteEditor.underline=Underline
noteEditor.strikethrough=Strikethrough
noteEditor.subscript=Subscript
noteEditor.superscript=Superscript
noteEditor.returnToNotesList=Return to Notes List
noteEditor.insertLink=Insert Link
noteEditor.clearFormatting=Clear Formatting
noteEditor.align=Align
noteEditor.alignLeft=Align Left
noteEditor.alignCenter=Align Center
noteEditor.alignRight=Align Right
noteEditor.insertCitation=Insert Citation
noteEditor.updateNotice=La nota es va editar en una versió més nova del %1$S.\nActualitzeu el %1$S per a fer-ne canvis.
noteEditor.enterLink=Introdueix un enllaç
noteEditor.heading1=Encapçalament 1
noteEditor.heading2=Encapçalament 2
noteEditor.heading3=Encapçalament 3
noteEditor.paragraph=Paràgraf
noteEditor.monospaced=Monoespaiat
noteEditor.bulletList=Llista de pics
noteEditor.orderedList=Llista numerada
noteEditor.blockquote=Cita en bloc
noteEditor.mathBlock=Bloc de matemàtiques
noteEditor.formatText=Text formal
noteEditor.highlightText=Marca el text
noteEditor.removeColor=Elimina el color
noteEditor.bold=Negreta
noteEditor.italic=Cursiva
noteEditor.underline=Subratllat
noteEditor.strikethrough=Ratllat
noteEditor.subscript=Subíndex
noteEditor.superscript=Superíndex
noteEditor.returnToNotesList=Torna a la llista de notes
noteEditor.insertLink=Insereix un enllaç
noteEditor.clearFormatting=Neteja el format
noteEditor.align=Alinea
noteEditor.alignLeft=Alinea a l'esquerra
noteEditor.alignCenter=Alinea al centre
noteEditor.alignRight=Alinea a la dreta
noteEditor.insertCitation=Insereix una cita
noteEditor.more=Més
noteEditor.find=Find
noteEditor.replace=Replace
noteEditor.previous=Previous
noteEditor.next=Next
noteEditor.replaceNext=Replace
noteEditor.replaceAll=Replace All
noteEditor.goToPage=Go to Page
noteEditor.showItem=Show Item
noteEditor.editCitation=Edit Citation
noteEditor.showOnPage=Show on Page
noteEditor.unlink=Unlink
noteEditor.set=Set
noteEditor.edit=Edit
noteEditor.addCitation=Add Citation
noteEditor.removeCitation=Hide Citation
noteEditor.findAndReplace=Find and Replace
noteEditor.editInWindow=Edit in a Separate Window
noteEditor.applyAnnotationColors=Show Annotation Colors
noteEditor.removeAnnotationColors=Hide Annotation Colors
noteEditor.addCitations=Show Annotation Citations
noteEditor.removeCitations=Hide Annotation Citations
noteEditor.insertTable=Insert Table
noteEditor.insertRowBefore=Insert Row Above
noteEditor.insertRowAfter=Insert Row Below
noteEditor.insertColumnBefore=Insert Column Left
noteEditor.insertColumnAfter=Insert Column Right
noteEditor.deleteRow=Delete Row
noteEditor.deleteColumn=Delete Column
noteEditor.deleteTable=Delete Table
noteEditor.find=Cerca
noteEditor.replace=Reemplaça
noteEditor.previous=Anterior
noteEditor.next=Següent
noteEditor.replaceNext=Reemplaça
noteEditor.replaceAll=Reemplaça-ho tot
noteEditor.goToPage=Vés a la pàgina
noteEditor.showItem=Mostra l'element
noteEditor.editCitation=Edita la cita
noteEditor.showOnPage=Mostra-ho a la pàgina
noteEditor.unlink=Desenllaça
noteEditor.set=Defineix
noteEditor.edit=Edita
noteEditor.addCitation=Afegeix una cita
noteEditor.removeCitation=Amaga la cita
noteEditor.findAndReplace=Cerca i reemplaça
noteEditor.editInWindow=Edita en una finestra separada
noteEditor.applyAnnotationColors=Mostra els colors d'anotació
noteEditor.removeAnnotationColors=Amaga els colors d'anotació
noteEditor.addCitations=Mostra les cites d'anotació
noteEditor.removeCitations=Amaga les cites d'anotació
noteEditor.image=Imatge
noteEditor.math=Matemàtiques
noteEditor.table=Taula
noteEditor.copyImage=Copia la imatge
noteEditor.saveImageAs=Anomena i desa la imatge…
noteEditor.insertRowBefore=Insereix una fila a sobre
noteEditor.insertRowAfter=Insereix una fila a sota
noteEditor.insertColumnBefore=Insereix una columna a l'esquerra
noteEditor.insertColumnAfter=Insereix una columna a la dreta
noteEditor.deleteRow=Suprimeix la fila
noteEditor.deleteColumn=Suprimeix la columna
noteEditor.deleteTable=Suprimeix la taula
pdfReader.annotations=Annotations
pdfReader.showAnnotations=Show Annotations
pdfReader.searchAnnotations=Search Annotations
pdfReader.noAnnotations=Create an annotation to see it in the sidebar
pdfReader.addText=Add extracted text
pdfReader.addComment=Add comment
pdfReader.addTags=Add tags…
pdfReader.highlightText=Highlight Text
pdfReader.addNote=Add Note
pdfReader.selectArea=Select Area
pdfReader.pickColor=Pick a Color
pdfReader.addToNote=Add to Note
pdfReader.zoomIn=Zoom In
pdfReader.zoomOut=Zoom Out
pdfReader.zoomAuto=Automatically Resize
pdfReader.zoomPageWidth=Zoom to Page Width
pdfReader.zoomPageHeight=Zoom to Page Height
pdfReader.annotations=Anotacions
pdfReader.showAnnotations=Mostra les anotacions
pdfReader.searchAnnotations=Cerca anotacions
pdfReader.noAnnotations=Creeu una anotació per a veure-la en la barra lateral
pdfReader.noExtractedText=Text no extret
pdfReader.addComment=Afegeix un comentari
pdfReader.addTags=Afegeix etiquetes…
pdfReader.highlightText=Marca el text
pdfReader.addNote=Afegeix una nota
pdfReader.selectArea=Selecciona l'àrea
pdfReader.pickColor=Tria un color
pdfReader.addToNote=Afegeix a la nota
pdfReader.zoomIn=Amplia
pdfReader.zoomOut=Redueix
pdfReader.zoomAuto=Redimensiona automàticament
pdfReader.zoomPageWidth=Ajusta a l'amplada de la pàgina
pdfReader.zoomPageHeight=Ajusta a l'alçada de la pàgina
pdfReader.splitVertically=Divideix verticalment
pdfReader.splitHorizontally=Divideix horitzontalment
pdfReader.nextPage=Pàgina següent
pdfReader.previousPage=Pàgina anterior
pdfReader.page=Pàgina
pdfReader.readOnly=Només de lectura
pdfReader.promptTransferFromPDF.title=Import Annotations
pdfReader.promptTransferFromPDF.text=Annotations stored in the PDF file will be moved to %1$S.
pdfReader.promptTransferToPDF.title=Store Annotations in File
pdfReader.promptTransferToPDF.text=Annotations will be transferred to the PDF file and will no longer be editable in %S.
pdfReader.promptPasswordProtected=The operation is not supported for password-protected PDF files.
pdfReader.editPageNumber=Edit Page Number…
pdfReader.editHighlightedText=Edit Highlighted Text
pdfReader.pageNumberPopupHeader=Change page number for:
pdfReader.thisAnnotation=This annotation
pdfReader.selectedAnnotations=Selected annotations
pdfReader.thisPage=This page
pdfReader.thisPageAndLaterPages=This page and later pages
pdfReader.allPages=All pages
pdfReader.autoDetect=Auto-Detect
pdfReader.deleteAnnotation.singular=Are you sure you want to delete the selected annotation?
pdfReader.deleteAnnotation.plural=Are you sure you want to delete the selected annotations?
pdfReader.promptTransferFromPDF.title=Importa anotacions
pdfReader.promptTransferFromPDF.text=Les anotacions emmagatzemades en el fitxer PDF es mouran a %1$S.
pdfReader.promptTransferToPDF.title=Emmagatzema les anotacions en un fitxer
pdfReader.promptTransferToPDF.text=Les anotacions es transferiran al fixer PDF i ja no podren ser editades més al %S.
pdfReader.promptPasswordProtected=L'operació no funciona en fitxers PDF protegits amb contrasenya.
pdfReader.promptDeletePages.title=Elimina les pàgines
pdfReader.promptDeletePages.text=Esteu segur que voleu suprimir %1$S pàgina del fitxer PDF?;Esteu segur que voleu suprimir %1$S pàgines del fitxer PDF?
pdfReader.rotateLeft=Gira a l'esquerra
pdfReader.rotateRight=Gira a la dreta
pdfReader.rotate180=Gira 180°
pdfReader.editPageNumber=Edita el número de pàgina…
pdfReader.editHighlightedText=Edita el text ressaltat
pdfReader.copyImage=Copia la imatge
pdfReader.saveImageAs=Anomena i desa la imatge…
pdfReader.pageNumberPopupHeader=Canvia el número de la pàgina per:
pdfReader.thisAnnotation=Aquesta anotació
pdfReader.selectedAnnotations=Anotacions seleccionades
pdfReader.thisPage=Aquesta pàgina
pdfReader.thisPageAndLaterPages=Aquesta pàgina i posteriors
pdfReader.allPages=Totes les pàgines
pdfReader.autoDetect=Detecció automàtica
pdfReader.deleteAnnotation.singular=Esteu segur que voleu eliminar l'anotació seleccionada?
pdfReader.deleteAnnotation.plural=Esteu segur que voleu eliminar les anotacions seleccionades?
spellCheck.checkSpelling=Check Spelling
spellCheck.addRemoveDictionaries=Add/Remove Dictionaries…
spellCheck.dictionaryManager.title=Add/Remove Dictionaries
spellCheck.dictionaryManager.updateAvailable=%S (update available)
spellCheck.dictionaryManager.error.unableToInstall=Unable to install “%S”:
spellCheck.checkSpelling=Revisa l'ortografia
spellCheck.addRemoveDictionaries=Afegeix/Suprimeix diccionaris…
spellCheck.dictionaryManager.title=Afegeix/Suprimeix diccionaris
spellCheck.dictionaryManager.updateAvailable=%S (actualització disponible)
spellCheck.dictionaryManager.error.unableToInstall=No s'ha pogut instal·lar «%S»:
tabs.move=Move Tab
tabs.moveToStart=Move to Start
tabs.moveToEnd=Move to End
tabs.moveToWindow=Move to New Window
tabs.duplicate=Duplicate Tab
tabs.undoClose=Reopen Closed Tab;Reopen Closed Tabs
tabs.closeOther=Close Other Tabs
tabs.move=Mou la pestanya
tabs.moveToStart=Mou a l'inici
tabs.moveToEnd=Mou al final
tabs.moveToWindow=Mou a una finestra nova
tabs.duplicate=Duplica la pestanya
tabs.undoClose=Torna a obrir la pestanya tancada;Torna a obrir les pestanyes tancades
tabs.closeOther=Tanca les altres pestanyes

View file

@ -5,6 +5,12 @@
"general_done": {
"message": "Hotovo"
},
"general_yes": {
"message": "Ano"
},
"general_no": {
"message": "Ne"
},
"general_tryAgain": {
"message": "Zkusit znovu"
},
@ -14,6 +20,9 @@
"general_cancel": {
"message": "Zrušit"
},
"general_copyToClipboard": {
"message": "Kopírovat do schránky"
},
"progressWindow_savingTo": {
"message": "Ukládám do"
},

View file

@ -30,7 +30,7 @@
<!ENTITY zotero.preferences.groups.whenCopyingInclude "Při kopírování položek mezi knihovnami zahrnout:">
<!ENTITY zotero.preferences.groups.childNotes "dceřiné poznámky">
<!ENTITY zotero.preferences.groups.childFiles "dceřiné snímky a importované soubory">
<!ENTITY zotero.preferences.groups.annotations "annotations">
<!ENTITY zotero.preferences.groups.annotations "anotace">
<!ENTITY zotero.preferences.groups.childLinks "dceřiné odkazy">
<!ENTITY zotero.preferences.groups.tags "štítky">
@ -105,8 +105,8 @@
<!ENTITY zotero.preferences.export.citePaperJournalArticleURL.description "Pokud je tato volba neaktivní, Zotero zahrne URL v případě citování článku (z časopisu, magazínu nebo novin) pouze když článek nemá specifikovaný rozsah stran.">
<!ENTITY zotero.preferences.quickCopy.caption "Rychlé kopírování">
<!ENTITY zotero.preferences.quickCopy.itemFormat "Item Format:">
<!ENTITY zotero.preferences.quickCopy.noteFormat "Note Format:">
<!ENTITY zotero.preferences.quickCopy.itemFormat "Formát položky">
<!ENTITY zotero.preferences.quickCopy.noteFormat "Formát poznámky">
<!ENTITY zotero.preferences.quickCopy.copyAsHTML "Kopírovat jako HTML">
<!ENTITY zotero.preferences.quickCopy.siteEditor.setings "Nastavení dle stránky:">
<!ENTITY zotero.preferences.quickCopy.siteEditor.domainPath "Doména/Cesta">
@ -144,7 +144,7 @@
<!ENTITY zotero.preferences.proxies.proxyOptions "Možnosti Proxy">
<!ENTITY zotero.preferences.proxies.desc_before_link "Zotero bude transparentně přesměrovávat požadavky skrz uložené proxy servery. Více informací najdete v">
<!ENTITY zotero.preferences.proxies.desc_link "dokumentaci k proxy">
<!ENTITY zotero.preferences.proxies.desc_after_link ".">
<!ENTITY zotero.preferences.proxies.desc_after_link "pro další informace.">
<!ENTITY zotero.preferences.proxies.transparent "Automaticky přesměrovávat požadavky skrz dříve použité proxy servery.">
<!ENTITY zotero.preferences.proxies.autoRecognize "Automaticky rozpoznávat proxované zdroje">
<!ENTITY zotero.preferences.proxies.showRedirectNotification "Zobrazovat upozornění při přesměrování přes proxy">

View file

@ -36,7 +36,7 @@
<!ENTITY copyCitationCmd.label "Kopírovat citaci">
<!ENTITY copyBibliographyCmd.label "Kopírovat bibliografii">
<!ENTITY copyNoteCmd.label "Copy Note">
<!ENTITY copyNoteCmd.label "Kopírovat poznámku">
<!ENTITY bidiSwitchPageDirectionItem.label "Přepnout směr stránky">
<!ENTITY bidiSwitchPageDirectionItem.accesskey "g">

View file

@ -6,7 +6,7 @@
<!ENTITY zotero.general.deselectAll "Odznačit vše">
<!ENTITY zotero.general.edit "Editovat">
<!ENTITY zotero.general.delete "Smazat">
<!ENTITY zotero.general.moveToTrash "Move to Trash">
<!ENTITY zotero.general.moveToTrash "Přesunout do Koše">
<!ENTITY zotero.general.ok "OK">
<!ENTITY zotero.general.cancel "Zrušit">
<!ENTITY zotero.general.refresh "Obnovit">
@ -128,8 +128,8 @@
<!ENTITY zotero.toolbar.attachment.add "Uložit kopii souboru...">
<!ENTITY zotero.toolbar.attachment.weblink "Uložit odkaz na aktuální stránku">
<!ENTITY zotero.toolbar.attachment.snapshot "Vytvořit snímek aktuální stránky">
<!ENTITY zotero.toolbar.context.item "Item">
<!ENTITY zotero.toolbar.context.notes "Notes">
<!ENTITY zotero.toolbar.context.item "Položka">
<!ENTITY zotero.toolbar.context.notes "Poznámky">
<!ENTITY zotero.tagSelector.noTagsToDisplay "Žádné štítky k zobrazení">
<!ENTITY zotero.tagSelector.loadingTags "Nahrávám štítky...">
@ -307,12 +307,10 @@
<!ENTITY zotero.context.addStandaloneNoteFromAnnotations "Add Standalone Note from Annotations">
<!ENTITY zotero.context.editInWindow "Edit in a Separate Window">
<!ENTITY zotero.pdfReader.firstPage "First Page">
<!ENTITY zotero.pdfReader.firstPage "První strana">
<!ENTITY zotero.pdfReader.lastPage "Last Page">
<!ENTITY zotero.pdfReader.back "Back">
<!ENTITY zotero.pdfReader.forward "Forward">
<!ENTITY zotero.pdfReader.rotateClockwise "Rotate Clockwise">
<!ENTITY zotero.pdfReader.rotateCounterclockwise "Rotate Counterclockwise">
<!ENTITY zotero.pdfReader.handTool "Hand Tool">
<!ENTITY zotero.pdfReader.verticalScrolling "Vertical Scrolling">
<!ENTITY zotero.pdfReader.horizontalScrolling "Horizontal Scrolling">
@ -325,4 +323,9 @@
<!ENTITY zotero.pdfReader.zoomAuto "Automatically Resize">
<!ENTITY zotero.pdfReader.zoomPageWidth "Zoom to Page Width">
<!ENTITY zotero.pdfReader.zoomPageHeight "Zoom to Page Height">
<!ENTITY zotero.pdfReader.splitVertically "Split Vertically">
<!ENTITY zotero.pdfReader.splitHorizontally "Split Horizontally">
<!ENTITY zotero.pdfReader.transferFromPDF "Import Annotations…">
<!ENTITY zotero.pdfReader.rotatePageLeft "Rotate Page Left">
<!ENTITY zotero.pdfReader.rotatePageRight "Rotate Page Right">
<!ENTITY zotero.pdfReader.rotatePage180 "Rotate Page 180°">

View file

@ -52,7 +52,7 @@ general.moreInformation=Více informací
general.learnMore=Learn More
general.seeForMoreInformation=Pro více informací se podívejte na %S
general.open=Otevřít %S
general.close=Close
general.close=Zavřít
general.enable=Povolit
general.disable=Zakázat
general.reset=Resetovat
@ -71,9 +71,9 @@ general.fix=Fix...
general.tryAgain=Zkusit znovu
general.tryLater=Zkusit později
general.showDirectory=Zobrazit adresář
general.showInLibrary=Show in Library
general.showInLibrary=Ukázat v knihovně
general.continue=Pokračovat
general.copy=Copy
general.copy=Kopírovat
general.copyToClipboard=Kopírovat do schránky
general.cancel=Zrušit
general.clear=Vyčistit
@ -89,9 +89,10 @@ general.back=Back
general.languages=Languages
general.default=Default
general.custom=Custom
general.loading=Loading…
general.loading=Nahrává se...
general.richText=Rich Text
general.clearSelection=Clear Selection
general.insert=Insert
general.yellow=Yellow
general.red=Red
@ -114,6 +115,7 @@ about.getInvolved=Chcete pomoci? [Zapojte se] ještě dnes!
punctuation.openingQMark=“
punctuation.closingQMark=”
punctuation.colon=:
punctuation.colon.withString=%S:
punctuation.ellipsis=...
install.quickStartGuide=Rychlý průvodce
@ -327,8 +329,11 @@ pane.items.remove.multiple=Opravdu chcete odstranit vybrané položky z této ko
pane.items.removeFromPublications.title=Odstranit z Mých publikací
pane.items.removeFromPublications=Opravdu chcete odstranit vybranou položku z Mých publikací?
pane.items.removeFromPublications.multiple=Opravdu chcete odstranit vybrané položky z Mých publikací?
pane.items.menu.addNoteFromAnnotations=Add Note from Annotations
pane.items.menu.addNoteFromAnnotations.multiple=Add Notes from Annotations
pane.items.removeRecursive.title=Remove from Collection and Subcollections
pane.items.removeRecursive=Are you sure you want to remove the selected item from this collection and all subcollections?
pane.items.removeRecursive.multiple=Are you sure you want to remove the selected items from this collection and all subcollections?
pane.items.menu.addNoteFromAnnotations=Přidat poznámku z anotací
pane.items.menu.createNoteFromAnnotations=Create Note from Annotations
pane.items.menu.findAvailablePDF=Najít dostupné PDF
pane.items.menu.findAvailablePDF.multiple=Najít dostupná PDF
pane.items.menu.addToCollection=Add to Collection
@ -338,11 +343,13 @@ pane.items.menu.removeFromPublications=Odstranit položku z Mých publikací
pane.items.menu.removeFromPublications.multiple=Odstranit položky z Mých publikací...
pane.items.menu.moveToTrash=Přesunout položku do Koše...
pane.items.menu.moveToTrash.multiple=Přesunout položky do Koše...
pane.items.menu.delete=Delete Permanently…
pane.items.menu.delete=Definitivně smazat...
pane.items.menu.export=Exportovat vybranou položku...
pane.items.menu.export.multiple=Exportovat vybrané položky...
pane.items.menu.exportNote=Export Note…
pane.items.menu.exportNote.multiple=Export Notes…
pane.items.menu.exportPDF=Export PDF…
pane.items.menu.exportPDF.multiple=Export PDFs…
pane.items.menu.createBib=Vytvořit bibliografii z vybrané položky...
pane.items.menu.createBib.multiple=Vytvořit bibliografii z vybraných položek...
pane.items.menu.generateReport=Vygenerovat zprávu z vybrané položky...
@ -355,6 +362,8 @@ pane.items.menu.createParent=Create Parent Item…
pane.items.menu.createParent.multiple=Create Parent Items
pane.items.menu.renameAttachments=Přejmenovat soubor z rodičovských metadat
pane.items.menu.renameAttachments.multiple=Přejmenovat soubory z rodičovských metadat
pane.items.menu.duplicateAndConvert.toBookSection=Create Book Section
pane.items.menu.duplicateAndConvert.toBook=Create Book from Book Section
pane.items.showItemInLibrary=Zobrazit položku v Knihovně.
pane.items.letter.oneParticipant=Dopis pro %S
@ -393,7 +402,7 @@ pane.item.switchFieldMode.two=Přepnout na více polí
pane.item.creator.moveToTop=Přesunout nahoru
pane.item.creator.moveUp=Posunout nahoru
pane.item.creator.moveDown=Posunout dolů
pane.item.notes.allNotes=All Notes
pane.item.notes.allNotes=Všechny poznámky
pane.item.notes.untitled=Nepojmenovaná poznámka
pane.item.notes.delete.confirm=Jste si jistý, že chcete smazat tuto poznámku?
pane.item.notes.count=%1$S note;%1$S notes
@ -408,6 +417,16 @@ pane.item.attachments.fileNotFound.text2.stored=Je možné, že byl přesunut ne
pane.item.attachments.fileNotFound.text2.stored.notOnServer=Je možné, že byl přesunut nebo smazán mimo %1$S, nebo, pokud byl tento soubor přidán na jiném počítači, je možné, že ještě nebyla provedena synchronizace do %2$S.
pane.item.attachments.fileNotFound.text2.linked=Je možné, že byl přesunut nebo smazán mimo %1$S nebo na vašem počítači není správně nastaven základní adresář pro připojené přílohy.
pane.item.attachments.delete.confirm=Jste si jisti, že chcete smazat tuto přílohu?
pane.item.attachments.autoRelink.title=File Located Automatically
pane.item.attachments.autoRelink.text1=The file could not be found at the specified location, but a file with the same name was found within your Linked Attachment Base Directory:
pane.item.attachments.autoRelink.text2=Old Location: %S
pane.item.attachments.autoRelink.text3=New Location: %S
pane.item.attachments.autoRelink.text4=%1$S can automatically relink this attachment.
pane.item.attachments.autoRelink.relink=Relink
pane.item.attachments.autoRelinkOthers.title=Additional Files Located
pane.item.attachments.autoRelinkOthers.text=One other unlinked attachment in this library was found within the same directory. Relink this attachment as well?;%S other unlinked attachments in this library were found within the same directory. Relink all located attachments?
pane.item.attachments.autoRelink.locateManually=Locate Manually…
pane.item.attachments.autoRelink.relinkAll=Relink All
pane.item.attachments.count.zero=%S příloh:
pane.item.attachments.count.singular=%S příloha:
pane.item.attachments.count.plural=%S příloh:
@ -430,15 +449,15 @@ pane.item.related.count.zero=%S souvisejících:
pane.item.related.count.singular=%S související:
pane.item.related.count.plural=%S souvisejících:
pane.item.parentItem=Rodičovská položka:
pane.item.viewOnline.tooltip=Go to this item online
pane.item.viewOnline.tooltip=Přejít na tuto položku online
pane.context.noParent=No parent item
pane.context.itemNotes=Item Notes
pane.context.allNotes=All Notes
pane.context.noNotes=No notes
pane.context.allNotes=Všechny poznámky
pane.context.noNotes=Žádné poznámky
itemTypes.note=Poznámka
itemTypes.annotation=Annotation
itemTypes.annotation=Anotace
itemTypes.attachment=Příloha
itemTypes.book=Kniha
itemTypes.bookSection=Kapitola knihy
@ -719,6 +738,7 @@ zotero.preferences.export.quickCopy.citationStyles=Citační styly
zotero.preferences.export.quickCopy.exportFormats=Formáty exportu
zotero.preferences.export.quickCopy.instructions=Rychlé kopírování vám umožní jednoduše kopírovat vybrané položky v daném formátu. Vybranou položku zkopírujete do schránky stisknutím %S nebo přetažením položek do textového pole v jiném programu.
zotero.preferences.export.quickCopy.citationInstructions=U citačních stylů můžete kopírovat citace nebo poznámky pod čarou stisknutím %S nebo podržením klávesy Shift před přetažením položek.
zotero.preferences.export.quickCopy.note.htmlOptions.label=Rich Text/HTML
zotero.preferences.wordProcessors.installationSuccess=Instalace proběhla úspěšně.
zotero.preferences.wordProcessors.installationError=Instalace nemohla být dokončena, protože došlo k chybě. Prosím ujistěte se, že je %1$S zavřený a poté restartujte %2$S.
@ -864,22 +884,7 @@ citation.showEditor=Zobrazit editor...
citation.hideEditor=Skrýt editor...
citation.citations=Citace
citation.notes=Poznámky
citation.locator.page=Stránka
citation.locator.book=Kniha
citation.locator.chapter=Kapitola
citation.locator.column=Sloupec
citation.locator.figure=Obrázek
citation.locator.folio=Složka
citation.locator.issue=Číslo
citation.locator.line=Řádek
citation.locator.note=Poznámka
citation.locator.opus=Opus
citation.locator.paragraph=Odstavec
citation.locator.part=Část
citation.locator.section=Sekce
citation.locator.subverbo=Pod heslem
citation.locator.volume=Ročník
citation.locator.verse=Verš
citation.locator.timestamp=Timestamp
report.title.default=Zotero report
report.parentItem=Rodičovská položka:
@ -1005,7 +1010,7 @@ sync.skipGroup=Přeskočit skupinu
sync.removeGroupsAndSync=Odstranit skupinu a synchronizovat
sync.reminder.setUp.message=Back up your library with %S syncing.
sync.reminder.setUp.action=Set Up Syncing
sync.reminder.setUp.action=Nastavení synchronizace
sync.reminder.autoSync.message=%S hasn’t synced in a while. Do you want to enable automatic syncing?
sync.error.usernameNotSet=Uživatelské jméno není nastaveno
@ -1218,6 +1223,8 @@ locate.externalViewer.label=Otevřít v externí aplikaci
locate.internalViewer.label=Otevřít v interní prohlížečce
locate.showFile.label=Zobrazit soubor
locate.libraryLookup.label=Hledat v knihovně
locate.libraryLookup.noResolver.title=No OpenURL Resolver
locate.libraryLookup.noResolver.text=You must choose an OpenURL resolver from the Advanced pane of the %S preferences.
locate.manageLocateEngines=Manage Lookup Engines...
locate.locateEngineDescription=Lookup engines help you find resources in your %S library on the web. Engines enabled in this list appear in the Locate drop-down in the toolbar.
@ -1293,9 +1300,9 @@ retraction.citationWarning=Ve vašem dokumentu byly odvoláno publikování cita
retraction.citationWarning.dontWarn=Varování k této položce mi příště nezobrazujte
noteEditor.annotationsDateLine=(%S)
noteEditor.cut=Cut
noteEditor.copy=Copy
noteEditor.paste=Paste
noteEditor.cut=Vyjmout
noteEditor.copy=Kopírovat
noteEditor.paste=Vložit
noteEditor.rightToLeft=Right to Left
noteEditor.leftToRight=Left to Right
noteEditor.updateNotice=This note was edited with a newer version of %1$S.\nPlease update %1$S to make changes.
@ -1303,21 +1310,22 @@ noteEditor.enterLink=Enter Link
noteEditor.heading1=Heading 1
noteEditor.heading2=Heading 2
noteEditor.heading3=Heading 3
noteEditor.paragraph=Paragraph
noteEditor.paragraph=Odstavec
noteEditor.monospaced=Monospaced
noteEditor.bulletList=Bulleted List
noteEditor.orderedList=Numbered List
noteEditor.blockquote=Block Quote
noteEditor.mathBlock=Math Block
noteEditor.formatText=Format Text
noteEditor.highlightText=Highlight Text
noteEditor.removeColor=Remove Color
noteEditor.bold=Bold
noteEditor.italic=Italic
noteEditor.underline=Underline
noteEditor.highlightText=Zvýraznit text
noteEditor.removeColor=Odstranit barvu
noteEditor.bold=Tučné
noteEditor.italic=Kurzíva
noteEditor.underline=Podtržené
noteEditor.strikethrough=Strikethrough
noteEditor.subscript=Subscript
noteEditor.superscript=Superscript
noteEditor.returnToNotesList=Return to Notes List
noteEditor.subscript=Spodní index
noteEditor.superscript=Horní index
noteEditor.returnToNotesList=Návrat do seznamu poznámek
noteEditor.insertLink=Insert Link
noteEditor.clearFormatting=Clear Formatting
noteEditor.align=Align
@ -1325,8 +1333,8 @@ noteEditor.alignLeft=Align Left
noteEditor.alignCenter=Align Center
noteEditor.alignRight=Align Right
noteEditor.insertCitation=Insert Citation
noteEditor.more=More
noteEditor.find=Find
noteEditor.more=Více
noteEditor.find=Najít
noteEditor.replace=Replace
noteEditor.previous=Previous
noteEditor.next=Next
@ -1338,16 +1346,20 @@ noteEditor.editCitation=Edit Citation
noteEditor.showOnPage=Show on Page
noteEditor.unlink=Unlink
noteEditor.set=Set
noteEditor.edit=Edit
noteEditor.edit=Upravit
noteEditor.addCitation=Add Citation
noteEditor.removeCitation=Hide Citation
noteEditor.findAndReplace=Find and Replace
noteEditor.editInWindow=Edit in a Separate Window
noteEditor.applyAnnotationColors=Show Annotation Colors
noteEditor.removeAnnotationColors=Hide Annotation Colors
noteEditor.applyAnnotationColors=Zobrazit barvu anotací
noteEditor.removeAnnotationColors=Skrýt barvu anotací
noteEditor.addCitations=Show Annotation Citations
noteEditor.removeCitations=Hide Annotation Citations
noteEditor.insertTable=Insert Table
noteEditor.image=Obrázek
noteEditor.math=Math
noteEditor.table=Table
noteEditor.copyImage=Copy Image
noteEditor.saveImageAs=Save Image As…
noteEditor.insertRowBefore=Insert Row Above
noteEditor.insertRowAfter=Insert Row Below
noteEditor.insertColumnBefore=Insert Column Left
@ -1356,37 +1368,46 @@ noteEditor.deleteRow=Delete Row
noteEditor.deleteColumn=Delete Column
noteEditor.deleteTable=Delete Table
pdfReader.annotations=Annotations
pdfReader.showAnnotations=Show Annotations
pdfReader.searchAnnotations=Search Annotations
pdfReader.annotations=Anotace
pdfReader.showAnnotations=Zobrazit anotace
pdfReader.searchAnnotations=Vyhledat anotace
pdfReader.noAnnotations=Create an annotation to see it in the sidebar
pdfReader.addText=Add extracted text
pdfReader.noExtractedText=No extracted text
pdfReader.addComment=Add comment
pdfReader.addTags=Add tags…
pdfReader.highlightText=Highlight Text
pdfReader.addNote=Add Note
pdfReader.highlightText=Zvýraznit text
pdfReader.addNote=Přidat poznámku
pdfReader.selectArea=Select Area
pdfReader.pickColor=Pick a Color
pdfReader.addToNote=Add to Note
pdfReader.addToNote=Přidat do poznámky
pdfReader.zoomIn=Zoom In
pdfReader.zoomOut=Zoom Out
pdfReader.zoomAuto=Automatically Resize
pdfReader.zoomPageWidth=Zoom to Page Width
pdfReader.zoomPageHeight=Zoom to Page Height
pdfReader.splitVertically=Split Vertically
pdfReader.splitHorizontally=Split Horizontally
pdfReader.nextPage=Next Page
pdfReader.previousPage=Previous Page
pdfReader.page=Page
pdfReader.page=Stránka
pdfReader.readOnly=Read-only
pdfReader.promptTransferFromPDF.title=Import Annotations
pdfReader.promptTransferFromPDF.title=Importovat anotace
pdfReader.promptTransferFromPDF.text=Annotations stored in the PDF file will be moved to %1$S.
pdfReader.promptTransferToPDF.title=Store Annotations in File
pdfReader.promptTransferToPDF.title=Uložit anotace v souboru
pdfReader.promptTransferToPDF.text=Annotations will be transferred to the PDF file and will no longer be editable in %S.
pdfReader.promptPasswordProtected=The operation is not supported for password-protected PDF files.
pdfReader.promptDeletePages.title=Delete Pages
pdfReader.promptDeletePages.text=Are you sure you want to delete %1$S page from the PDF file?;Are you sure you want to delete %1$S pages from the PDF file?
pdfReader.rotateLeft=Rotate Left
pdfReader.rotateRight=Rotate Right
pdfReader.rotate180=Rotate 180°
pdfReader.editPageNumber=Edit Page Number…
pdfReader.editHighlightedText=Edit Highlighted Text
pdfReader.copyImage=Copy Image
pdfReader.saveImageAs=Save Image As…
pdfReader.pageNumberPopupHeader=Change page number for:
pdfReader.thisAnnotation=This annotation
pdfReader.selectedAnnotations=Selected annotations
pdfReader.thisAnnotation=Tato anotace
pdfReader.selectedAnnotations=Vybrané anotace
pdfReader.thisPage=This page
pdfReader.thisPageAndLaterPages=This page and later pages
pdfReader.allPages=All pages

View file

@ -5,6 +5,12 @@
"general_done": {
"message": "Færdig"
},
"general_yes": {
"message": "Ja"
},
"general_no": {
"message": "Nej"
},
"general_tryAgain": {
"message": "Prøv igen"
},
@ -14,6 +20,9 @@
"general_cancel": {
"message": "Annullér"
},
"general_copyToClipboard": {
"message": "Kopiér til udklipsholderen"
},
"progressWindow_savingTo": {
"message": "Gemmer i"
},

View file

@ -6,14 +6,14 @@
<!ENTITY zotero.general.deselectAll "Fravælg alle">
<!ENTITY zotero.general.edit "Rediger">
<!ENTITY zotero.general.delete "Slet">
<!ENTITY zotero.general.moveToTrash "Move to Trash">
<!ENTITY zotero.general.moveToTrash "Flyt til papirkurv">
<!ENTITY zotero.general.ok "OK">
<!ENTITY zotero.general.cancel "Annullér">
<!ENTITY zotero.general.refresh "Opdatér">
<!ENTITY zotero.general.saveAs "Gem som...">
<!ENTITY zotero.general.options "Indstillinger">
<!ENTITY zotero.general.advancedOptions.label "Avancerede indstillinger">
<!ENTITY zotero.general.tools "Tools">
<!ENTITY zotero.general.tools "Værktøj">
<!ENTITY zotero.general.more "Mere">
<!ENTITY zotero.general.loading "Indlæser...">
<!ENTITY zotero.general.close "Luk">
@ -21,7 +21,7 @@
<!ENTITY zotero.general.other "Andet...">
<!ENTITY zotero.general.bigger "Bigger">
<!ENTITY zotero.general.smaller "Smaller">
<!ENTITY zotero.general.reset "Reset">
<!ENTITY zotero.general.reset "Nulstil">
<!ENTITY zotero.errorReport.title "Zotero fejlrapport">
@ -128,8 +128,8 @@
<!ENTITY zotero.toolbar.attachment.add "Gem kopi af filen...">
<!ENTITY zotero.toolbar.attachment.weblink "Gem link til den aktuelle side">
<!ENTITY zotero.toolbar.attachment.snapshot "Tag kopi af den aktuelle side">
<!ENTITY zotero.toolbar.context.item "Item">
<!ENTITY zotero.toolbar.context.notes "Notes">
<!ENTITY zotero.toolbar.context.item "Element">
<!ENTITY zotero.toolbar.context.notes "Noter">
<!ENTITY zotero.tagSelector.noTagsToDisplay "Der er ingen mærker at vise">
<!ENTITY zotero.tagSelector.loadingTags "Indlæser mærker...">
@ -150,7 +150,7 @@
<!ENTITY zotero.lookup.description "Enter ISBNs, DOIs, PMIDs, arXiv IDs, or ADS Bibcodes to add to your library:">
<!ENTITY zotero.lookup.button.search "Søg">
<!ENTITY zotero.createParent.title "Create Parent Item">
<!ENTITY zotero.createParent.title "Opret moder-element">
<!ENTITY zotero.createParent.button.manual "Manual Entry">
<!ENTITY zotero.selectitems.title "Vælg elementer">
@ -307,12 +307,10 @@
<!ENTITY zotero.context.addStandaloneNoteFromAnnotations "Add Standalone Note from Annotations">
<!ENTITY zotero.context.editInWindow "Edit in a Separate Window">
<!ENTITY zotero.pdfReader.firstPage "First Page">
<!ENTITY zotero.pdfReader.firstPage "Første side">
<!ENTITY zotero.pdfReader.lastPage "Last Page">
<!ENTITY zotero.pdfReader.back "Back">
<!ENTITY zotero.pdfReader.forward "Forward">
<!ENTITY zotero.pdfReader.rotateClockwise "Rotate Clockwise">
<!ENTITY zotero.pdfReader.rotateCounterclockwise "Rotate Counterclockwise">
<!ENTITY zotero.pdfReader.handTool "Hand Tool">
<!ENTITY zotero.pdfReader.verticalScrolling "Vertical Scrolling">
<!ENTITY zotero.pdfReader.horizontalScrolling "Horizontal Scrolling">
@ -325,4 +323,9 @@
<!ENTITY zotero.pdfReader.zoomAuto "Automatically Resize">
<!ENTITY zotero.pdfReader.zoomPageWidth "Zoom to Page Width">
<!ENTITY zotero.pdfReader.zoomPageHeight "Zoom to Page Height">
<!ENTITY zotero.pdfReader.splitVertically "Split Vertically">
<!ENTITY zotero.pdfReader.splitHorizontally "Split Horizontally">
<!ENTITY zotero.pdfReader.transferFromPDF "Import Annotations…">
<!ENTITY zotero.pdfReader.rotatePageLeft "Rotate Page Left">
<!ENTITY zotero.pdfReader.rotatePageRight "Rotate Page Right">
<!ENTITY zotero.pdfReader.rotatePage180 "Rotate Page 180°">

View file

@ -45,14 +45,14 @@ general.character.plural=tegn
general.create=Opret
general.delete=Slet
general.remove=Fjern
general.import=Import
general.export=Export
general.import=Importér
general.export=Eksportér
general.update=Update
general.moreInformation=Mere information
general.learnMore=Learn More
general.seeForMoreInformation=Se %S for nærmere oplysninger.
general.open=Åbn %S
general.close=Close
general.close=Luk
general.enable=Slå til
general.disable=Slå fra
general.reset=Nulstil
@ -71,9 +71,9 @@ general.fix=Løs...
general.tryAgain=Prøv igen
general.tryLater=Prøv igen senere
general.showDirectory=Vis mappe
general.showInLibrary=Show in Library
general.showInLibrary=Vis i bibliotek
general.continue=Fortsæt
general.copy=Copy
general.copy=Kopier
general.copyToClipboard=Kopiér til udklipsholderen
general.cancel=Annullér
general.clear=Ryd
@ -89,9 +89,10 @@ general.back=Back
general.languages=Languages
general.default=Default
general.custom=Custom
general.loading=Loading…
general.loading=Indlæser...
general.richText=Rich Text
general.clearSelection=Clear Selection
general.insert=Insert
general.yellow=Yellow
general.red=Red
@ -114,6 +115,7 @@ about.getInvolved=Vil du hjælpe? [Bliv involveret] i dag!
punctuation.openingQMark=“
punctuation.closingQMark=”
punctuation.colon=:
punctuation.colon.withString=%S:
punctuation.ellipsis=...
install.quickStartGuide=Kom hurtigt i gang med Zotero
@ -327,8 +329,11 @@ pane.items.remove.multiple=Er du sikker på, du vil slette de valgte elementer f
pane.items.removeFromPublications.title=Fjern fra Mine publikationer
pane.items.removeFromPublications=Er du sikker på, du vil slette det valgte element fra Mine publikationer?
pane.items.removeFromPublications.multiple=Er du sikker på, du vil slette det valgte element fra Mine publikationer?
pane.items.removeRecursive.title=Remove from Collection and Subcollections
pane.items.removeRecursive=Are you sure you want to remove the selected item from this collection and all subcollections?
pane.items.removeRecursive.multiple=Are you sure you want to remove the selected items from this collection and all subcollections?
pane.items.menu.addNoteFromAnnotations=Add Note from Annotations
pane.items.menu.addNoteFromAnnotations.multiple=Add Notes from Annotations
pane.items.menu.createNoteFromAnnotations=Create Note from Annotations
pane.items.menu.findAvailablePDF=Find tilgængelig PDF
pane.items.menu.findAvailablePDF.multiple=Find tilgængelige PDF'er
pane.items.menu.addToCollection=Add to Collection
@ -338,11 +343,13 @@ pane.items.menu.removeFromPublications=Fjern element fra Mine publikationer...
pane.items.menu.removeFromPublications.multiple=Fjern elementer fra Mine publikationer...
pane.items.menu.moveToTrash=Flyt element til papirkurven...
pane.items.menu.moveToTrash.multiple=Flyt elementer til papirkurven...
pane.items.menu.delete=Delete Permanently…
pane.items.menu.delete=Slet permanent...
pane.items.menu.export=Eksportér dette element...
pane.items.menu.export.multiple=Eksportér disse elementer...
pane.items.menu.exportNote=Export Note…
pane.items.menu.exportNote.multiple=Export Notes…
pane.items.menu.exportPDF=Export PDF…
pane.items.menu.exportPDF.multiple=Export PDFs…
pane.items.menu.createBib=Dan en referenceliste ud fra element...
pane.items.menu.createBib.multiple=Dan en referenceliste ud fra elementer...
pane.items.menu.generateReport=Opret rapport ud fra element...
@ -352,9 +359,11 @@ pane.items.menu.reindexItem.multiple=Indeksér elementer igen
pane.items.menu.recognizePDF=Hent metadata for pdf-filen.
pane.items.menu.recognizePDF.multiple=Hent metadata for pdf-filerne
pane.items.menu.createParent=Create Parent Item…
pane.items.menu.createParent.multiple=Create Parent Items
pane.items.menu.createParent.multiple=Opret moder-elementer
pane.items.menu.renameAttachments=Omdøb fil fra moder-metadata
pane.items.menu.renameAttachments.multiple=Omdøb filer fra moder-metadata
pane.items.menu.duplicateAndConvert.toBookSection=Create Book Section
pane.items.menu.duplicateAndConvert.toBook=Create Book from Book Section
pane.items.showItemInLibrary=Vis element i Bibliotek
pane.items.letter.oneParticipant=Brev til %S
@ -404,10 +413,20 @@ pane.item.attachments.rename.error=Der opstod en fejl under omdøbningen af file
pane.item.attachments.fileNotFound.title=Fil ikke fundet
pane.item.attachments.fileNotFound.text1=Den vedhæftede fil kunne ikke findes.
pane.item.attachments.fileNotFound.text1.path=The attached file could not be found at the following path:
pane.item.attachments.fileNotFound.text2.stored=It may have been moved or deleted outside of %1$S, or, if the file was added on another computer, it may not yet have been synced to or from %2$S.
pane.item.attachments.fileNotFound.text2.stored.notOnServer=It may have been moved or deleted outside of %1$S, or, if the file was added on another computer, it may not yet have been synced to %2$S.
pane.item.attachments.fileNotFound.text2.stored=Den kan være blevet slettet eller flyttet uden for %1$S eller, hvis filen blev tilføjet på en anden computer, er den måske endnu ikke blevet synkroniseret til eller fra %2$S.
pane.item.attachments.fileNotFound.text2.stored.notOnServer=Den kan være blevet slettet eller flyttet uden for %1$S eller, hvis filen blev tilføjet på en anden computer, er den måske endnu ikke blevet synkroniseret til eller fra %2$S.
pane.item.attachments.fileNotFound.text2.linked=It may have been moved or deleted outside of %1$S, or a Linked Attachment Base Directory may be set incorrectly on one of your computers.
pane.item.attachments.delete.confirm=Er du sikker på, du vil slette denne vedhæftning ?
pane.item.attachments.autoRelink.title=File Located Automatically
pane.item.attachments.autoRelink.text1=The file could not be found at the specified location, but a file with the same name was found within your Linked Attachment Base Directory:
pane.item.attachments.autoRelink.text2=Old Location: %S
pane.item.attachments.autoRelink.text3=New Location: %S
pane.item.attachments.autoRelink.text4=%1$S can automatically relink this attachment.
pane.item.attachments.autoRelink.relink=Relink
pane.item.attachments.autoRelinkOthers.title=Additional Files Located
pane.item.attachments.autoRelinkOthers.text=One other unlinked attachment in this library was found within the same directory. Relink this attachment as well?;%S other unlinked attachments in this library were found within the same directory. Relink all located attachments?
pane.item.attachments.autoRelink.locateManually=Locate Manually…
pane.item.attachments.autoRelink.relinkAll=Relink All
pane.item.attachments.count.zero=%S vedhæftninger:
pane.item.attachments.count.singular=%S vedhæftning:
pane.item.attachments.count.plural=%S vedhæftninger:
@ -430,7 +449,7 @@ pane.item.related.count.zero=%S relaterede:
pane.item.related.count.singular=%S relateret:
pane.item.related.count.plural=%S relaterede:
pane.item.parentItem=Overordnet element:
pane.item.viewOnline.tooltip=Go to this item online
pane.item.viewOnline.tooltip=Gå til dette element online
pane.context.noParent=No parent item
pane.context.itemNotes=Item Notes
@ -438,7 +457,7 @@ pane.context.allNotes=All Notes
pane.context.noNotes=No notes
itemTypes.note=Note
itemTypes.annotation=Annotation
itemTypes.annotation=Annotering
itemTypes.attachment=Vedhæftning
itemTypes.book=Bog
itemTypes.bookSection=Bidrag til bog
@ -719,6 +738,7 @@ zotero.preferences.export.quickCopy.citationStyles=Referenceformater
zotero.preferences.export.quickCopy.exportFormats=Eksport-formater
zotero.preferences.export.quickCopy.instructions=Med hurtigkopiering kan du eksportere elementer i et givent format. Du kan kopiere valgte elementer til udklipsholderen ved at trykke %S eller trække dem til en tekstboks i et andet program.
zotero.preferences.export.quickCopy.citationInstructions=I referenceformater kan du kopiere henvisninger og fodnoter ved at trykke %S eller holde Skift nede, før du trækker elementer.
zotero.preferences.export.quickCopy.note.htmlOptions.label=Rich Text/HTML
zotero.preferences.wordProcessors.installationSuccess=Installationen gennemført.
zotero.preferences.wordProcessors.installationError=Installationen kunne ikke gennemføres, fordi der opstod en fejl. Sikr dig venligst, at %1$S er lukket og genstart %2$S.
@ -864,22 +884,7 @@ citation.showEditor=Vis redaktør...
citation.hideEditor=Skjul redaktør...
citation.citations=Henvisninger
citation.notes=Noter
citation.locator.page=Side
citation.locator.book=Bog
citation.locator.chapter=Kapitel
citation.locator.column=Spalte
citation.locator.figure=Figur
citation.locator.folio=Folio
citation.locator.issue=Nummer
citation.locator.line=Linje
citation.locator.note=Note
citation.locator.opus=Værk
citation.locator.paragraph=Afsnit
citation.locator.part=Del
citation.locator.section=Afsnit
citation.locator.subverbo=Under ordet
citation.locator.volume=Bind
citation.locator.verse=Vers
citation.locator.timestamp=Timestamp
report.title.default=Zotero-rapport
report.parentItem=Overordnet element:
@ -1005,7 +1010,7 @@ sync.skipGroup=Spring gruppe over
sync.removeGroupsAndSync=Fjern grupper og synkronisering
sync.reminder.setUp.message=Back up your library with %S syncing.
sync.reminder.setUp.action=Set Up Syncing
sync.reminder.setUp.action=Indstil synkronisering
sync.reminder.autoSync.message=%S hasn’t synced in a while. Do you want to enable automatic syncing?
sync.error.usernameNotSet=Brugernavn ikke angivet
@ -1218,6 +1223,8 @@ locate.externalViewer.label=Åbn i ekstern fremviser
locate.internalViewer.label=Åbn i intern fremviser
locate.showFile.label=Vis fil
locate.libraryLookup.label=Opslag i bibliotekskatalogen
locate.libraryLookup.noResolver.title=No OpenURL Resolver
locate.libraryLookup.noResolver.text=You must choose an OpenURL resolver from the Advanced pane of the %S preferences.
locate.manageLocateEngines=Håndtér søgemaskiner...
locate.locateEngineDescription=Lookup engines help you find resources in your %S library on the web. Engines enabled in this list appear in the Locate drop-down in the toolbar.
@ -1293,9 +1300,9 @@ retraction.citationWarning=A citation in your document has been retracted:
retraction.citationWarning.dontWarn=Don’t warn me about this item again
noteEditor.annotationsDateLine=(%S)
noteEditor.cut=Cut
noteEditor.copy=Copy
noteEditor.paste=Paste
noteEditor.cut=Klip
noteEditor.copy=Kopier
noteEditor.paste=Indsæt
noteEditor.rightToLeft=Right to Left
noteEditor.leftToRight=Left to Right
noteEditor.updateNotice=This note was edited with a newer version of %1$S.\nPlease update %1$S to make changes.
@ -1303,20 +1310,21 @@ noteEditor.enterLink=Enter Link
noteEditor.heading1=Heading 1
noteEditor.heading2=Heading 2
noteEditor.heading3=Heading 3
noteEditor.paragraph=Paragraph
noteEditor.paragraph=Afsnit
noteEditor.monospaced=Monospaced
noteEditor.bulletList=Bulleted List
noteEditor.orderedList=Numbered List
noteEditor.blockquote=Block Quote
noteEditor.mathBlock=Math Block
noteEditor.formatText=Format Text
noteEditor.highlightText=Highlight Text
noteEditor.removeColor=Remove Color
noteEditor.bold=Bold
noteEditor.italic=Italic
noteEditor.underline=Underline
noteEditor.highlightText=Marker tekst
noteEditor.removeColor=Fjern farve
noteEditor.bold=Fed
noteEditor.italic=Kursiv
noteEditor.underline=Understreg
noteEditor.strikethrough=Strikethrough
noteEditor.subscript=Subscript
noteEditor.superscript=Superscript
noteEditor.subscript=Sænket skrift
noteEditor.superscript=Hævet skrift
noteEditor.returnToNotesList=Return to Notes List
noteEditor.insertLink=Insert Link
noteEditor.clearFormatting=Clear Formatting
@ -1325,7 +1333,7 @@ noteEditor.alignLeft=Align Left
noteEditor.alignCenter=Align Center
noteEditor.alignRight=Align Right
noteEditor.insertCitation=Insert Citation
noteEditor.more=More
noteEditor.more=Mere
noteEditor.find=Find
noteEditor.replace=Replace
noteEditor.previous=Previous
@ -1338,7 +1346,7 @@ noteEditor.editCitation=Edit Citation
noteEditor.showOnPage=Show on Page
noteEditor.unlink=Unlink
noteEditor.set=Set
noteEditor.edit=Edit
noteEditor.edit=Rediger
noteEditor.addCitation=Add Citation
noteEditor.removeCitation=Hide Citation
noteEditor.findAndReplace=Find and Replace
@ -1347,7 +1355,11 @@ noteEditor.applyAnnotationColors=Show Annotation Colors
noteEditor.removeAnnotationColors=Hide Annotation Colors
noteEditor.addCitations=Show Annotation Citations
noteEditor.removeCitations=Hide Annotation Citations
noteEditor.insertTable=Insert Table
noteEditor.image=Grafik
noteEditor.math=Math
noteEditor.table=Table
noteEditor.copyImage=Copy Image
noteEditor.saveImageAs=Save Image As…
noteEditor.insertRowBefore=Insert Row Above
noteEditor.insertRowAfter=Insert Row Below
noteEditor.insertColumnBefore=Insert Column Left
@ -1360,11 +1372,11 @@ pdfReader.annotations=Annotations
pdfReader.showAnnotations=Show Annotations
pdfReader.searchAnnotations=Search Annotations
pdfReader.noAnnotations=Create an annotation to see it in the sidebar
pdfReader.addText=Add extracted text
pdfReader.noExtractedText=No extracted text
pdfReader.addComment=Add comment
pdfReader.addTags=Add tags…
pdfReader.highlightText=Highlight Text
pdfReader.addNote=Add Note
pdfReader.highlightText=Marker tekst
pdfReader.addNote=Tilføj note
pdfReader.selectArea=Select Area
pdfReader.pickColor=Pick a Color
pdfReader.addToNote=Add to Note
@ -1373,17 +1385,26 @@ pdfReader.zoomOut=Zoom Out
pdfReader.zoomAuto=Automatically Resize
pdfReader.zoomPageWidth=Zoom to Page Width
pdfReader.zoomPageHeight=Zoom to Page Height
pdfReader.splitVertically=Split Vertically
pdfReader.splitHorizontally=Split Horizontally
pdfReader.nextPage=Next Page
pdfReader.previousPage=Previous Page
pdfReader.page=Page
pdfReader.page=Side
pdfReader.readOnly=Read-only
pdfReader.promptTransferFromPDF.title=Import Annotations
pdfReader.promptTransferFromPDF.text=Annotations stored in the PDF file will be moved to %1$S.
pdfReader.promptTransferToPDF.title=Store Annotations in File
pdfReader.promptTransferToPDF.text=Annotations will be transferred to the PDF file and will no longer be editable in %S.
pdfReader.promptPasswordProtected=The operation is not supported for password-protected PDF files.
pdfReader.promptDeletePages.title=Delete Pages
pdfReader.promptDeletePages.text=Are you sure you want to delete %1$S page from the PDF file?;Are you sure you want to delete %1$S pages from the PDF file?
pdfReader.rotateLeft=Rotate Left
pdfReader.rotateRight=Rotate Right
pdfReader.rotate180=Rotate 180°
pdfReader.editPageNumber=Edit Page Number…
pdfReader.editHighlightedText=Edit Highlighted Text
pdfReader.copyImage=Copy Image
pdfReader.saveImageAs=Save Image As…
pdfReader.pageNumberPopupHeader=Change page number for:
pdfReader.thisAnnotation=This annotation
pdfReader.selectedAnnotations=Selected annotations

View file

@ -301,18 +301,16 @@
<!ENTITY zotero.attachLink.label.link "Link:">
<!ENTITY zotero.attachLink.label.title "Titel:">
<!ENTITY zotero.context.addChildNote "Notiz zum Eintrag hinzufügen">
<!ENTITY zotero.context.addChildNoteFromAnnotations "Aus Annotationen Notiz zum Eintrag hinzufügen">
<!ENTITY zotero.context.addChildNote "Eintragsnotiz hinzufügen">
<!ENTITY zotero.context.addChildNoteFromAnnotations "Eintragsnotiz aus Anmerkungen hinzufügen">
<!ENTITY zotero.context.addStandaloneNote "Eigenständige Notiz hinzufügen">
<!ENTITY zotero.context.addStandaloneNoteFromAnnotations "Aus Annotationen eigenständige Notiz hinzufügen">
<!ENTITY zotero.context.addStandaloneNoteFromAnnotations "Eigenständige Notiz aus Anmerkungen hinzufügen">
<!ENTITY zotero.context.editInWindow "In einem neuen Fenster bearbeiten">
<!ENTITY zotero.pdfReader.firstPage "Erste Seite">
<!ENTITY zotero.pdfReader.lastPage "Letzte Seite">
<!ENTITY zotero.pdfReader.back "Zurück">
<!ENTITY zotero.pdfReader.forward "Vorwärts">
<!ENTITY zotero.pdfReader.rotateClockwise "Drehe im Uhrzeigersinn">
<!ENTITY zotero.pdfReader.rotateCounterclockwise "Drehe gegen den Uhrzeigersinn">
<!ENTITY zotero.pdfReader.handTool "Hand-Werkzeug">
<!ENTITY zotero.pdfReader.verticalScrolling "Vertikale Seitenanordnung">
<!ENTITY zotero.pdfReader.horizontalScrolling "Horizontale Seitenanordnung">
@ -325,4 +323,9 @@
<!ENTITY zotero.pdfReader.zoomAuto "Automatisch Größe anpassen">
<!ENTITY zotero.pdfReader.zoomPageWidth "Auf Fensterbreite skalieren">
<!ENTITY zotero.pdfReader.zoomPageHeight "Auf Fensterhöhe skalieren">
<!ENTITY zotero.pdfReader.splitVertically "Vertikal aufteilen">
<!ENTITY zotero.pdfReader.splitHorizontally "Horizontal aufteilen">
<!ENTITY zotero.pdfReader.transferFromPDF "Anmerkungen importieren…">
<!ENTITY zotero.pdfReader.rotatePageLeft "Seite links drehen">
<!ENTITY zotero.pdfReader.rotatePageRight "Seite rechts drehen">
<!ENTITY zotero.pdfReader.rotatePage180 "Seite 180° drehen">

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