Compare commits
209 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
72481b072e | ||
![]() |
cdee741a6d | ||
![]() |
68d03bc6c3 | ||
![]() |
0383f104dd | ||
![]() |
d38d55e2b4 | ||
![]() |
94a0c3ce8c | ||
![]() |
5ddbe433b9 | ||
![]() |
79d7c15d4f | ||
![]() |
d4c201fbc0 | ||
![]() |
d4097d90f6 | ||
![]() |
e67db436c6 | ||
![]() |
cfbb3d3d47 | ||
![]() |
bd5f2525dc | ||
![]() |
da5db4cb38 | ||
![]() |
2831e8be3e | ||
![]() |
8547dedca9 | ||
![]() |
46fc28dc16 | ||
![]() |
06a819bf34 | ||
![]() |
7f64c6803a | ||
![]() |
3c70dee5c1 | ||
![]() |
337144a5eb | ||
![]() |
7494e4d88c | ||
![]() |
2b49e94a61 | ||
![]() |
d0087d59d5 | ||
![]() |
854cb05883 | ||
![]() |
835bdf6941 | ||
![]() |
d98e89cf26 | ||
![]() |
4008848c64 | ||
![]() |
024b428fe6 | ||
![]() |
17f2025c53 | ||
![]() |
6ac65373a3 | ||
![]() |
2939b3ae95 | ||
![]() |
0f4e5ef508 | ||
![]() |
a894404ad3 | ||
![]() |
e46f1d06e0 | ||
![]() |
c5ba2e55f3 | ||
![]() |
f7e411d561 | ||
![]() |
603388c79d | ||
![]() |
547f1c29e3 | ||
![]() |
2dd0a560b7 | ||
![]() |
9220b2d9c2 | ||
![]() |
7271fdf6b7 | ||
![]() |
86b94ae713 | ||
![]() |
52737ec694 | ||
![]() |
6ff51103f5 | ||
![]() |
44fd598699 | ||
![]() |
a714f06670 | ||
![]() |
78e87a351c | ||
![]() |
3a3f46530d | ||
![]() |
3241faf503 | ||
![]() |
2f620c5f91 | ||
![]() |
763d7dec75 | ||
![]() |
4acd178819 | ||
![]() |
6960b7f86e | ||
![]() |
81ab8f7b20 | ||
![]() |
05843bb093 | ||
![]() |
5751838a3d | ||
![]() |
20dd81729b | ||
![]() |
3516598f4c | ||
![]() |
4a7aad03c4 | ||
![]() |
90677ae158 | ||
![]() |
23224f6093 | ||
![]() |
9165a0247f | ||
![]() |
e6dbd1ed92 | ||
![]() |
5a85d1ac9e | ||
![]() |
9ebe4037a4 | ||
![]() |
7dbfd5ce29 | ||
![]() |
040e0a726a | ||
![]() |
60e0d79e01 | ||
![]() |
609657a8e4 | ||
![]() |
c0a4fa43f0 | ||
![]() |
8f39e9cb36 | ||
![]() |
1ef2469b40 | ||
![]() |
5b595122b7 | ||
![]() |
b8e0c3f7e4 | ||
![]() |
81b876a5b2 | ||
![]() |
17b4c2e98e | ||
![]() |
6b4bce8754 | ||
![]() |
1649cae956 | ||
![]() |
41590741ba | ||
![]() |
a0d85c0673 | ||
![]() |
d0e42a1186 | ||
![]() |
c55b355548 | ||
![]() |
a4cd45ceb4 | ||
![]() |
6fb99d2d90 | ||
![]() |
6cf11f083b | ||
![]() |
ac24ffe636 | ||
![]() |
aad29a5469 | ||
![]() |
f69c7d2e2d | ||
![]() |
8b36f33513 | ||
![]() |
e31d706ee4 | ||
![]() |
d573a5b639 | ||
![]() |
390b8b2c86 | ||
![]() |
387109c1da | ||
![]() |
593153eebe | ||
![]() |
1c5e0475f5 | ||
![]() |
33f8fcfafb | ||
![]() |
5e5b567782 | ||
![]() |
7c093b4fb0 | ||
![]() |
00efd01aaf | ||
![]() |
6450d39933 | ||
![]() |
3bc08ba10c | ||
![]() |
0b4e1f9360 | ||
![]() |
b22e496b05 | ||
![]() |
16c2f6602a | ||
![]() |
653343149a | ||
![]() |
fa0576a4dd | ||
![]() |
8beda5eed4 | ||
![]() |
bf26c230be | ||
![]() |
8474c7b52a | ||
![]() |
3835bc9110 | ||
![]() |
8853f8ca47 | ||
![]() |
794d3880e7 | ||
![]() |
0cc3e64b8a | ||
![]() |
0e3071576b | ||
![]() |
460a423df3 | ||
![]() |
71f7ec5207 | ||
![]() |
ce834fc5f3 | ||
![]() |
94271325d7 | ||
![]() |
ceb9749937 | ||
![]() |
6f1833f936 | ||
![]() |
4248e59eeb | ||
![]() |
7da17ab63e | ||
![]() |
42bbcabdfe | ||
![]() |
ff2e8c9047 | ||
![]() |
029c9fc251 | ||
![]() |
ea8003c541 | ||
![]() |
9a304b6699 | ||
![]() |
ac4abf0ebb | ||
![]() |
b4244f8a25 | ||
![]() |
018ec79a74 | ||
![]() |
17f7bcbbad | ||
![]() |
eeadeaa1b1 | ||
![]() |
222bb5bad4 | ||
![]() |
7d424f6d12 | ||
![]() |
bddb5ed243 | ||
![]() |
ac180c2324 | ||
![]() |
21fadee7bb | ||
![]() |
d4fce3c855 | ||
![]() |
3b17681db3 | ||
![]() |
89d7dad126 | ||
![]() |
13f788f3f1 | ||
![]() |
be698e499f | ||
![]() |
4f9847da04 | ||
![]() |
a8d199967e | ||
![]() |
8d0dc359b4 | ||
![]() |
7f81e62bc8 | ||
![]() |
0b384abe66 | ||
![]() |
53df1b8dae | ||
![]() |
350b47364e | ||
![]() |
9e955bde99 | ||
![]() |
bc141ce36b | ||
![]() |
20df18636d | ||
![]() |
cd1c65a784 | ||
![]() |
37e850c67b | ||
![]() |
5a08d22dbf | ||
![]() |
fe5af63277 | ||
![]() |
52c1249523 | ||
![]() |
c5cd38b4a5 | ||
![]() |
9b9773db16 | ||
![]() |
a4aabd9f3e | ||
![]() |
00d85fb6da | ||
![]() |
0f6b712963 | ||
![]() |
46eb84e859 | ||
![]() |
38330e4c13 | ||
![]() |
bff7cee374 | ||
![]() |
9c7271c606 | ||
![]() |
9ae582e345 | ||
![]() |
292a033c1a | ||
![]() |
3c5bc44ddf | ||
![]() |
3db915516a | ||
![]() |
24b9612a56 | ||
![]() |
a9e047cc9e | ||
![]() |
6052f4c211 | ||
![]() |
4e977f91cc | ||
![]() |
b600885d11 | ||
![]() |
3445519714 | ||
![]() |
e746da4fab | ||
![]() |
32aebd388b | ||
![]() |
c96363746b | ||
![]() |
43692ee564 | ||
![]() |
39ed2217cd | ||
![]() |
2effad4f6a | ||
![]() |
b9837c690d | ||
![]() |
67b34b1482 | ||
![]() |
a673956787 | ||
![]() |
447659cdab | ||
![]() |
8782eaf507 | ||
![]() |
c71f657b08 | ||
![]() |
dabd7c1055 | ||
![]() |
3e39cb33d9 | ||
![]() |
d44cc05c3d | ||
![]() |
a12b997b8a | ||
![]() |
8d3e893aec | ||
![]() |
0bb2bdcbe3 | ||
![]() |
a659c6c6ff | ||
![]() |
749faabb46 | ||
![]() |
7b8f2e1dfe | ||
![]() |
cf12f1e9cc | ||
![]() |
da08d7f078 | ||
![]() |
06e17fd76d | ||
![]() |
d145cff000 | ||
![]() |
e3648dfda0 | ||
![]() |
25d8898ab0 | ||
![]() |
92346c8fb9 | ||
![]() |
6f6e18b406 | ||
![]() |
6c5aa06d6b | ||
![]() |
c04b85e746 | ||
![]() |
c89df69f14 |
6
.gitmodules
vendored
6
.gitmodules
vendored
|
@ -1,18 +1,24 @@
|
|||
[submodule "translators"]
|
||||
path = translators
|
||||
url = git://github.com/zotero/translators.git
|
||||
branch = master
|
||||
[submodule "chrome/content/zotero/locale/csl"]
|
||||
path = chrome/content/zotero/locale/csl
|
||||
url = git://github.com/citation-style-language/locales.git
|
||||
branch = master
|
||||
[submodule "styles"]
|
||||
path = styles
|
||||
url = git://github.com/zotero/bundled-styles.git
|
||||
branch = master
|
||||
[submodule "test/resource/chai"]
|
||||
path = test/resource/chai
|
||||
url = https://github.com/chaijs/chai.git
|
||||
branch = master
|
||||
[submodule "test/resource/mocha"]
|
||||
path = test/resource/mocha
|
||||
url = https://github.com/mochajs/mocha.git
|
||||
branch = master
|
||||
[submodule "test/resource/chai-as-promised"]
|
||||
path = test/resource/chai-as-promised
|
||||
url = https://github.com/domenic/chai-as-promised.git
|
||||
branch = master
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
## Bug Reports and Feature Requests
|
||||
|
||||
In order to keep product discussions open to as many people as possible, Zotero does not use GitHub Issues for bug reports or feature requests. Please use the [Zotero Forums](https://forums.zotero.org) to report problems and suggest changes.
|
||||
Zotero does not use GitHub Issues for bug reports or feature requests. Please post all such requests to the [Zotero Forums](https://forums.zotero.org), where Zotero developers and many others can help. Keeping product discussions in the Zotero Forums allows the entire Zotero community to participate, including domain experts that can address many questions better than Zotero developers.
|
||||
|
||||
For confirmed bugs or agreed-upon changes, new issues will be created in the relevant repositories on GitHub by Zotero developers.
|
||||
For confirmed bugs or agreed-upon changes, Zotero developers will create new issues in the relevant repositories.
|
||||
|
||||
## Working with Zotero Code
|
||||
|
||||
|
|
7
COPYING
7
COPYING
|
@ -1,8 +1,11 @@
|
|||
Zotero is Copyright © 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
|
||||
Zotero is Copyright © 2018 Corporation for Digital Scholarship,
|
||||
Vienna, Virginia, USA http://digitalscholar.org
|
||||
|
||||
Copyright © 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
|
||||
Roy Rosenzweig Center for History and New Media, George Mason University,
|
||||
Fairfax, Virginia, USA http://zotero.org
|
||||
|
||||
The Roy Rosenzweig Center for History and New Media distributes the Zotero source code
|
||||
The Corporation for Digital Scholarship distributes the Zotero source code
|
||||
under the GNU Affero General Public License, version 3 (AGPLv3). The full text
|
||||
of this license is given below.
|
||||
|
||||
|
|
|
@ -14,6 +14,10 @@ body[multiline="true"] {
|
|||
width: 800px;
|
||||
}
|
||||
|
||||
#quick-format-dialog.progress-bar #quick-format-deck {
|
||||
height: 37px;
|
||||
}
|
||||
|
||||
#quick-format-search {
|
||||
background: white;
|
||||
-moz-appearance: searchfield;
|
||||
|
|
|
@ -33,6 +33,6 @@ textbox
|
|||
|
||||
|
||||
/* BEGIN 2X BLOCK -- DO NOT EDIT MANUALLY -- USE 2XIZE */
|
||||
@media (min-resolution: 1.5dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.creator-type-label > image { list-style-image: url('chrome://zotero/skin/mac/arrow-down@2x.png'); }
|
||||
}
|
||||
|
|
|
@ -491,7 +491,7 @@ treechildren::-moz-tree-image {
|
|||
|
||||
|
||||
/* BEGIN 2X BLOCK -- DO NOT EDIT MANUALLY -- USE 2XIZE */
|
||||
@media (min-resolution: 1.5dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.zotero-tb-button,.zotero-tb-button:first-child,.zotero-tb-button:last-child { background: url("chrome://zotero/skin/mac/menubutton-end@2x.png") right center/auto 24px no-repeat; }
|
||||
.zotero-tb-button > .toolbarbutton-icon { background: url("chrome://zotero/skin/mac/menubutton-start@2x.png") left center/auto 24px no-repeat; }
|
||||
.zotero-tb-button:-moz-window-inactive > .toolbarbutton-icon { background: url("chrome://zotero/skin/mac/menubutton-start-inactive-window@2x.png") left center/auto 24px no-repeat; }
|
||||
|
|
|
@ -173,7 +173,7 @@ toolbar:not([id="nav-bar"]) #zotero-toolbar-buttons separator {
|
|||
}
|
||||
|
||||
|
||||
@media (min-resolution: 1.5dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#zotero-pane .toolbarbutton-icon {
|
||||
width: 16px;
|
||||
}
|
||||
|
|
|
@ -191,7 +191,7 @@ toolbar:not([id="nav-bar"]) #zotero-toolbar-buttons separator {
|
|||
/* End toolbar buttons */
|
||||
|
||||
|
||||
@media (min-resolution: 1.5dppx) {
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#zotero-toolbar .toolbarbutton-icon {
|
||||
width: 16px;
|
||||
}
|
||||
|
|
|
@ -147,26 +147,33 @@ var Zotero_File_Interface_Bibliography = new function() {
|
|||
let dialog = document.getElementById("zotero-doc-prefs-dialog");
|
||||
dialog.setAttribute('title', `${Zotero.clientName} - ${dialog.getAttribute('title')}`);
|
||||
|
||||
if(_io.fieldType == "Bookmark") document.getElementById("formatUsing").selectedIndex = 1;
|
||||
var formatOption = (_io.primaryFieldType == "ReferenceMark" ? "referenceMarks" : "fields");
|
||||
document.getElementById("fields").label =
|
||||
Zotero.getString("integration."+formatOption+".label");
|
||||
document.getElementById("fields-caption").textContent =
|
||||
Zotero.getString("integration."+formatOption+".caption");
|
||||
document.getElementById("fields-file-format-notice").textContent =
|
||||
Zotero.getString("integration."+formatOption+".fileFormatNotice");
|
||||
document.getElementById("bookmarks-file-format-notice").textContent =
|
||||
Zotero.getString("integration.fields.fileFormatNotice");
|
||||
|
||||
|
||||
if(_io.automaticJournalAbbreviations === undefined) {
|
||||
_io.automaticJournalAbbreviations = Zotero.Prefs.get("cite.automaticJournalAbbreviations");
|
||||
if (document.getElementById("formatUsing-groupbox")) {
|
||||
if (["Field", "ReferenceMark"].includes(_io.primaryFieldType)) {
|
||||
if(_io.fieldType == "Bookmark") document.getElementById("formatUsing").selectedIndex = 1;
|
||||
var formatOption = (_io.primaryFieldType == "ReferenceMark" ? "referenceMarks" : "fields");
|
||||
document.getElementById("fields").label =
|
||||
Zotero.getString("integration."+formatOption+".label");
|
||||
document.getElementById("fields-caption").textContent =
|
||||
Zotero.getString("integration."+formatOption+".caption");
|
||||
document.getElementById("fields-file-format-notice").textContent =
|
||||
Zotero.getString("integration."+formatOption+".fileFormatNotice");
|
||||
document.getElementById("bookmarks-file-format-notice").textContent =
|
||||
Zotero.getString("integration.fields.fileFormatNotice");
|
||||
} else {
|
||||
document.getElementById("formatUsing-groupbox").style.display = "none";
|
||||
_io.fieldType = _io.primaryFieldType;
|
||||
}
|
||||
}
|
||||
if(_io.automaticJournalAbbreviations) {
|
||||
document.getElementById("automaticJournalAbbreviations-checkbox").checked = true;
|
||||
if(document.getElementById("automaticJournalAbbreviations-checkbox")) {
|
||||
if(_io.automaticJournalAbbreviations === undefined) {
|
||||
_io.automaticJournalAbbreviations = Zotero.Prefs.get("cite.automaticJournalAbbreviations");
|
||||
}
|
||||
if(_io.automaticJournalAbbreviations) {
|
||||
document.getElementById("automaticJournalAbbreviations-checkbox").checked = true;
|
||||
}
|
||||
|
||||
document.getElementById("automaticCitationUpdates-checkbox").checked = !_io.delayCitationUpdates;
|
||||
}
|
||||
|
||||
document.getElementById("automaticCitationUpdates-checkbox").checked = !_io.delayCitationUpdates;
|
||||
}
|
||||
|
||||
// set style to false, in case this is cancelled
|
||||
|
@ -204,7 +211,8 @@ var Zotero_File_Interface_Bibliography = new function() {
|
|||
if (isDocPrefs) {
|
||||
// update status of displayAs box based on style class
|
||||
var isNote = selectedStyleObj.class == "note";
|
||||
document.getElementById("displayAs-groupbox").hidden = !isNote;
|
||||
var multipleNotesSupported = _io.supportedNotes.length > 1;
|
||||
document.getElementById("displayAs-groupbox").hidden = !isNote || !multipleNotesSupported;
|
||||
|
||||
// update status of formatUsing box based on style class
|
||||
if(isNote) document.getElementById("formatUsing").selectedIndex = 0;
|
||||
|
|
|
@ -1087,12 +1087,17 @@
|
|||
<method name="changeTypeTo">
|
||||
<parameter name="itemTypeID"/>
|
||||
<parameter name="menu"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
<body><![CDATA[
|
||||
return (async function () {
|
||||
if (itemTypeID == this.item.itemTypeID) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.saveOnEdit) {
|
||||
await this.blurOpenField();
|
||||
await this.item.saveTx();
|
||||
}
|
||||
|
||||
var fieldsToDelete = this.item.getFieldsNotInType(itemTypeID, true);
|
||||
|
||||
// Special cases handled below
|
||||
|
@ -1149,15 +1154,15 @@
|
|||
|
||||
if (this.saveOnEdit) {
|
||||
// See note in transformText()
|
||||
this.blurOpenField().then(() => this.item.saveTx());
|
||||
await this.blurOpenField();
|
||||
await this.item.saveTx();
|
||||
}
|
||||
else {
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
if (this.eventHandlers['itemtypechange'] && this.eventHandlers['itemtypechange'].length) {
|
||||
var self = this;
|
||||
this.eventHandlers['itemtypechange'].forEach(function (f) f.bind(self)());
|
||||
this.eventHandlers['itemtypechange'].forEach(f => f.bind(this)());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -1169,8 +1174,8 @@
|
|||
}
|
||||
|
||||
return false;
|
||||
]]>
|
||||
</body>
|
||||
}.bind(this))();
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
|
||||
|
@ -1269,6 +1274,7 @@
|
|||
var valueElement = document.createElement("label");
|
||||
}
|
||||
|
||||
valueElement.setAttribute('id', `itembox-field-value-${fieldName}`);
|
||||
valueElement.setAttribute('fieldname', fieldName);
|
||||
valueElement.setAttribute('flex', 1);
|
||||
|
||||
|
@ -1308,7 +1314,10 @@
|
|||
if (date) {
|
||||
// If no time, interpret as local, not UTC
|
||||
if (Zotero.Date.isSQLDate(valueText)) {
|
||||
date = Zotero.Date.sqlToDate(valueText);
|
||||
// Add time to avoid showing previous day if date is in
|
||||
// DST (including the current date at 00:00:00) and we're
|
||||
// in standard time
|
||||
date = Zotero.Date.sqlToDate(valueText + ' 12:00:00');
|
||||
valueText = date.toLocaleDateString();
|
||||
}
|
||||
else {
|
||||
|
@ -1421,6 +1430,7 @@
|
|||
return (async function () {
|
||||
Zotero.debug(`Showing editor for ${elem.getAttribute('fieldname')}`);
|
||||
|
||||
var label = Zotero.getAncestorByTagName(elem, 'row').querySelector('label');
|
||||
var lastTabIndex = this._lastTabIndex = parseInt(elem.getAttribute('ztabindex'));
|
||||
|
||||
// If a field is open, hide it before selecting the new field, which might
|
||||
|
@ -1485,6 +1495,7 @@
|
|||
}
|
||||
|
||||
var t = document.createElement("textbox");
|
||||
t.setAttribute('id', `itembox-field-textbox-${fieldName}`);
|
||||
t.setAttribute('value', value);
|
||||
t.setAttribute('fieldname', fieldName);
|
||||
t.setAttribute('ztabindex', tabindex);
|
||||
|
@ -1541,6 +1552,9 @@
|
|||
var box = elem.parentNode;
|
||||
box.replaceChild(t, elem);
|
||||
|
||||
// Associate textbox with label
|
||||
label.setAttribute('control', t.getAttribute('id'));
|
||||
|
||||
// Prevent error when clicking between a changed field
|
||||
// and another -- there's probably a better way
|
||||
if (!t.select) {
|
||||
|
@ -1614,8 +1628,8 @@
|
|||
textbox.getAttribute('fieldname').split('-');
|
||||
|
||||
if (stayFocused) {
|
||||
this._lastTabIndex = parseInt(textbox.getAttribute('ztabindex')) - 1;
|
||||
this._tabDirection = 1;
|
||||
this._lastTabIndex = parseInt(textbox.getAttribute('ztabindex'));
|
||||
this._tabDirection = false;
|
||||
}
|
||||
|
||||
var creator = Zotero.Creators.get(creatorID);
|
||||
|
@ -1767,6 +1781,7 @@
|
|||
return (async function () {
|
||||
Zotero.debug(`Hiding editor for ${textbox.getAttribute('fieldname')}`);
|
||||
|
||||
var label = Zotero.getAncestorByTagName(textbox, 'row').querySelector('label');
|
||||
this._lastTabIndex = -1;
|
||||
|
||||
// Prevent autocomplete breakage in Firefox 3
|
||||
|
@ -1957,6 +1972,9 @@
|
|||
var box = textbox.parentNode;
|
||||
box.replaceChild(elem, textbox);
|
||||
|
||||
// Disassociate textbox from label
|
||||
label.setAttribute('control', elem.getAttribute('id'));
|
||||
|
||||
if (this.saveOnEdit) {
|
||||
await this.item.saveTx();
|
||||
}
|
||||
|
|
|
@ -152,28 +152,7 @@
|
|||
let id = relatedItem.id;
|
||||
let icon = document.createElement("image");
|
||||
icon.className = "zotero-box-icon";
|
||||
let type = Zotero.ItemTypes.getName(relatedItem.itemTypeID);
|
||||
if (type=='attachment')
|
||||
{
|
||||
switch (relatedItem.attaachmentLinkMode) {
|
||||
case Zotero.Attachments.LINK_MODE_LINKED_URL:
|
||||
type += '-web-link';
|
||||
break;
|
||||
|
||||
case Zotero.Attachments.LINK_MODE_IMPORTED_URL:
|
||||
type += '-snapshot';
|
||||
break;
|
||||
|
||||
case Zotero.Attachments.LINK_MODE_LINKED_FILE:
|
||||
type += '-link';
|
||||
break;
|
||||
|
||||
case Zotero.Attachments.LINK_MODE_IMPORTED_FILE:
|
||||
type += '-file';
|
||||
break;
|
||||
}
|
||||
}
|
||||
icon.setAttribute('src','chrome://zotero/skin/treeitem-' + type + '.png');
|
||||
icon.setAttribute('src', relatedItem.getImageSrc());
|
||||
|
||||
var label = document.createElement("label");
|
||||
label.className = "zotero-box-label";
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
Components.utils.import("resource://gre/modules/osfile.jsm")
|
||||
|
||||
/****Zotero_File_Exporter****
|
||||
**
|
||||
* A class to handle exporting of items, collections, or the entire library
|
||||
|
@ -206,13 +208,119 @@ var Zotero_File_Interface = new function() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
this.getMendeleyDirectory = function () {
|
||||
Components.classes["@mozilla.org/net/osfileconstantsservice;1"]
|
||||
.getService(Components.interfaces.nsIOSFileConstantsService)
|
||||
.init();
|
||||
var path = OS.Constants.Path.homeDir;
|
||||
if (Zotero.isMac) {
|
||||
path = OS.Path.join(path, 'Library', 'Application Support', 'Mendeley Desktop');
|
||||
}
|
||||
else if (Zotero.isWin) {
|
||||
path = OS.Path.join(path, 'AppData', 'Local', 'Mendeley Ltd', 'Mendeley Desktop');
|
||||
}
|
||||
else if (Zotero.isLinux) {
|
||||
path = OS.Path.join(path, '.local', 'share', 'data', 'Mendeley Ltd.', 'Mendeley Desktop');
|
||||
}
|
||||
else {
|
||||
throw new Error("Invalid platform");
|
||||
}
|
||||
return path;
|
||||
};
|
||||
|
||||
|
||||
this.findMendeleyDatabases = async function () {
|
||||
var dbs = [];
|
||||
try {
|
||||
var dir = this.getMendeleyDirectory();
|
||||
if (!await OS.File.exists(dir)) {
|
||||
Zotero.debug(`${dir} does not exist`);
|
||||
return dbs;
|
||||
}
|
||||
await Zotero.File.iterateDirectory(dir, function* (iterator) {
|
||||
while (true) {
|
||||
let entry = yield iterator.next();
|
||||
if (entry.isDir) continue;
|
||||
// online.sqlite, counterintuitively, is the default database before you sign in
|
||||
if (entry.name == 'online.sqlite' || entry.name.endsWith('@www.mendeley.com.sqlite')) {
|
||||
dbs.push({
|
||||
name: entry.name,
|
||||
path: entry.path,
|
||||
lastModified: null,
|
||||
size: null
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
for (let i = 0; i < dbs.length; i++) {
|
||||
let dbPath = OS.Path.join(dir, dbs[i].name);
|
||||
let info = await OS.File.stat(dbPath);
|
||||
dbs[i].size = info.size;
|
||||
dbs[i].lastModified = info.lastModificationDate;
|
||||
}
|
||||
dbs.sort((a, b) => {
|
||||
return b.lastModified - a.lastModified;
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
}
|
||||
return dbs;
|
||||
};
|
||||
|
||||
|
||||
this.showImportWizard = function () {
|
||||
var libraryID = Zotero.Libraries.userLibraryID;
|
||||
try {
|
||||
let zp = Zotero.getActiveZoteroPane();
|
||||
libraryID = zp.getSelectedLibraryID();
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
}
|
||||
var args = {
|
||||
libraryID
|
||||
};
|
||||
args.wrappedJSObject = args;
|
||||
|
||||
Services.ww.openWindow(null, "chrome://zotero/content/import/importWizard.xul",
|
||||
"importFile", "chrome,dialog=yes,centerscreen,width=600,height=400", args);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates Zotero.Translate instance and shows file picker for file import
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {nsIFile|string|null} [options.file=null] - File to import, or none to show a filepicker
|
||||
* @param {Boolean} [options.addToLibraryRoot=false]
|
||||
* @param {Boolean} [options.createNewCollection=true] - Put items in a new collection
|
||||
* @param {Function} [options.onBeforeImport] - Callback to receive translation object, useful
|
||||
* for displaying progress in a different way. This also causes an error to be throw
|
||||
* instead of shown in the main window.
|
||||
*/
|
||||
this.importFile = Zotero.Promise.coroutine(function* (file, createNewCollection) {
|
||||
if(createNewCollection === undefined) {
|
||||
this.importFile = Zotero.Promise.coroutine(function* (options = {}) {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
if (typeof options == 'string' || options instanceof Components.interfaces.nsIFile) {
|
||||
Zotero.debug("WARNING: importFile() now takes a single options object -- update your code");
|
||||
options = {
|
||||
file: options,
|
||||
createNewCollection: arguments[1]
|
||||
};
|
||||
}
|
||||
|
||||
var file = options.file ? Zotero.File.pathToFile(options.file) : null;
|
||||
var createNewCollection = options.createNewCollection;
|
||||
var addToLibraryRoot = options.addToLibraryRoot;
|
||||
var onBeforeImport = options.onBeforeImport;
|
||||
|
||||
if (createNewCollection === undefined && !addToLibraryRoot) {
|
||||
createNewCollection = true;
|
||||
} else if(!createNewCollection) {
|
||||
}
|
||||
else if (!createNewCollection) {
|
||||
try {
|
||||
if (!ZoteroPane.collectionsView.editable) {
|
||||
ZoteroPane.collectionsView.selectLibrary(null);
|
||||
|
@ -220,32 +328,40 @@ var Zotero_File_Interface = new function() {
|
|||
} catch(e) {}
|
||||
}
|
||||
|
||||
var translation = new Zotero.Translate.Import();
|
||||
if (!file) {
|
||||
let translators = yield translation.getTranslators();
|
||||
const nsIFilePicker = Components.interfaces.nsIFilePicker;
|
||||
var fp = Components.classes["@mozilla.org/filepicker;1"]
|
||||
.createInstance(nsIFilePicker);
|
||||
fp.init(window, Zotero.getString("fileInterface.import"), nsIFilePicker.modeOpen);
|
||||
|
||||
fp.appendFilters(nsIFilePicker.filterAll);
|
||||
|
||||
var collation = Zotero.getLocaleCollation();
|
||||
translators.sort((a, b) => collation.compareString(1, a.label, b.label))
|
||||
for (let translator of translators) {
|
||||
fp.appendFilter(translator.label, "*." + translator.target);
|
||||
}
|
||||
|
||||
var rv = fp.show();
|
||||
if (rv !== nsIFilePicker.returnOK && rv !== nsIFilePicker.returnReplace) {
|
||||
return false;
|
||||
}
|
||||
|
||||
file = fp.file;
|
||||
var defaultNewCollectionPrefix = Zotero.getString("fileInterface.imported");
|
||||
|
||||
var translation;
|
||||
// Check if the file is an SQLite database
|
||||
var sample = yield Zotero.File.getSample(file.path);
|
||||
if (file.path == Zotero.DataDirectory.getDatabase()) {
|
||||
// Blacklist the current Zotero database, which would cause a hang
|
||||
}
|
||||
else if (Zotero.MIME.sniffForMIMEType(sample) == 'application/x-sqlite3') {
|
||||
// Mendeley import doesn't use the real translation architecture, but we create a
|
||||
// translation object with the same interface
|
||||
translation = yield _getMendeleyTranslation();
|
||||
translation.createNewCollection = createNewCollection;
|
||||
defaultNewCollectionPrefix = Zotero.getString(
|
||||
'fileInterface.appImportCollection', 'Mendeley'
|
||||
);
|
||||
}
|
||||
else if (file.path.endsWith('@www.mendeley.com.sqlite')
|
||||
|| file.path.endsWith('online.sqlite')) {
|
||||
// Keep in sync with importWizard.js
|
||||
throw new Error('Encrypted Mendeley database');
|
||||
}
|
||||
|
||||
if (!translation) {
|
||||
translation = new Zotero.Translate.Import();
|
||||
}
|
||||
|
||||
translation.setLocation(file);
|
||||
yield _finishImport(translation, createNewCollection);
|
||||
return _finishImport({
|
||||
translation,
|
||||
createNewCollection,
|
||||
addToLibraryRoot,
|
||||
defaultNewCollectionPrefix,
|
||||
onBeforeImport
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
@ -273,7 +389,10 @@ var Zotero_File_Interface = new function() {
|
|||
}
|
||||
} catch(e) {}
|
||||
|
||||
yield _finishImport(translation, false);
|
||||
yield _finishImport({
|
||||
translation,
|
||||
createNewCollection: false
|
||||
});
|
||||
|
||||
// Select imported items
|
||||
try {
|
||||
|
@ -287,17 +406,36 @@ var Zotero_File_Interface = new function() {
|
|||
});
|
||||
|
||||
|
||||
var _finishImport = Zotero.Promise.coroutine(function* (translation, createNewCollection) {
|
||||
var _finishImport = Zotero.Promise.coroutine(function* (options) {
|
||||
var t = performance.now();
|
||||
|
||||
var translation = options.translation;
|
||||
var addToLibraryRoot = options.addToLibraryRoot;
|
||||
var createNewCollection = options.createNewCollection;
|
||||
var defaultNewCollectionPrefix = options.defaultNewCollectionPrefix;
|
||||
var onBeforeImport = options.onBeforeImport;
|
||||
|
||||
if (addToLibraryRoot && createNewCollection) {
|
||||
throw new Error("Can't add to library root and create new collection");
|
||||
}
|
||||
|
||||
var showProgressWindow = !onBeforeImport;
|
||||
|
||||
let translators = yield translation.getTranslators();
|
||||
|
||||
if(!translators.length) {
|
||||
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
||||
.getService(Components.interfaces.nsIPromptService);
|
||||
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_OK)
|
||||
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_IS_STRING);
|
||||
var index = ps.confirmEx(
|
||||
|
||||
// Unrecognized file
|
||||
if (!translators.length) {
|
||||
if (onBeforeImport) {
|
||||
yield onBeforeImport(false);
|
||||
}
|
||||
|
||||
let ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
||||
.getService(Components.interfaces.nsIPromptService);
|
||||
let buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_OK
|
||||
+ ps.BUTTON_POS_1 * ps.BUTTON_TITLE_IS_STRING;
|
||||
let index = ps.confirmEx(
|
||||
null,
|
||||
"",
|
||||
Zotero.getString('general.error'),
|
||||
Zotero.getString("fileInterface.unsupportedFormat"),
|
||||
buttonFlags,
|
||||
null,
|
||||
|
@ -305,17 +443,27 @@ var Zotero_File_Interface = new function() {
|
|||
null, null, {}
|
||||
);
|
||||
if (index == 1) {
|
||||
ZoteroPane_Local.loadURI("http://zotero.org/support/kb/importing");
|
||||
Zotero.launchURL("https://www.zotero.org/support/kb/importing");
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
let importCollection = null, libraryID = Zotero.Libraries.userLibraryID;
|
||||
|
||||
var libraryID = Zotero.Libraries.userLibraryID;
|
||||
var importCollection = null;
|
||||
try {
|
||||
libraryID = ZoteroPane.getSelectedLibraryID();
|
||||
importCollection = ZoteroPane.getSelectedCollection();
|
||||
} catch(e) {}
|
||||
|
||||
let zp = Zotero.getActiveZoteroPane();
|
||||
libraryID = zp.getSelectedLibraryID();
|
||||
if (addToLibraryRoot) {
|
||||
yield zp.collectionsView.selectLibrary(libraryID);
|
||||
}
|
||||
else if (!createNewCollection) {
|
||||
importCollection = zp.getSelectedCollection();
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
}
|
||||
|
||||
if(createNewCollection) {
|
||||
// Create a new collection to take imported items
|
||||
let collectionName;
|
||||
|
@ -330,8 +478,9 @@ var Zotero_File_Interface = new function() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
collectionName = Zotero.getString("fileInterface.imported")+" "+(new Date()).toLocaleString();
|
||||
}
|
||||
else {
|
||||
collectionName = defaultNewCollectionPrefix + " " + (new Date()).toLocaleString();
|
||||
}
|
||||
importCollection = new Zotero.Collection;
|
||||
importCollection.libraryID = libraryID;
|
||||
|
@ -342,22 +491,29 @@ var Zotero_File_Interface = new function() {
|
|||
translation.setTranslator(translators[0]);
|
||||
|
||||
// Show progress popup
|
||||
var progressWin = new Zotero.ProgressWindow({
|
||||
closeOnClick: false
|
||||
});
|
||||
progressWin.changeHeadline(Zotero.getString('fileInterface.importing'));
|
||||
var icon = 'chrome://zotero/skin/treesource-unfiled' + (Zotero.hiDPI ? "@2x" : "") + '.png';
|
||||
let progress = new progressWin.ItemProgress(
|
||||
icon, translation.path ? OS.Path.basename(translation.path) : translators[0].label
|
||||
);
|
||||
progressWin.show();
|
||||
var progressWin;
|
||||
var progress;
|
||||
if (showProgressWindow) {
|
||||
progressWin = new Zotero.ProgressWindow({
|
||||
closeOnClick: false
|
||||
});
|
||||
progressWin.changeHeadline(Zotero.getString('fileInterface.importing'));
|
||||
let icon = 'chrome://zotero/skin/treesource-unfiled' + (Zotero.hiDPI ? "@2x" : "") + '.png';
|
||||
progress = new progressWin.ItemProgress(
|
||||
icon, translation.path ? OS.Path.basename(translation.path) : translators[0].label
|
||||
);
|
||||
progressWin.show();
|
||||
|
||||
translation.setHandler("itemDone", function () {
|
||||
progress.setProgress(translation.getProgress());
|
||||
});
|
||||
|
||||
yield Zotero.Promise.delay(0);
|
||||
}
|
||||
else {
|
||||
yield onBeforeImport(translation);
|
||||
}
|
||||
|
||||
translation.setHandler("itemDone", function () {
|
||||
progress.setProgress(translation.getProgress());
|
||||
});
|
||||
|
||||
yield Zotero.Promise.delay(0);
|
||||
|
||||
let failed = false;
|
||||
try {
|
||||
yield translation.translate({
|
||||
|
@ -365,6 +521,10 @@ var Zotero_File_Interface = new function() {
|
|||
collections: importCollection ? [importCollection.id] : null
|
||||
});
|
||||
} catch(e) {
|
||||
if (!showProgressWindow) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
progressWin.close();
|
||||
Zotero.logError(e);
|
||||
Zotero.alert(
|
||||
|
@ -372,26 +532,62 @@ var Zotero_File_Interface = new function() {
|
|||
Zotero.getString('general.error'),
|
||||
Zotero.getString("fileInterface.importError")
|
||||
);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Show popup on completion
|
||||
var numItems = translation.newItems.length;
|
||||
progressWin.changeHeadline(Zotero.getString('fileInterface.importComplete'));
|
||||
if (numItems == 1) {
|
||||
var icon = translation.newItems[0].getImageSrc();
|
||||
|
||||
// Show popup on completion
|
||||
if (showProgressWindow) {
|
||||
progressWin.changeHeadline(Zotero.getString('fileInterface.importComplete'));
|
||||
let icon;
|
||||
if (numItems == 1) {
|
||||
icon = translation.newItems[0].getImageSrc();
|
||||
}
|
||||
else {
|
||||
icon = 'chrome://zotero/skin/treesource-unfiled' + (Zotero.hiDPI ? "@2x" : "") + '.png';
|
||||
}
|
||||
let text = Zotero.getString(`fileInterface.itemsWereImported`, numItems, numItems);
|
||||
progress.setIcon(icon);
|
||||
progress.setText(text);
|
||||
// For synchronous translators, which don't update progress
|
||||
progress.setProgress(100);
|
||||
progressWin.startCloseTimer(5000);
|
||||
}
|
||||
else {
|
||||
var icon = 'chrome://zotero/skin/treesource-unfiled' + (Zotero.hiDPI ? "@2x" : "") + '.png';
|
||||
}
|
||||
var text = Zotero.getString(`fileInterface.itemsWereImported`, numItems, numItems);
|
||||
progress.setIcon(icon);
|
||||
progress.setText(text);
|
||||
// For synchronous translators, which don't update progress
|
||||
progress.setProgress(100);
|
||||
progressWin.startCloseTimer(5000);
|
||||
|
||||
Zotero.debug(`Imported ${numItems} item(s) in ${performance.now() - t} ms`);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
var _getMendeleyTranslation = async function () {
|
||||
if (true) {
|
||||
Components.utils.import("chrome://zotero/content/import/mendeley/mendeleyImport.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', 'mendeleyImport.js'
|
||||
);
|
||||
let fileURI = OS.Path.toFileURI(file);
|
||||
let xmlhttp = await Zotero.HTTP.request(
|
||||
'GET',
|
||||
fileURI,
|
||||
{
|
||||
dontCache: true,
|
||||
responseType: 'text'
|
||||
}
|
||||
);
|
||||
eval(xmlhttp.response);
|
||||
}
|
||||
return new Zotero_Import_Mendeley();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a bibliography from a collection or saved search
|
||||
*/
|
||||
|
|
332
chrome/content/zotero/import/importWizard.js
Normal file
332
chrome/content/zotero/import/importWizard.js
Normal file
|
@ -0,0 +1,332 @@
|
|||
var Zotero_Import_Wizard = {
|
||||
_wizard: null,
|
||||
_dbs: null,
|
||||
_file: null,
|
||||
_translation: null,
|
||||
|
||||
|
||||
init: async function () {
|
||||
this._wizard = document.getElementById('import-wizard');
|
||||
|
||||
var dbs = await Zotero_File_Interface.findMendeleyDatabases();
|
||||
if (dbs.length) {
|
||||
document.getElementById('radio-import-source-mendeley').hidden = false;
|
||||
}
|
||||
|
||||
// If no existing collections or non-trash items in the library, don't create a new
|
||||
// collection by default
|
||||
var args = window.arguments[0].wrappedJSObject;
|
||||
if (args && args.libraryID) {
|
||||
let sql = "SELECT ROWID FROM collections WHERE libraryID=?1 "
|
||||
+ "UNION "
|
||||
+ "SELECT ROWID FROM items WHERE libraryID=?1 "
|
||||
// Not in trash
|
||||
+ "AND itemID NOT IN (SELECT itemID FROM deletedItems) "
|
||||
// And not a child item (which doesn't necessarily show up in the trash)
|
||||
+ "AND itemID NOT IN (SELECT itemID FROM itemNotes WHERE parentItemID IS NOT NULL) "
|
||||
+ "AND itemID NOT IN (SELECT itemID FROM itemAttachments WHERE parentItemID IS NOT NULL) "
|
||||
+ "LIMIT 1";
|
||||
if (!await Zotero.DB.valueQueryAsync(sql, args.libraryID)) {
|
||||
document.getElementById('create-collection-checkbox').removeAttribute('checked');
|
||||
}
|
||||
}
|
||||
|
||||
Zotero.Translators.init(); // async
|
||||
},
|
||||
|
||||
|
||||
onModeChosen: async function () {
|
||||
var wizard = this._wizard;
|
||||
|
||||
var mode = document.getElementById('import-source').selectedItem.id;
|
||||
try {
|
||||
switch (mode) {
|
||||
case 'radio-import-source-file':
|
||||
await this.chooseFile();
|
||||
break;
|
||||
|
||||
case 'radio-import-source-mendeley':
|
||||
this._dbs = await Zotero_File_Interface.findMendeleyDatabases();
|
||||
// This shouldn't happen, because we only show the wizard if there are databases
|
||||
if (!this._dbs.length) {
|
||||
throw new Error("No databases found");
|
||||
}
|
||||
this._populateFileList(this._dbs);
|
||||
document.getElementById('file-options-header').textContent
|
||||
= Zotero.getString('fileInterface.chooseAppDatabaseToImport', 'Mendeley')
|
||||
wizard.goTo('page-file-list');
|
||||
wizard.canRewind = true;
|
||||
this._enableCancel();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown mode ${mode}`);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
this._onDone(
|
||||
Zotero.getString('general.error'),
|
||||
Zotero.getString('fileInterface.importError'),
|
||||
true
|
||||
);
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
goToStart: function () {
|
||||
this._wizard.goTo('page-start');
|
||||
this._wizard.canAdvance = true;
|
||||
return false;
|
||||
},
|
||||
|
||||
|
||||
chooseFile: async function (translation) {
|
||||
var translation = new Zotero.Translate.Import();
|
||||
var translators = await translation.getTranslators();
|
||||
const nsIFilePicker = Components.interfaces.nsIFilePicker;
|
||||
var fp = Components.classes["@mozilla.org/filepicker;1"]
|
||||
.createInstance(nsIFilePicker);
|
||||
fp.init(window, Zotero.getString("fileInterface.import"), nsIFilePicker.modeOpen);
|
||||
|
||||
fp.appendFilters(nsIFilePicker.filterAll);
|
||||
|
||||
var collation = Zotero.getLocaleCollation();
|
||||
|
||||
// Add Mendeley DB, which isn't a translator
|
||||
var mendeleyFilter = {
|
||||
label: "Mendeley Database", // TODO: Localize
|
||||
target: "*.sqlite"
|
||||
};
|
||||
var filters = [...translators];
|
||||
filters.push(mendeleyFilter);
|
||||
|
||||
filters.sort((a, b) => collation.compareString(1, a.label, b.label));
|
||||
for (let filter of filters) {
|
||||
fp.appendFilter(filter.label, "*." + filter.target);
|
||||
}
|
||||
|
||||
var rv = fp.show();
|
||||
if (rv !== nsIFilePicker.returnOK && rv !== nsIFilePicker.returnReplace) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Zotero.debug(`File is ${fp.file.path}`);
|
||||
|
||||
this._file = fp.file.path;
|
||||
this._wizard.canAdvance = true;
|
||||
this._wizard.goTo('page-options');
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* When a file is clicked on in the file list
|
||||
*/
|
||||
onFileSelected: async function () {
|
||||
var index = document.getElementById('file-list').selectedIndex;
|
||||
if (index != -1) {
|
||||
this._file = this._dbs[index].path;
|
||||
this._wizard.canAdvance = true;
|
||||
}
|
||||
else {
|
||||
this._file = null;
|
||||
this._wizard.canAdvance = false;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* When the user clicks "Other…" to choose a file not in the list
|
||||
*/
|
||||
chooseMendeleyDB: async function () {
|
||||
document.getElementById('file-list').selectedIndex = -1;
|
||||
const nsIFilePicker = Components.interfaces.nsIFilePicker;
|
||||
var fp = Components.classes["@mozilla.org/filepicker;1"]
|
||||
.createInstance(nsIFilePicker);
|
||||
fp.init(window, Zotero.getString('fileInterface.import'), nsIFilePicker.modeOpen);
|
||||
fp.appendFilter("Mendeley Database", "*.sqlite"); // TODO: Localize
|
||||
var rv = fp.show();
|
||||
if (rv != nsIFilePicker.returnOK) {
|
||||
return false;
|
||||
}
|
||||
this._file = fp.file.path;
|
||||
this._wizard.canAdvance = true;
|
||||
this._wizard.advance();
|
||||
},
|
||||
|
||||
|
||||
onOptionsShown: function () {
|
||||
|
||||
},
|
||||
|
||||
|
||||
onImportStart: async function () {
|
||||
if (!this._file) {
|
||||
let index = document.getElementById('file-list').selectedIndex;
|
||||
this._file = this._dbs[index].path;
|
||||
}
|
||||
this._disableCancel();
|
||||
this._wizard.canRewind = false;
|
||||
this._wizard.canAdvance = false;
|
||||
await this.doImport({
|
||||
createNewCollection: document.getElementById('create-collection-checkbox').hasAttribute('checked')
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
onBeforeImport: async function (translation) {
|
||||
// Unrecognized translator
|
||||
if (!translation) {
|
||||
// Allow error dialog to be displayed, and then close window
|
||||
setTimeout(function () {
|
||||
window.close();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this._translation = translation;
|
||||
|
||||
// Switch to progress pane
|
||||
this._wizard.goTo('page-progress');
|
||||
var pm = document.getElementById('import-progressmeter');
|
||||
|
||||
translation.setHandler('itemDone', function () {
|
||||
pm.value = translation.getProgress();
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
doImport: async function (options) {
|
||||
try {
|
||||
let result = await Zotero_File_Interface.importFile({
|
||||
file: this._file,
|
||||
onBeforeImport: this.onBeforeImport.bind(this),
|
||||
addToLibraryRoot: !options.createNewCollection
|
||||
});
|
||||
|
||||
// Cancelled by user or due to error
|
||||
if (!result) {
|
||||
window.close();
|
||||
return;
|
||||
}
|
||||
|
||||
let numItems = this._translation.newItems.length;
|
||||
this._onDone(
|
||||
Zotero.getString('fileInterface.importComplete'),
|
||||
Zotero.getString(`fileInterface.itemsWereImported`, numItems, numItems)
|
||||
);
|
||||
}
|
||||
catch (e) {
|
||||
if (e.message == 'Encrypted Mendeley database') {
|
||||
let url = 'https://www.zotero.org/support/kb/mendeley_import';
|
||||
this._onDone(
|
||||
Zotero.getString('general.error'),
|
||||
// TODO: Localize
|
||||
`The selected Mendeley database cannot be read, likely because it is encrypted. `
|
||||
+ `See <a href="${url}" class="text-link">How do I import a Mendeley library `
|
||||
+ `into Zotero?</a> for more information.`
|
||||
);
|
||||
}
|
||||
else {
|
||||
this._onDone(
|
||||
Zotero.getString('general.error'),
|
||||
Zotero.getString('fileInterface.importError'),
|
||||
true
|
||||
);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
reportError: function () {
|
||||
Zotero.getActiveZoteroPane().reportErrors();
|
||||
window.close();
|
||||
},
|
||||
|
||||
|
||||
_populateFileList: async function (files) {
|
||||
var listbox = document.getElementById('file-list');
|
||||
|
||||
// Remove existing entries
|
||||
var items = listbox.getElementsByTagName('listitem');
|
||||
for (let item of items) {
|
||||
listbox.removeChild(item);
|
||||
}
|
||||
|
||||
for (let file of files) {
|
||||
let li = document.createElement('listitem');
|
||||
|
||||
let name = document.createElement('listcell');
|
||||
// Simply filenames
|
||||
let nameStr = file.name
|
||||
.replace(/\.sqlite$/, '')
|
||||
.replace(/@www\.mendeley\.com$/, '');
|
||||
if (nameStr == 'online') {
|
||||
nameStr = Zotero.getString('dataDir.default', 'online.sqlite');
|
||||
}
|
||||
name.setAttribute('label', nameStr + ' ');
|
||||
li.appendChild(name);
|
||||
|
||||
let lastModified = document.createElement('listcell');
|
||||
lastModified.setAttribute('label', file.lastModified.toLocaleString() + ' ');
|
||||
li.appendChild(lastModified);
|
||||
|
||||
let size = document.createElement('listcell');
|
||||
size.setAttribute(
|
||||
'label',
|
||||
Zotero.getString('general.nMegabytes', (file.size / 1024 / 1024).toFixed(1)) + ' '
|
||||
);
|
||||
li.appendChild(size);
|
||||
|
||||
listbox.appendChild(li);
|
||||
}
|
||||
|
||||
if (files.length == 1) {
|
||||
listbox.selectedIndex = 0;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
_enableCancel: function () {
|
||||
this._wizard.getButton('cancel').disabled = false;
|
||||
},
|
||||
|
||||
|
||||
_disableCancel: function () {
|
||||
this._wizard.getButton('cancel').disabled = true;
|
||||
},
|
||||
|
||||
|
||||
_onDone: function (label, description, showReportErrorButton) {
|
||||
var wizard = this._wizard;
|
||||
wizard.getPageById('page-done').setAttribute('label', label);
|
||||
|
||||
var xulElem = document.getElementById('result-description');
|
||||
var htmlElem = document.getElementById('result-description-html');
|
||||
|
||||
if (description.includes('href')) {
|
||||
htmlElem.innerHTML = description;
|
||||
Zotero.Utilities.Internal.updateHTMLInXUL(htmlElem);
|
||||
xulElem.hidden = true;
|
||||
htmlElem.setAttribute('display', 'block');
|
||||
}
|
||||
else {
|
||||
xulElem.textContent = description;
|
||||
xulElem.hidden = false;
|
||||
htmlElem.setAttribute('display', 'none');
|
||||
}
|
||||
document.getElementById('result-description')
|
||||
|
||||
if (showReportErrorButton) {
|
||||
let button = document.getElementById('result-report-error');
|
||||
button.setAttribute('label', Zotero.getString('errorReport.reportError'));
|
||||
button.hidden = false;
|
||||
}
|
||||
|
||||
// When done, move to last page and allow closing
|
||||
wizard.canAdvance = true;
|
||||
wizard.goTo('page-done');
|
||||
wizard.canRewind = false;
|
||||
}
|
||||
};
|
75
chrome/content/zotero/import/importWizard.xul
Normal file
75
chrome/content/zotero/import/importWizard.xul
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero/skin/importWizard.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE window SYSTEM "chrome://zotero/locale/zotero.dtd">
|
||||
|
||||
<wizard id="import-wizard"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
title="&zotero.import;"
|
||||
onload="Zotero_Import_Wizard.init()">
|
||||
|
||||
<script src="../include.js"/>
|
||||
<script src="../fileInterface.js"/>
|
||||
<script src="importWizard.js"/>
|
||||
|
||||
<wizardpage pageid="page-start"
|
||||
label="&zotero.import.whereToImportFrom;"
|
||||
next="page-options"
|
||||
onpageadvanced="Zotero_Import_Wizard.onModeChosen(); return false;">
|
||||
<radiogroup id="import-source">
|
||||
<radio id="radio-import-source-file" label="&zotero.import.source.file;"/>
|
||||
<radio id="radio-import-source-mendeley" label="Mendeley" hidden="true"/>
|
||||
</radiogroup>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage pageid="page-file-list"
|
||||
next="page-options"
|
||||
onpagerewound="return Zotero_Import_Wizard.goToStart()">
|
||||
<description id="file-options-header"/>
|
||||
<listbox id="file-list" onselect="Zotero_Import_Wizard.onFileSelected()">
|
||||
<listhead>
|
||||
<listheader label="&zotero.import.database;"/>
|
||||
<listheader label="&zotero.import.lastModified;"/>
|
||||
<listheader label="&zotero.import.size;"/>
|
||||
</listhead>
|
||||
|
||||
<listcols>
|
||||
<listcol flex="1"/>
|
||||
<listcol/>
|
||||
<listcol/>
|
||||
</listcols>
|
||||
</listbox>
|
||||
<hbox>
|
||||
<button label="&zotero.general.other;" oncommand="Zotero_Import_Wizard.chooseMendeleyDB()"/>
|
||||
</hbox>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage pageid="page-options"
|
||||
label="&zotero.general.options;"
|
||||
next="page-progress"
|
||||
onpageshow="Zotero_Import_Wizard.onOptionsShown()"
|
||||
onpagerewound="return Zotero_Import_Wizard.goToStart()"
|
||||
onpageadvanced="Zotero_Import_Wizard.onImportStart()">
|
||||
<checkbox id="create-collection-checkbox" label="&zotero.import.createCollection;" checked="true" />
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage pageid="page-progress"
|
||||
label="&zotero.import.importing;"
|
||||
onpageshow="document.getElementById('import-wizard').canRewind = false;"
|
||||
next="page-done">
|
||||
<progressmeter id="import-progressmeter" mode="determined"/>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage pageid="page-done">
|
||||
<description id="result-description"/>
|
||||
<html:div id="result-description-html"/>
|
||||
<hbox>
|
||||
<button id="result-report-error"
|
||||
oncommand="Zotero_Import_Wizard.reportError()"
|
||||
hidden="true"/>
|
||||
</hbox>
|
||||
</wizardpage>
|
||||
</wizard>
|
1210
chrome/content/zotero/import/mendeley/mendeleyImport.js
Normal file
1210
chrome/content/zotero/import/mendeley/mendeleyImport.js
Normal file
File diff suppressed because it is too large
Load Diff
102
chrome/content/zotero/import/mendeley/mendeleySchemaMap.js
Normal file
102
chrome/content/zotero/import/mendeley/mendeleySchemaMap.js
Normal file
|
@ -0,0 +1,102 @@
|
|||
var map = {
|
||||
83: {
|
||||
itemTypes: {
|
||||
Bill: "bill",
|
||||
Book: "book",
|
||||
BookSection: "bookSection",
|
||||
Case: "case",
|
||||
ComputerProgram: "computerProgram",
|
||||
ConferenceProceedings: "conferencePaper",
|
||||
EncyclopediaArticle: "encyclopediaArticle",
|
||||
Film: "film",
|
||||
Generic: "document",
|
||||
JournalArticle: "journalArticle",
|
||||
MagazineArticle: "magazineArticle",
|
||||
NewspaperArticle: "newspaperArticle",
|
||||
Patent: "patent",
|
||||
Report: "report",
|
||||
Statute: "statute",
|
||||
TelevisionBroadcast: "tvBroadcast",
|
||||
Thesis: "thesis",
|
||||
WebPage: "webpage",
|
||||
WorkingPaper: "report"
|
||||
},
|
||||
fields: {
|
||||
id: "",
|
||||
uuid: "",
|
||||
reviewedArticle: "",
|
||||
revisionNumber: "",
|
||||
publisher: "publisher",
|
||||
reprintEdition: "",
|
||||
series: "seriesTitle",
|
||||
seriesNumber: "seriesNumber",
|
||||
sections: "section",
|
||||
seriesEditor: "creator[seriesEditor]", // falls back to editor if necessary
|
||||
owner: "",
|
||||
pages: "func[pages]",
|
||||
month: "", // handled explicitly
|
||||
originalPublication: "",
|
||||
publication: "publicationTitle",
|
||||
publicLawNumber: "publicLawNumber",
|
||||
pmid: "extra[PMID]",
|
||||
sourceType: "",
|
||||
session: "session",
|
||||
shortTitle: "shortTitle",
|
||||
volume: "volume",
|
||||
year: "", // handled explicitly
|
||||
userType: "type",
|
||||
country: "place[country]",
|
||||
dateAccessed: "accessDate",
|
||||
committee: "committee",
|
||||
counsel: "creator[counsel]",
|
||||
doi: "DOI",
|
||||
edition: "edition",
|
||||
day: "", // handled explicitly
|
||||
department: "",
|
||||
citationKey: "citationKey", // put in Extra
|
||||
city: "place[city]",
|
||||
chapter: "",
|
||||
codeSection: "section",
|
||||
codeVolume: "codeVolume",
|
||||
code: "code",
|
||||
codeNumber: "codeNumber",
|
||||
issue: "issue",
|
||||
language: "language",
|
||||
isbn: "ISBN",
|
||||
issn: "ISSN",
|
||||
length: "",
|
||||
medium: "medium",
|
||||
lastUpdate: "",
|
||||
legalStatus: "legalStatus",
|
||||
hideFromMendeleyWebIndex: "",
|
||||
institution: "publisher",
|
||||
genre: "genre",
|
||||
internationalTitle: "",
|
||||
internationalUserType: "",
|
||||
internationalAuthor: "",
|
||||
internationalNumber: "",
|
||||
deletionPending: "",
|
||||
favourite: "", // tag?
|
||||
confirmed: "", // tag?
|
||||
deduplicated: "",
|
||||
read: "", // tag?
|
||||
type: "", // item type handled separately
|
||||
title: "title",
|
||||
privacy: "",
|
||||
applicationNumber: "applicationNumber",
|
||||
arxivId: "extra[arXiv]",
|
||||
advisor: "",
|
||||
articleColumn: "",
|
||||
modified: "func[fromUnixtime:dateModified]",
|
||||
abstract: "abstractNote",
|
||||
added: "func[fromUnixtime:dateAdded]",
|
||||
note: "func[note]",
|
||||
importer: ""
|
||||
},
|
||||
creatorTypes: {
|
||||
DocumentAuthor: "author",
|
||||
DocumentEditor: "editor",
|
||||
DocumentTranslator: "translator"
|
||||
}
|
||||
}
|
||||
};
|
|
@ -68,7 +68,7 @@
|
|||
</radiogroup>
|
||||
</groupbox>
|
||||
|
||||
<groupbox>
|
||||
<groupbox id="formatUsing-groupbox">
|
||||
<caption label="&zotero.integration.prefs.formatUsing.label;"/>
|
||||
|
||||
<radiogroup id="formatUsing" orient="vertical">
|
||||
|
|
121
chrome/content/zotero/integration/progressBar.js
Normal file
121
chrome/content/zotero/integration/progressBar.js
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2018 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
http://zotero.org
|
||||
|
||||
This file is part of Zotero.
|
||||
|
||||
Zotero is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Zotero is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
var Zotero_ProgressBar = new function () {
|
||||
var initialized, io;
|
||||
|
||||
/**
|
||||
* Pre-initialization, when the dialog has loaded but has not yet appeared
|
||||
*/
|
||||
this.onDOMContentLoaded = function(event) {
|
||||
if(event.target === document) {
|
||||
initialized = true;
|
||||
io = window.arguments[0].wrappedJSObject;
|
||||
if (io.onLoad) {
|
||||
io.onLoad(_onProgress);
|
||||
}
|
||||
|
||||
// Only hide chrome on Windows or Mac
|
||||
if(Zotero.isMac) {
|
||||
document.documentElement.setAttribute("drawintitlebar", true);
|
||||
} else if(Zotero.isWin) {
|
||||
document.documentElement.setAttribute("hidechrome", true);
|
||||
}
|
||||
|
||||
new WindowDraggingElement(document.getElementById("quick-format-dialog"), window);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Center the window
|
||||
*/
|
||||
this.onLoad = function(event) {
|
||||
if(event.target !== document) return;
|
||||
// make sure we are visible
|
||||
window.focus();
|
||||
window.setTimeout(function() {
|
||||
var targetX = Math.floor(-window.outerWidth/2 + (window.screen.width / 2));
|
||||
var targetY = Math.floor(-window.outerHeight/2 + (window.screen.height / 2));
|
||||
Zotero.debug("Moving window to "+targetX+", "+targetY);
|
||||
window.moveTo(targetX, targetY);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when progress changes
|
||||
*/
|
||||
function _onProgress(percent) {
|
||||
var meter = document.getElementById("quick-format-progress-meter");
|
||||
if(percent === null) {
|
||||
meter.mode = "undetermined";
|
||||
} else {
|
||||
meter.mode = "determined";
|
||||
meter.value = Math.round(percent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes windows
|
||||
* @constructor
|
||||
*/
|
||||
var Resizer = function(panel, targetWidth, targetHeight, pixelsPerStep, stepsPerSecond) {
|
||||
this.panel = panel;
|
||||
this.curWidth = panel.clientWidth;
|
||||
this.curHeight = panel.clientHeight;
|
||||
this.difX = (targetWidth ? targetWidth - this.curWidth : 0);
|
||||
this.difY = (targetHeight ? targetHeight - this.curHeight : 0);
|
||||
this.step = 0;
|
||||
this.steps = Math.ceil(Math.max(Math.abs(this.difX), Math.abs(this.difY))/pixelsPerStep);
|
||||
this.timeout = (1000/stepsPerSecond);
|
||||
|
||||
var me = this;
|
||||
this._animateCallback = function() { me.animate() };
|
||||
};
|
||||
|
||||
/**
|
||||
* Performs a step of the animation
|
||||
*/
|
||||
Resizer.prototype.animate = function() {
|
||||
if(this.stopped) return;
|
||||
this.step++;
|
||||
this.panel.sizeTo(this.curWidth+Math.round(this.step*this.difX/this.steps),
|
||||
this.curHeight+Math.round(this.step*this.difY/this.steps));
|
||||
if(this.step !== this.steps) {
|
||||
window.setTimeout(this._animateCallback, this.timeout);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Halts resizing
|
||||
*/
|
||||
Resizer.prototype.stop = function() {
|
||||
this.stopped = true;
|
||||
};
|
||||
}
|
||||
|
||||
window.addEventListener("DOMContentLoaded", Zotero_ProgressBar.onDOMContentLoaded, false);
|
||||
window.addEventListener("load", Zotero_ProgressBar.onLoad, false);
|
51
chrome/content/zotero/integration/progressBar.xul
Normal file
51
chrome/content/zotero/integration/progressBar.xul
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2018 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
http://zotero.org
|
||||
|
||||
This file is part of Zotero.
|
||||
|
||||
Zotero is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Zotero is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
-->
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://global/skin/browser.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero/skin/zotero.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero/skin/integration.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero-platform/content/integration.css" type="text/css"?>
|
||||
<!DOCTYPE window SYSTEM "chrome://zotero/locale/zotero.dtd">
|
||||
|
||||
<window
|
||||
id="quick-format-dialog"
|
||||
class="progress-bar"
|
||||
orient="vertical"
|
||||
title="&zotero.progress.title;"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
persist="screenX screenY">
|
||||
|
||||
<script src="../include.js"/>
|
||||
<script src="windowDraggingUtils.js" type="text/javascript"/>
|
||||
<script src="progressBar.js" type="text/javascript"/>
|
||||
|
||||
<box orient="horizontal" id="quick-format-entry">
|
||||
<deck id="quick-format-deck" selectedIndex="0" flex="1">
|
||||
<progressmeter id="quick-format-progress-meter" mode="undetermined" value="0" flex="1"/>
|
||||
</deck>
|
||||
</box>
|
||||
</window>
|
|
@ -179,6 +179,7 @@ var Zotero_QuickFormat = new function () {
|
|||
*/
|
||||
function _getCurrentEditorTextNode() {
|
||||
var selection = qfiWindow.getSelection();
|
||||
if (!selection) return false;
|
||||
var range = selection.getRangeAt(0);
|
||||
|
||||
var node = range.startContainer;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 15396c0c18b768f0837015091bae9c931d4dd56b
|
||||
Subproject commit b8c370c8a978790d2aeefa302f05f3bfb1478e75
|
|
@ -58,7 +58,7 @@ Zotero_Preferences.General = {
|
|||
}
|
||||
fp.init(
|
||||
window,
|
||||
Zotero.getString('zotero.preferences.chooseFileHandler'),
|
||||
Zotero.getString('zotero.preferences.chooseApplication'),
|
||||
nsIFilePicker.modeOpen
|
||||
);
|
||||
fp.appendFilters(nsIFilePicker.filterApps);
|
||||
|
|
|
@ -49,6 +49,14 @@
|
|||
<textbox size="10" preference="pref-fulltext-textMaxLength"/>
|
||||
<label value="(&zotero.preferences.default; 500000)"/>
|
||||
</hbox>
|
||||
|
||||
<separator class="thin"/>
|
||||
|
||||
<hbox align="center">
|
||||
<label value="&zotero.preferences.fulltext.pdfMaxPages;"/>
|
||||
<textbox size="5" preference="pref-fulltext-pdfmaxpages"/>
|
||||
<label value="(&zotero.preferences.default; 100)"/>
|
||||
</hbox>
|
||||
</groupbox>
|
||||
|
||||
<groupbox id="fulltext-stats">
|
||||
|
|
|
@ -281,6 +281,8 @@ ZoteroStandalone.DebugOutput = {
|
|||
submit: function () {
|
||||
// 'Zotero' isn't defined yet when this function is created, so do it inline
|
||||
return Zotero.Promise.coroutine(function* () {
|
||||
Zotero.debug("Submitting debug output");
|
||||
|
||||
Components.utils.import("resource://zotero/config.js");
|
||||
|
||||
var url = ZOTERO_CONFIG.REPOSITORY_URL + "report?debug=1";
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
for (let type of types) {
|
||||
var fieldIDs = Zotero.ItemFields.getItemTypeFields(type.id);
|
||||
var baseFields = {};
|
||||
for (let fieldID in fieldIDs) {
|
||||
for (let fieldID of fieldIDs) {
|
||||
if (baseMappedFields.includes(fieldID)) {
|
||||
baseFields[fieldID] = Zotero.ItemFields.getBaseIDFromTypeAndField(type.id, fieldID);
|
||||
}
|
||||
|
|
|
@ -154,7 +154,7 @@ Zotero.API = {
|
|||
return 'groups/' + Zotero.Groups.getGroupIDFromLibraryID(libraryID);
|
||||
|
||||
default:
|
||||
throw new Error(`Invalid type '${type}`);
|
||||
throw new Error(`Invalid type '${type}'`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -176,14 +176,18 @@ Zotero.Attachments = new function(){
|
|||
|
||||
|
||||
/**
|
||||
* @param {Object} options - 'file', 'url', 'title', 'contentType', 'charset', 'parentItemID'
|
||||
* @param {Object} options - 'file', 'url', 'title', 'contentType', 'charset', 'parentItemID', 'singleFile'
|
||||
* @return {Promise<Zotero.Item>}
|
||||
*/
|
||||
this.importSnapshotFromFile = Zotero.Promise.coroutine(function* (options) {
|
||||
Zotero.debug('Importing snapshot from file');
|
||||
|
||||
var file = Zotero.File.pathToFile(options.file);
|
||||
var fileName = file.leafName;
|
||||
// TODO: Fix main filename when copying directory, though in that case it's probably
|
||||
// from our own export and already clean
|
||||
var fileName = options.singleFile
|
||||
? Zotero.File.getValidFileName(file.leafName)
|
||||
: file.leafName;
|
||||
var url = options.url;
|
||||
var title = options.title;
|
||||
var contentType = options.contentType;
|
||||
|
@ -194,7 +198,7 @@ Zotero.Attachments = new function(){
|
|||
throw new Error("parentItemID not provided");
|
||||
}
|
||||
|
||||
var attachmentItem, itemID, destDir, newFile;
|
||||
var attachmentItem, itemID, destDir, newPath;
|
||||
try {
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
// Create a new attachment
|
||||
|
@ -217,13 +221,23 @@ Zotero.Attachments = new function(){
|
|||
var storageDir = Zotero.getStorageDirectory();
|
||||
destDir = this.getStorageDirectory(attachmentItem);
|
||||
yield OS.File.removeDir(destDir.path);
|
||||
file.parent.copyTo(storageDir, destDir.leafName);
|
||||
|
||||
// Point to copied file
|
||||
newFile = destDir.clone();
|
||||
newFile.append(file.leafName);
|
||||
newPath = OS.Path.join(destDir.path, fileName);
|
||||
// Copy single file to new directory
|
||||
if (options.singleFile) {
|
||||
yield this.createDirectoryForItem(attachmentItem);
|
||||
yield OS.File.copy(file.path, newPath);
|
||||
}
|
||||
// Copy entire parent directory (for HTML snapshots)
|
||||
else {
|
||||
file.parent.copyTo(storageDir, destDir.leafName);
|
||||
}
|
||||
}.bind(this));
|
||||
yield _postProcessFile(attachmentItem, newFile, contentType, charset);
|
||||
yield _postProcessFile(
|
||||
attachmentItem,
|
||||
Zotero.File.pathToFile(newPath),
|
||||
contentType,
|
||||
charset
|
||||
);
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
|
@ -257,7 +271,7 @@ Zotero.Attachments = new function(){
|
|||
* @param {String} [options.referrer]
|
||||
* @param {CookieSandbox} [options.cookieSandbox]
|
||||
* @param {Object} [options.saveOptions]
|
||||
* @return {Promise<Zotero.Item|false>} - A promise for the created attachment item
|
||||
* @return {Promise<Zotero.Item>} - A promise for the created attachment item
|
||||
*/
|
||||
this.importFromURL = Zotero.Promise.coroutine(function* (options) {
|
||||
var libraryID = options.libraryID;
|
||||
|
@ -1115,13 +1129,75 @@ Zotero.Attachments = new function(){
|
|||
|
||||
|
||||
/**
|
||||
* Copy attachment item, including files, to another library
|
||||
* Move attachment item, including file, to another library
|
||||
*/
|
||||
this.moveAttachmentToLibrary = async function (attachment, libraryID, parentItemID) {
|
||||
if (attachment.libraryID == libraryID) {
|
||||
throw new Error("Attachment is already in library " + libraryID);
|
||||
}
|
||||
|
||||
Zotero.DB.requireTransaction();
|
||||
|
||||
var newAttachment = attachment.clone(libraryID);
|
||||
if (attachment.isImportedAttachment()) {
|
||||
// Attachment path isn't copied over by clone() if libraryID is different
|
||||
newAttachment.attachmentPath = attachment.attachmentPath;
|
||||
}
|
||||
if (parentItemID) {
|
||||
newAttachment.parentID = parentItemID;
|
||||
}
|
||||
await newAttachment.save();
|
||||
|
||||
// Move files over if they exist
|
||||
var oldDir;
|
||||
var newDir;
|
||||
if (newAttachment.isImportedAttachment()) {
|
||||
oldDir = this.getStorageDirectory(attachment).path;
|
||||
if (await OS.File.exists(oldDir)) {
|
||||
newDir = this.getStorageDirectory(newAttachment).path;
|
||||
// Target directory shouldn't exist, but remove it if it does
|
||||
//
|
||||
// Testing for directories in OS.File, used by removeDir(), is broken on Travis,
|
||||
// so use nsIFile
|
||||
if (Zotero.automatedTest) {
|
||||
let nsIFile = Zotero.File.pathToFile(newDir);
|
||||
if (nsIFile.exists()) {
|
||||
nsIFile.remove(true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
await OS.File.removeDir(newDir, { ignoreAbsent: true });
|
||||
}
|
||||
await OS.File.move(oldDir, newDir);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await attachment.erase();
|
||||
}
|
||||
catch (e) {
|
||||
// Move files back if old item can't be deleted
|
||||
if (newAttachment.isImportedAttachment()) {
|
||||
try {
|
||||
await OS.File.move(newDir, oldDir);
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
return newAttachment.id;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Copy attachment item, including file, to another library
|
||||
*/
|
||||
this.copyAttachmentToLibrary = Zotero.Promise.coroutine(function* (attachment, libraryID, parentItemID) {
|
||||
var linkMode = attachment.attachmentLinkMode;
|
||||
|
||||
if (attachment.libraryID == libraryID) {
|
||||
throw ("Attachment is already in library " + libraryID);
|
||||
throw new Error("Attachment is already in library " + libraryID);
|
||||
}
|
||||
|
||||
Zotero.DB.requireTransaction();
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
*/
|
||||
|
||||
var CSL = {
|
||||
PROCESSOR_VERSION: "1.1.190",
|
||||
PROCESSOR_VERSION: "1.1.206",
|
||||
CONDITION_LEVEL_TOP: 1,
|
||||
CONDITION_LEVEL_BOTTOM: 2,
|
||||
PLAIN_HYPHEN_REGEX: /(?:[^\\]-|\u2013)/,
|
||||
|
@ -163,7 +163,7 @@ var CSL = {
|
|||
}
|
||||
};
|
||||
},
|
||||
MULTI_FIELDS: ["event", "publisher", "publisher-place", "event-place", "title", "container-title", "collection-title", "authority","genre","title-short","medium","jurisdiction","archive","archive-place"],
|
||||
MULTI_FIELDS: ["event", "publisher", "publisher-place", "event-place", "title", "container-title", "collection-title", "authority","genre","title-short","medium","country","jurisdiction","archive","archive-place"],
|
||||
LangPrefsMap: {
|
||||
"title":"titles",
|
||||
"title-short":"titles",
|
||||
|
@ -202,6 +202,7 @@ var CSL = {
|
|||
"title": "title",
|
||||
"container-title": "container-title",
|
||||
"collection-title": "collection-title",
|
||||
"country": "place",
|
||||
"number": "number",
|
||||
"place": "place",
|
||||
"archive": "collection-title",
|
||||
|
@ -460,12 +461,12 @@ var CSL = {
|
|||
PREFIX_PUNCTUATION: /[.;:]\s*$/,
|
||||
SUFFIX_PUNCTUATION: /^\s*[.;:,\(\)]/,
|
||||
NUMBER_REGEXP: /(?:^\d+|\d+$)/,
|
||||
NAME_INITIAL_REGEXP: /^([A-Z\u0590-\u05ff\u00c0-\u017f\u0400-\u042f\u0600-\u06ff\u0370\u0372\u0376\u0386\u0388-\u03ab\u03e2\u03e4\u03e6\u03e8\u03ea\u03ec\u03ee\u03f4\u03f7\u03fd-\u03ff])([a-zA-Z\u00c0-\u017f\u0400-\u052f\u0600-\u06ff\u0370-\u03ff\u1f00-\u1fff]*|)/,
|
||||
ROMANESQUE_REGEXP: /[-0-9a-zA-Z\u0590-\u05d4\u05d6-\u05ff\u0080-\u017f\u0400-\u052f\u0370-\u03ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]/,
|
||||
ROMANESQUE_NOT_REGEXP: /[^a-zA-Z\u0590-\u05ff\u00c0-\u017f\u0400-\u052f\u0370-\u03ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]/g,
|
||||
STARTSWITH_ROMANESQUE_REGEXP: /^[&a-zA-Z\u0590-\u05d4\u05d6-\u05ff\u00c0-\u017f\u0400-\u052f\u0370-\u03ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]/,
|
||||
ENDSWITH_ROMANESQUE_REGEXP: /[.;:&a-zA-Z\u0590-\u05d4\u05d6-\u05ff\u00c0-\u017f\u0400-\u052f\u0370-\u03ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]$/,
|
||||
ALL_ROMANESQUE_REGEXP: /^[a-zA-Z\u0590-\u05ff\u00c0-\u017f\u0400-\u052f\u0370-\u03ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]+$/,
|
||||
NAME_INITIAL_REGEXP: /^([A-Z\u00c0-\u017f\u0400-\u042f\u0590-\u05d4\u05d6-\u05ff\u0600-\u06ff\u0370\u0372\u0376\u0386\u0388-\u03ab\u03e2\u03e4\u03e6\u03e8\u03ea\u03ec\u03ee\u03f4\u03f7\u03fd-\u03ff])([a-zA-Z\u00c0-\u017f\u0400-\u052f\u0600-\u06ff\u0370-\u03ff\u1f00-\u1fff]*|)/,
|
||||
ROMANESQUE_REGEXP: /[-0-9a-zA-Z\u00c0-\u017f\u0370-\u03ff\u0400-\u052f\u0590-\u05d4\u05d6-\u05ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]/,
|
||||
ROMANESQUE_NOT_REGEXP: /[^a-zA-Z\u00c0-\u017f\u0370-\u03ff\u0400-\u052f\u0590-\u05d4\u05d6-\u05ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]/g,
|
||||
STARTSWITH_ROMANESQUE_REGEXP: /^[&a-zA-Z\u00c0-\u017f\u0370-\u03ff\u0400-\u052f\u0590-\u05d4\u05d6-\u05ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]/,
|
||||
ENDSWITH_ROMANESQUE_REGEXP: /[.;:&a-zA-Z\u00c0-\u017f\u0370-\u03ff\u0400-\u052f\u0590-\u05d4\u05d6-\u05ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]$/,
|
||||
ALL_ROMANESQUE_REGEXP: /^[a-zA-Z\u00c0-\u017f\u0370-\u03ff\u0400-\u052f\u0590-\u05d4\u05d6-\u05ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]+$/,
|
||||
VIETNAMESE_SPECIALS: /[\u00c0-\u00c3\u00c8-\u00ca\u00cc\u00cd\u00d2-\u00d5\u00d9\u00da\u00dd\u00e0-\u00e3\u00e8-\u00ea\u00ec\u00ed\u00f2-\u00f5\u00f9\u00fa\u00fd\u0101\u0103\u0110\u0111\u0128\u0129\u0168\u0169\u01a0\u01a1\u01af\u01b0\u1ea0-\u1ef9]/,
|
||||
VIETNAMESE_NAMES: /^(?:(?:[.AaBbCcDdEeGgHhIiKkLlMmNnOoPpQqRrSsTtUuVvXxYy \u00c0-\u00c3\u00c8-\u00ca\u00cc\u00cd\u00d2-\u00d5\u00d9\u00da\u00dd\u00e0-\u00e3\u00e8-\u00ea\u00ec\u00ed\u00f2-\u00f5\u00f9\u00fa\u00fd\u0101\u0103\u0110\u0111\u0128\u0129\u0168\u0169\u01a0\u01a1\u01af\u01b0\u1ea0-\u1ef9]{2,6})(\s+|$))+$/,
|
||||
NOTE_FIELDS_REGEXP: /\{:(?:[\-_a-z]+|[A-Z]+):[^\}]+\}/g,
|
||||
|
@ -2137,7 +2138,7 @@ CSL.configureMacro = function (mytarget) {
|
|||
this.configureTokenList(mytarget);
|
||||
}
|
||||
}
|
||||
CSL.XmlToToken = function (state, tokentype, explicitTarget) {
|
||||
CSL.XmlToToken = function (state, tokentype, explicitTarget, var_stack) {
|
||||
var name, txt, attrfuncs, attributes, decorations, token, key, target;
|
||||
name = state.cslXml.nodename(this);
|
||||
if (state.build.skip && state.build.skip !== name) {
|
||||
|
@ -2178,10 +2179,13 @@ CSL.XmlToToken = function (state, tokentype, explicitTarget) {
|
|||
}
|
||||
}
|
||||
token.decorations = decorations;
|
||||
if (CSL.DATE_VARIABLES.indexOf(attributes['@variable']) > -1) {
|
||||
var_stack.push(token.variables);
|
||||
}
|
||||
} else if (tokentype === CSL.END && attributes['@variable']) {
|
||||
token.hasVariable = true;
|
||||
if (CSL.DATE_VARIABLES.indexOf(attributes['@variable']) > -1) {
|
||||
token.variables = attributes['@variable'].split(/\s+/);
|
||||
token.variables = var_stack.pop();
|
||||
}
|
||||
}
|
||||
if (explicitTarget) {
|
||||
|
@ -2645,9 +2649,6 @@ CSL.Engine = function (sys, style, lang, forceLang) {
|
|||
this.opt.xclass = this.cslXml.getAttributeValue(this.cslXml.dataObj, "class");
|
||||
this.opt["class"] = this.opt.xclass;
|
||||
this.opt.styleID = this.cslXml.getStyleId(this.cslXml.dataObj);
|
||||
if (CSL.setSuppressedJurisdictions) {
|
||||
CSL.setSuppressedJurisdictions(this.opt.styleID, this.opt.suppressedJurisdictions);
|
||||
}
|
||||
this.opt.styleName = this.cslXml.getStyleId(this.cslXml.dataObj, true);
|
||||
if (this.opt.version.slice(0,4) === "1.1m") {
|
||||
this.opt.development_extensions.static_statute_locator = true;
|
||||
|
@ -2732,14 +2733,15 @@ CSL.Engine.prototype.setCloseQuotesArray = function () {
|
|||
this.opt.close_quotes_array = ret;
|
||||
};
|
||||
CSL.makeBuilder = function (me, target) {
|
||||
var var_stack = [];
|
||||
function enterFunc (node) {
|
||||
CSL.XmlToToken.call(node, me, CSL.START, target);
|
||||
CSL.XmlToToken.call(node, me, CSL.START, target, var_stack);
|
||||
};
|
||||
function leaveFunc (node) {
|
||||
CSL.XmlToToken.call(node, me, CSL.END, target);
|
||||
CSL.XmlToToken.call(node, me, CSL.END, target, var_stack);
|
||||
};
|
||||
function singletonFunc (node) {
|
||||
CSL.XmlToToken.call(node, me, CSL.SINGLETON, target);
|
||||
CSL.XmlToToken.call(node, me, CSL.SINGLETON, target, var_stack);
|
||||
};
|
||||
function buildStyle (node) {
|
||||
var starttag, origparent;
|
||||
|
@ -2997,23 +2999,14 @@ CSL.Engine.prototype.retrieveItem = function (id) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (this.sys.getLanguageName && Item.language) {
|
||||
if (Item.language) {
|
||||
Item.language = Item.language.toLowerCase();
|
||||
var lst = Item.language.split("<");
|
||||
if (lst.length > 0) {
|
||||
var languageName = this.sys.getLanguageName(lst[0]);
|
||||
if (languageName) {
|
||||
Item["language-name"] = languageName;
|
||||
}
|
||||
}
|
||||
if (lst.length === 2) {
|
||||
var originalLanguage = this.sys.getLanguageName(lst[1]);
|
||||
if (originalLanguage) {
|
||||
Item["language-name-original"] = originalLanguage;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Item.language) {
|
||||
var lst = Item.language.split("<");
|
||||
if (lst.length > 0) {
|
||||
Item["language-name"] = lst[0];
|
||||
}
|
||||
if (lst.length === 2) {
|
||||
Item["language-name-original"] = lst[1];
|
||||
}
|
||||
}
|
||||
if (Item.page) {
|
||||
Item["page-first"] = Item.page;
|
||||
|
@ -3086,10 +3079,15 @@ CSL.Engine.prototype.retrieveItem = function (id) {
|
|||
if (!Item.jurisdiction) {
|
||||
noHints = true;
|
||||
}
|
||||
var jurisdiction = this.transform.loadAbbreviation(Item.jurisdiction, "title", Item.title, Item.type, true);
|
||||
if (this.transform.abbrevs[jurisdiction].title) {
|
||||
if (this.transform.abbrevs[jurisdiction].title[Item.title]) {
|
||||
Item["title-short"] = this.transform.abbrevs[jurisdiction].title[Item.title];
|
||||
if (this.sys.normalizeAbbrevsKey) {
|
||||
var normalizedKey = this.sys.normalizeAbbrevsKey(Item.title);
|
||||
} else {
|
||||
var normalizedKey = Item.title;
|
||||
}
|
||||
var jurisdiction = this.transform.loadAbbreviation(Item.jurisdiction, "title", normalizedKey, Item.type);
|
||||
if (this.transform.abbrevs[jurisdiction]["title"]) {
|
||||
if (this.transform.abbrevs[jurisdiction]["title"][normalizedKey]) {
|
||||
Item["title-short"] = this.transform.abbrevs[jurisdiction]["title"][normalizedKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3097,13 +3095,21 @@ CSL.Engine.prototype.retrieveItem = function (id) {
|
|||
Item["container-title-short"] = Item.journalAbbreviation;
|
||||
}
|
||||
if (Item["container-title"] && this.sys.getAbbreviation) {
|
||||
var jurisdiction = this.transform.loadAbbreviation(Item.jurisdiction, "container-title", Item["container-title"]);
|
||||
if (this.sys.normalizeAbbrevsKey) {
|
||||
var normalizedKey = this.sys.normalizeAbbrevsKey(Item["container-title"]);
|
||||
} else {
|
||||
var normalizedKey = Item["container-title"];
|
||||
}
|
||||
var jurisdiction = this.transform.loadAbbreviation(Item.jurisdiction, "container-title", normalizedKey);
|
||||
if (this.transform.abbrevs[jurisdiction]["container-title"]) {
|
||||
if (this.transform.abbrevs[jurisdiction]["container-title"][Item["container-title"]]) {
|
||||
Item["container-title-short"] = this.transform.abbrevs[jurisdiction]["container-title"][Item["container-title"]];
|
||||
if (this.transform.abbrevs[jurisdiction]["container-title"][normalizedKey]) {
|
||||
Item["container-title-short"] = this.transform.abbrevs[jurisdiction]["container-title"][normalizedKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Item["jurisdiction"]) {
|
||||
Item["country"] = Item["jurisdiction"].split(":")[0];
|
||||
}
|
||||
this.registry.refhash[id] = Item;
|
||||
return Item;
|
||||
};
|
||||
|
@ -3354,56 +3360,6 @@ CSL.Engine.prototype.normalDecorIsOrphan = function (blob, params) {
|
|||
}
|
||||
return false;
|
||||
};
|
||||
CSL.getJurisdictionNameAndSuppress = function(state, jurisdictionID, jurisdictionName, chopTo) {
|
||||
var ret = null;
|
||||
if (chopTo) {
|
||||
jurisdictionID = jurisdictionID.split(":").slice(0, chopTo).join(":");
|
||||
jurisdictionName = state.sys.getHumanForm(jurisdictionID);
|
||||
}
|
||||
if (!jurisdictionName) {
|
||||
jurisdictionName = state.sys.getHumanForm(jurisdictionID);
|
||||
}
|
||||
if (!jurisdictionName) {
|
||||
ret = jurisdictionID;
|
||||
} else {
|
||||
var code = jurisdictionID.split(":");
|
||||
var name = jurisdictionName.split("|");
|
||||
var valid = false;
|
||||
if (code.length === 1 && name.length === 2) {
|
||||
valid = true;
|
||||
} else if (code.length > 1 && name.length === code.length) {
|
||||
valid = true;
|
||||
}
|
||||
if (!valid) {
|
||||
ret = name.join("|");
|
||||
} else {
|
||||
var mask = 0;
|
||||
var stub;
|
||||
for (var i=0,ilen=code.length;i<ilen;i++) {
|
||||
stub = code.slice(0, i+1).join(":");
|
||||
if (state.opt.suppressedJurisdictions[stub]) {
|
||||
mask = (i+1);
|
||||
}
|
||||
}
|
||||
if (mask === 0) {
|
||||
if (code.length === 1) {
|
||||
ret = name[0];
|
||||
} else {
|
||||
ret = name.join("|");
|
||||
}
|
||||
} else if (mask === 1) {
|
||||
if (code.length === 1) {
|
||||
ret = "";
|
||||
} else {
|
||||
ret = name.slice(mask).join("|");
|
||||
}
|
||||
} else {
|
||||
ret = name.slice(mask).join("|");
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
CSL.Engine.prototype.getCitationLabel = function (Item) {
|
||||
var label = "";
|
||||
var params = this.getTrigraphParams();
|
||||
|
@ -3911,6 +3867,10 @@ CSL.Output.Queue.prototype.string = function (state, myblobs, blob) {
|
|||
if ("number" === typeof blobjr.num) {
|
||||
ret.push(blobjr);
|
||||
} else if (blobjr.blobs) {
|
||||
if (blobjr.particle) {
|
||||
blobjr.blobs = blobjr.particle + blobjr.blobs;
|
||||
blobjr.particle = "";
|
||||
}
|
||||
b = txt_esc(blobjr.blobs);
|
||||
var blen = b.length;
|
||||
if (!state.tmp.suppress_decorations) {
|
||||
|
@ -5016,13 +4976,31 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
|
|||
var citationByIndex = [];
|
||||
for (var i = 0, ilen = citationsPre.length; i < ilen; i += 1) {
|
||||
c = citationsPre[i];
|
||||
this.registry.citationreg.citationById[c[0]].properties.noteIndex = c[1];
|
||||
try {
|
||||
this.registry.citationreg.citationById[c[0]].properties.noteIndex = c[1];
|
||||
} catch (e) {
|
||||
var err = "CSL error\n";
|
||||
err += " " + e + "\n";
|
||||
err += " citationID=" + c[0] + "\n";
|
||||
err += " noteIndex=" + c[1] + "\n";
|
||||
err += " atarray citationsPre index " + i + ", from citation at document position " + citationsPre.length;
|
||||
throw err;
|
||||
}
|
||||
citationByIndex.push(this.registry.citationreg.citationById[c[0]]);
|
||||
}
|
||||
citationByIndex.push(citation);
|
||||
for (var i = 0, ilen = citationsPost.length; i < ilen; i += 1) {
|
||||
c = citationsPost[i];
|
||||
this.registry.citationreg.citationById[c[0]].properties.noteIndex = c[1];
|
||||
try {
|
||||
this.registry.citationreg.citationById[c[0]].properties.noteIndex = c[1];
|
||||
} catch (e) {
|
||||
var err = "CSL error\n";
|
||||
err += " " + e + "\n";
|
||||
err += " citationID=" + c[0] + "\n";
|
||||
err += " noteIndex=" + c[1] + "\n";
|
||||
err += " at array citationsPost index " + i + ", from citation at document position " + citationsPre.length;
|
||||
throw err;
|
||||
}
|
||||
citationByIndex.push(this.registry.citationreg.citationById[c[0]]);
|
||||
}
|
||||
this.registry.citationreg.citationByIndex = citationByIndex;
|
||||
|
@ -5185,7 +5163,14 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
|
|||
var ibidme = false;
|
||||
var suprame = false;
|
||||
if (j > 0) {
|
||||
oldlastid = citations[j - 1].sortedItems.slice(-1)[0][1].id;
|
||||
try {
|
||||
oldlastid = citations[j - 1].sortedItems.slice(-1)[0][1].id;
|
||||
} catch (e) {
|
||||
var err = "CSL Error\n";
|
||||
err += " " + e;
|
||||
err += " in citation object " + citations[j - 1].citationID + " at index " + (j - 1);
|
||||
throw err;
|
||||
}
|
||||
if (citations[j - 1].sortedItems[0].slice(-1)[0].legislation_id) {
|
||||
oldlastid = citations[j - 1].sortedItems[0].slice(-1)[0].legislation_id;
|
||||
}
|
||||
|
@ -5509,20 +5494,30 @@ CSL.getAmbiguousCite = function (Item, disambig, visualForm, item) {
|
|||
this.tmp.group_context.replace(oldTermSiblingLayer);
|
||||
return ret;
|
||||
};
|
||||
CSL.getSpliceDelimiter = function (last_collapsed, pos) {
|
||||
if (last_collapsed && ! this.tmp.have_collapsed && "string" === typeof this.citation.opt["after-collapse-delimiter"]) {
|
||||
this.tmp.splice_delimiter = this.citation.opt["after-collapse-delimiter"];
|
||||
CSL.getSpliceDelimiter = function (last_locator, last_collapsed, pos) {
|
||||
if (undefined !== this.citation.opt["after-collapse-delimiter"]) {
|
||||
if (last_locator) {
|
||||
this.tmp.splice_delimiter = this.citation.opt["after-collapse-delimiter"];
|
||||
} else if (last_collapsed && !this.tmp.have_collapsed) {
|
||||
this.tmp.splice_delimiter = this.citation.opt["after-collapse-delimiter"];
|
||||
} else if (!last_collapsed && !this.tmp.have_collapsed && this.citation.opt.collapse !== "year-suffix") {
|
||||
this.tmp.splice_delimiter = this.citation.opt["after-collapse-delimiter"];
|
||||
} else {
|
||||
this.tmp.splice_delimiter = this.citation.opt.layout_delimiter;
|
||||
}
|
||||
} else if (this.tmp.use_cite_group_delimiter) {
|
||||
this.tmp.splice_delimiter = this.citation.opt.cite_group_delimiter;
|
||||
} else if (this.tmp.have_collapsed && this.opt.xclass === "in-text" && this.opt.update_mode !== CSL.NUMERIC) {
|
||||
this.tmp.splice_delimiter = ", ";
|
||||
} else if (this.tmp.cite_locales[pos - 1]) {
|
||||
var alt_affixes = this.tmp.cite_affixes[this.tmp.area][this.tmp.cite_locales[pos - 1]];
|
||||
if (alt_affixes && alt_affixes.delimiter) {
|
||||
this.tmp.splice_delimiter = alt_affixes.delimiter;
|
||||
} else {
|
||||
if (this.tmp.have_collapsed && this.opt.xclass === "in-text" && this.opt.update_mode !== CSL.NUMERIC) {
|
||||
this.tmp.splice_delimiter = ", ";
|
||||
} else if (this.tmp.cite_locales[pos - 1]) {
|
||||
var alt_affixes = this.tmp.cite_affixes[this.tmp.area][this.tmp.cite_locales[pos - 1]];
|
||||
if (alt_affixes && alt_affixes.delimiter) {
|
||||
this.tmp.splice_delimiter = alt_affixes.delimiter;
|
||||
}
|
||||
} else if (!this.tmp.splice_delimiter) {
|
||||
this.tmp.splice_delimiter = "";
|
||||
}
|
||||
} else if (!this.tmp.splice_delimiter) {
|
||||
this.tmp.splice_delimiter = "";
|
||||
}
|
||||
return this.tmp.splice_delimiter;
|
||||
};
|
||||
|
@ -5603,6 +5598,10 @@ CSL.getCitationCluster = function (inputList, citationID) {
|
|||
item = inputList[pos][1];
|
||||
item = CSL.parseLocator.call(this, item);
|
||||
last_collapsed = this.tmp.have_collapsed;
|
||||
var last_locator = false;
|
||||
if (pos > 0 && inputList[pos-1][1]) {
|
||||
last_locator = !!inputList[pos-1][1].locator;
|
||||
}
|
||||
params = {};
|
||||
this.tmp.shadow_numbers = {};
|
||||
if (!this.tmp.just_looking && this.opt.hasPlaceholderTerm) {
|
||||
|
@ -5633,7 +5632,7 @@ CSL.getCitationCluster = function (inputList, citationID) {
|
|||
if (pos === (inputList.length - 1)) {
|
||||
this.parallel.ComposeSet();
|
||||
}
|
||||
params.splice_delimiter = CSL.getSpliceDelimiter.call(this, last_collapsed, pos);
|
||||
params.splice_delimiter = CSL.getSpliceDelimiter.call(this, last_locator, last_collapsed, pos);
|
||||
if (item && item["author-only"]) {
|
||||
this.tmp.suppress_decorations = true;
|
||||
}
|
||||
|
@ -5768,9 +5767,6 @@ CSL.getCitationCluster = function (inputList, citationID) {
|
|||
if (buffer.length) {
|
||||
if ("string" === typeof buffer[0]) {
|
||||
if (pos > 0) {
|
||||
if (((myblobs.length-1) > pos && myparams[pos+1].have_collapsed) && !myparams[pos].have_collapsed) {
|
||||
this.tmp.splice_delimiter = myparams[pos-1].splice_delimiter;
|
||||
}
|
||||
buffer[0] = txt_esc(this.tmp.splice_delimiter) + buffer[0];
|
||||
}
|
||||
} else {
|
||||
|
@ -7828,6 +7824,11 @@ CSL.Node.layout = {
|
|||
if (this.tokentype === CSL.START && !state.tmp.cite_affixes[state.build.area]) {
|
||||
func = function (state, Item) {
|
||||
state.tmp.done_vars = [];
|
||||
if (state.opt.suppressedJurisdictions[Item["country"]]
|
||||
&& Item["country"]
|
||||
&& ["treaty", "patent"].indexOf(Item.type) === -1) {
|
||||
state.tmp.done_vars.push("country");
|
||||
}
|
||||
if (!state.tmp.just_looking && state.registry.registry[Item.id] && state.registry.registry[Item.id].parallel) {
|
||||
state.tmp.done_vars.push("first-reference-note-number");
|
||||
}
|
||||
|
@ -8537,7 +8538,7 @@ CSL.NameOutput.prototype._checkNickname = function (name) {
|
|||
if (author && this.state.sys.getAbbreviation && !(this.item && this.item["suppress-author"])) {
|
||||
var normalizedKey = author;
|
||||
if (this.state.sys.normalizeAbbrevsKey) {
|
||||
normalizedKey = this.state.sys.normalizeAbbrevsKey(author);
|
||||
normalizedKey = this.state.sys.normalizeAbbrevsKey("author", author);
|
||||
}
|
||||
this.state.transform.loadAbbreviation("default", "nickname", normalizedKey);
|
||||
var myLocalName = this.state.transform.abbrevs["default"].nickname[normalizedKey];
|
||||
|
@ -9800,7 +9801,7 @@ CSL.NameOutput.prototype.fixupInstitution = function (name, varname, listpos) {
|
|||
for (var j = 0, jlen = long_form.length; j < jlen; j += 1) {
|
||||
var normalizedKey = long_form[j];
|
||||
if (this.state.sys.normalizeAbbrevsKey) {
|
||||
normalizedKey = this.state.sys.normalizeAbbrevsKey(long_form[j]);
|
||||
normalizedKey = this.state.sys.normalizeAbbrevsKey(varname, long_form[j]);
|
||||
}
|
||||
jurisdiction = this.state.transform.loadAbbreviation(jurisdiction, "institution-part", normalizedKey);
|
||||
if (this.state.transform.abbrevs[jurisdiction]["institution-part"][normalizedKey]) {
|
||||
|
@ -9849,7 +9850,7 @@ CSL.NameOutput.prototype._splitInstitution = function (value, v, i) {
|
|||
var str = splitInstitution.slice(0, j).join("|");
|
||||
var normalizedKey = str;
|
||||
if (this.state.sys.normalizeAbbrevsKey) {
|
||||
normalizedKey = this.state.sys.normalizeAbbrevsKey(str);
|
||||
normalizedKey = this.state.sys.normalizeAbbrevsKey(v, str);
|
||||
}
|
||||
jurisdiction = this.state.transform.loadAbbreviation(jurisdiction, "institution-entire", normalizedKey);
|
||||
if (this.state.transform.abbrevs[jurisdiction]["institution-entire"][normalizedKey]) {
|
||||
|
@ -10636,7 +10637,7 @@ CSL.Node.text = {
|
|||
var abbrfall = false;
|
||||
var altvar = false;
|
||||
var transfall = false;
|
||||
if (form === "short") {
|
||||
if (form === "short" || ["country", "jurisdiction"].indexOf(this.variables_real[0]) > -1) {
|
||||
if (this.variables_real[0] === "container-title") {
|
||||
altvar = "journalAbbreviation";
|
||||
} else if (this.variables_real[0] === "title") {
|
||||
|
@ -12408,23 +12409,57 @@ CSL.Transform = function (state) {
|
|||
this.abbrevs = {};
|
||||
this.abbrevs["default"] = new state.sys.AbbreviationSegments();
|
||||
this.getTextSubField = getTextSubField;
|
||||
function abbreviate(state, Item, altvar, basevalue, myabbrev_family, use_field) {
|
||||
var value;
|
||||
myabbrev_family = CSL.FIELD_CATEGORY_REMAP[myabbrev_family];
|
||||
function getCountryOrJurisdiction(variable, normalizedKey, quashCountry) {
|
||||
var value = "";
|
||||
if (state.sys.getHumanForm) {
|
||||
if (variable === "country") {
|
||||
value = state.sys.getHumanForm(normalizedKey.toLowerCase(), false, true);
|
||||
value = value.split("|")[0];
|
||||
} else if (variable === "jurisdiction") {
|
||||
value = state.sys.getHumanForm(normalizedKey.toLowerCase(), false, true);
|
||||
if (!quashCountry) {
|
||||
value = value.split("|").slice(1).join(", ");
|
||||
} else {
|
||||
value = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function abbreviate(state, tok, Item, altvar, basevalue, family_var, use_field, form) {
|
||||
var value = "";
|
||||
var myabbrev_family = CSL.FIELD_CATEGORY_REMAP[family_var];
|
||||
if (!myabbrev_family) {
|
||||
return basevalue;
|
||||
}
|
||||
var variable = myabbrev_family;
|
||||
value = "";
|
||||
var variable = family_var;
|
||||
var normalizedKey = basevalue;
|
||||
if (state.sys.normalizeAbbrevsKey) {
|
||||
normalizedKey = state.sys.normalizeAbbrevsKey(family_var, basevalue);
|
||||
}
|
||||
var quashCountry = false;
|
||||
if (variable === "jurisdiction" && normalizedKey) {
|
||||
quashCountry = normalizedKey.indexOf(":") === -1;
|
||||
}
|
||||
if (state.sys.getAbbreviation) {
|
||||
var normalizedKey = basevalue;
|
||||
if (state.sys.normalizeAbbrevsKey) {
|
||||
normalizedKey = state.sys.normalizeAbbrevsKey(basevalue);
|
||||
if (["jurisdiction", "country", "language-name", "language-name-original"].indexOf(variable) > -1) {
|
||||
var loadJurisdiction = "default";
|
||||
} else if (Item.jurisdiction) {
|
||||
var loadJurisdiction = Item.jurisdiction;
|
||||
} else {
|
||||
var loadJurisdiction = "default";
|
||||
}
|
||||
var jurisdiction = state.transform.loadAbbreviation(Item.jurisdiction, myabbrev_family, normalizedKey, Item.type, true);
|
||||
if (state.transform.abbrevs[jurisdiction][myabbrev_family] && normalizedKey && state.sys.getAbbreviation) {
|
||||
if (state.transform.abbrevs[jurisdiction][myabbrev_family][normalizedKey]) {
|
||||
value = state.transform.abbrevs[jurisdiction][myabbrev_family][normalizedKey].replace("{stet}",basevalue);
|
||||
var jurisdiction = state.transform.loadAbbreviation(loadJurisdiction, myabbrev_family, normalizedKey, Item.type);
|
||||
if (state.transform.abbrevs[jurisdiction][myabbrev_family] && normalizedKey) {
|
||||
var abbrev = state.transform.abbrevs[jurisdiction][myabbrev_family][normalizedKey];
|
||||
if (tok.strings.form === "short" && abbrev) {
|
||||
if (quashCountry) {
|
||||
value = "";
|
||||
} else {
|
||||
value = abbrev;
|
||||
}
|
||||
} else {
|
||||
value = getCountryOrJurisdiction(variable, normalizedKey, quashCountry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12433,16 +12468,12 @@ CSL.Transform = function (state) {
|
|||
&& altvar && Item[altvar] && use_field) {
|
||||
value = Item[altvar];
|
||||
}
|
||||
if (!value) {
|
||||
if (!value && !state.sys.getAbbreviation && state.sys.getHumanForm) {
|
||||
value = getCountryOrJurisdiction(variable, normalizedKey, quashCountry);
|
||||
}
|
||||
if (!value && !quashCountry && (!state.sys.getHumanForm || variable !== "jurisdiction")) {
|
||||
value = basevalue;
|
||||
}
|
||||
if (value && value.match(/^\!(?:[^>]+,)*here(?:,[^>]+)*>>>/)) {
|
||||
if (variable === "jurisdiction" && ["treaty", "patent"].indexOf(Item.type) > -1) {
|
||||
value = value.replace(/^\![^>]*>>>\s*/, "");
|
||||
} else {
|
||||
value = false;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function getFieldLocale(Item,field) {
|
||||
|
@ -12505,12 +12536,10 @@ CSL.Transform = function (state) {
|
|||
if (opt && Item.multi && Item.multi._keys[field] && Item.multi._keys[field][opt]) {
|
||||
ret.name = Item.multi._keys[field][opt];
|
||||
ret.locale = opt;
|
||||
if (field === 'jurisdiction') jurisdictionName = ret.name;
|
||||
break;
|
||||
} else if (o && Item.multi && Item.multi._keys[field] && Item.multi._keys[field][o]) {
|
||||
ret.name = Item.multi._keys[field][o];
|
||||
ret.locale = o;
|
||||
if (field === 'jurisdiction') jurisdictionName = ret.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -12520,9 +12549,7 @@ CSL.Transform = function (state) {
|
|||
}
|
||||
}
|
||||
ret.token = CSL.Util.cloneToken(this);
|
||||
if (state.sys.getHumanForm && ret.name && field === 'jurisdiction') {
|
||||
ret.name = CSL.getJurisdictionNameAndSuppress(state, Item[field], jurisdictionName, this.strings.jurisdiction_depth);
|
||||
} else if (["title", "container-title"].indexOf(field) > -1) {
|
||||
if (["title", "container-title"].indexOf(field) > -1) {
|
||||
if (!usedOrig
|
||||
&& (!ret.token.strings["text-case"]
|
||||
|| ret.token.strings["text-case"] === "sentence"
|
||||
|
@ -12559,7 +12586,7 @@ CSL.Transform = function (state) {
|
|||
return jurisdiction;
|
||||
}
|
||||
this.loadAbbreviation = loadAbbreviation;
|
||||
function publisherCheck (tok, Item, primary, myabbrev_family) {
|
||||
function publisherCheck (tok, Item, primary, family_var) {
|
||||
var varname = tok.variables[0];
|
||||
if (state.publisherOutput && primary) {
|
||||
if (["publisher","publisher-place"].indexOf(varname) === -1) {
|
||||
|
@ -12572,7 +12599,7 @@ CSL.Transform = function (state) {
|
|||
state.publisherOutput[varname + "-list"] = lst;
|
||||
}
|
||||
for (var i = 0, ilen = lst.length; i < ilen; i += 1) {
|
||||
lst[i] = abbreviate(state, Item, false, lst[i], myabbrev_family, true);
|
||||
lst[i] = abbreviate(state, tok, Item, false, lst[i], family_var, true);
|
||||
}
|
||||
state.tmp[varname + "-token"] = tok;
|
||||
return true;
|
||||
|
@ -12580,7 +12607,7 @@ CSL.Transform = function (state) {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
function getOutputFunction(variables, myabbrev_family, abbreviation_fallback, alternative_varname, transform_fallback) {
|
||||
function getOutputFunction(variables, family_var, abbreviation_fallback, alternative_varname, transform_fallback) {
|
||||
var localesets;
|
||||
var langPrefs = CSL.LangPrefsMap[variables[0]];
|
||||
if (!langPrefs) {
|
||||
|
@ -12632,7 +12659,7 @@ CSL.Transform = function (state) {
|
|||
primary_locale = res.locale;
|
||||
var primary_tok = res.token;
|
||||
var primaryUsedOrig = res.usedOrig;
|
||||
if (publisherCheck(this, Item, primary, myabbrev_family)) {
|
||||
if (publisherCheck(this, Item, primary, family_var)) {
|
||||
return null;
|
||||
}
|
||||
secondary = false;
|
||||
|
@ -12649,13 +12676,17 @@ CSL.Transform = function (state) {
|
|||
tertiary_locale = res.locale;
|
||||
var tertiary_tok = res.token;
|
||||
}
|
||||
if (myabbrev_family) {
|
||||
primary = abbreviate(state, Item, alternative_varname, primary, myabbrev_family, true);
|
||||
if (family_var) {
|
||||
primary = abbreviate(state, primary_tok, Item, alternative_varname, primary, family_var, true);
|
||||
if (primary) {
|
||||
primary = quashCheck(primary);
|
||||
}
|
||||
secondary = abbreviate(state, Item, false, secondary, myabbrev_family, true);
|
||||
tertiary = abbreviate(state, Item, false, tertiary, myabbrev_family, true);
|
||||
if (secondary) {
|
||||
secondary = abbreviate(state, secondary_tok, Item, false, secondary, family_var, true);
|
||||
}
|
||||
if (tertiary) {
|
||||
tertiary = abbreviate(state, tertiary_tok, Item, false, tertiary, family_var, true);
|
||||
}
|
||||
}
|
||||
var primaryPrefix;
|
||||
if (slot.primary === "locale-translit") {
|
||||
|
@ -13089,8 +13120,8 @@ CSL.Engine.prototype.dateParseArray = function (date_obj) {
|
|||
exts = ["", "_end"];
|
||||
for (var i = 0, ilen = dp.length; i < ilen; i += 1) {
|
||||
for (var j = 0, jlen = CSL.DATE_PARTS.length; j < jlen; j += 1) {
|
||||
if ("undefined" === typeof dp[i][j]) {
|
||||
ret[(CSL.DATE_PARTS[j] + exts[i])] = dp[i][j];
|
||||
if (isNaN(parseInt(dp[i][j], 10))) {
|
||||
ret[(CSL.DATE_PARTS[j] + exts[i])] = undefined;
|
||||
} else {
|
||||
ret[(CSL.DATE_PARTS[j] + exts[i])] = parseInt(dp[i][j], 10);
|
||||
}
|
||||
|
@ -13336,7 +13367,7 @@ CSL.Util.Dates.year.imperial = function (state, num, end, makeShort) {
|
|||
if (label && offset) {
|
||||
var normalizedKey = label;
|
||||
if (state.sys.normalizeAbbrevsKey) {
|
||||
normalizedKey = state.sys.normalizeAbbrevsKey(label);
|
||||
normalizedKey = state.sys.normalizeAbbrevsKey("number", label);
|
||||
}
|
||||
if (!state.transform.abbrevs['default']['number'][normalizedKey]) {
|
||||
state.transform.loadAbbreviation('default', "number", normalizedKey);
|
||||
|
@ -13911,9 +13942,9 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable, type)
|
|||
info.plural = 0;
|
||||
info.labelVisibility = false;
|
||||
}
|
||||
var m = val.match(/^([a-zA-Z0]*)([0-9]+(?:[a-zA-Z]*|[-,a-zA-Z]+))$/);
|
||||
var m = val.match(/^([0-9]*[a-zA-Z]+0*)?([0-9]+(?:[a-zA-Z]*|[-,a-zA-Z]+))$/);
|
||||
if (m) {
|
||||
info.particle = m[1];
|
||||
info.particle = m[1] ? m[1] : "";
|
||||
info.value = m[2];
|
||||
} else {
|
||||
info.particle = "";
|
||||
|
@ -13953,9 +13984,9 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable, type)
|
|||
var label = defaultLabel;
|
||||
var origLabel = "";
|
||||
for (var i=0,ilen=elems.length;i<ilen;i += 2) {
|
||||
var m = elems[i].match(/((?:^| )(?:[a-z]|[a-z][a-z]|[a-z][a-z][a-z]|[a-z][a-z][a-z][a-z])\. *)/g);
|
||||
var m = elems[i].match(/((?:^| )(?:[a-z]|[a-z][a-z]|[a-z][a-z][a-z]|[a-z][a-z][a-z][a-z])(?:\.| ) *)/g);
|
||||
if (m) {
|
||||
var lst = elems[i].split(/(?:(?:^| )(?:[a-z]|[a-z][a-z]|[a-z][a-z][a-z]|[a-z][a-z][a-z][a-z])\. *)/);
|
||||
var lst = elems[i].split(/(?:(?:^| )(?:[a-z]|[a-z][a-z]|[a-z][a-z][a-z]|[a-z][a-z][a-z][a-z])(?:\.| ) *)/);
|
||||
for (var j=lst.length-1;j>0;j--) {
|
||||
if (lst[j-1] && (!lst[j].match(/^[0-9]+([-;,:a-zA-Z]*)$/) || !lst[j-1].match(/^[0-9]+([-;,:a-zA-Z]*)$/))) {
|
||||
lst[j-1] = lst[j-1] + m[j-1] + lst[j];
|
||||
|
@ -14035,75 +14066,76 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable, type)
|
|||
if ((mVal && mVal[1]) || (mCurrentLabel && mCurrentLabel[1])) {
|
||||
currentLabelInfo.collapsible = false;
|
||||
}
|
||||
var isCollapsible = currentLabelInfo.collapsible;
|
||||
if (!isCollapsible) {
|
||||
if (i>0 && val.match(/^[ivxlcmIVXLCM]+$/) && values[i-1].value.match(/^[ivxlcmIVXLCM]+$/)) {
|
||||
isCollapsible = true;
|
||||
} else if (i>0 && val.match(/^[0-9]+(?:\s|$)/) && values[i-1].value.match(/^[0-9]+(?:\s|$)/)) {
|
||||
isCollapsible = true;
|
||||
if (undefined === values[i].collapsible) {
|
||||
for (var j=i,jlen=i+currentLabelInfo.count;j<jlen;j++) {
|
||||
if (isNaN(parseInt(values[j].value)) && !values[j].value.match(/^[ivxlcmIVXLCM]+$/)) {
|
||||
values[j].collapsible = false;
|
||||
} else {
|
||||
values[j].collapsible = true;
|
||||
}
|
||||
}
|
||||
currentLabelInfo.collapsible = values[i].collapsible;
|
||||
}
|
||||
for (var j=currentLabelInfo.pos,jlen=values.length; j<jlen; j++) {
|
||||
if (currentLabelInfo.label === values[j].label && currentLabelInfo.count > 1 && isCollapsible) {
|
||||
var isCollapsible = currentLabelInfo.collapsible;
|
||||
for (var j=currentLabelInfo.pos,jlen=(currentLabelInfo.pos + currentLabelInfo.count); j<jlen; j++) {
|
||||
if (currentLabelInfo.count > 1 && isCollapsible) {
|
||||
values[j].plural = 1;
|
||||
}
|
||||
values[j].numeric = currentLabelInfo.numeric;
|
||||
values[j].collapsible = currentLabelInfo.collapsible;
|
||||
}
|
||||
currentLabelInfo.label = values[i].label;
|
||||
currentLabelInfo.count = 1;
|
||||
currentLabelInfo.pos = i;
|
||||
currentLabelInfo.numeric = true;
|
||||
currentLabelInfo.collapsible = true;
|
||||
}
|
||||
function setPluralsAndNumerics(values) {
|
||||
var currentLabelInfo = {
|
||||
label: null,
|
||||
count: 1,
|
||||
numeric: true,
|
||||
collapsible: true,
|
||||
pos: 0
|
||||
}
|
||||
var masterLabel = values.length ? values[0].label : null;
|
||||
for (var i=0,ilen=values.length;i<ilen;i++) {
|
||||
if (values[i].label) {
|
||||
if (values[i].label === currentLabelInfo.label) {
|
||||
currentLabelInfo.count++;
|
||||
} else {
|
||||
fixNumericAndCount(values, i, currentLabelInfo);
|
||||
if (currentLabelInfo.pos === 0) {
|
||||
if (variable === "locator" || variable === "number") {
|
||||
if (!me.getTerm(CSL.STATUTE_SUBDIV_STRINGS[currentLabelInfo.label]) && currentLabelInfo.label.slice(0, 4) !== "var:") {
|
||||
values[currentLabelInfo.pos].labelVisibility = true;
|
||||
}
|
||||
}
|
||||
if (["locator", "number"].indexOf(variable) === -1) {
|
||||
if (CSL.STATUTE_SUBDIV_STRINGS[currentLabelInfo.label] !== variable && currentLabelInfo.label.slice(0, 4) !== "var:") {
|
||||
values[0].labelVisibility = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (values[i-1].label !== values[i].label && currentLabelInfo.label.slice(0, 4) !== "var:") {
|
||||
values[currentLabelInfo.pos].labelVisibility = true;
|
||||
}
|
||||
function fixLabelVisibility(values, groupStartPos, currentLabelInfo) {
|
||||
if (currentLabelInfo.label.slice(0, 4) !== "var:") {
|
||||
if (currentLabelInfo.pos === 0) {
|
||||
if (variable === "locator" || variable === "number") {
|
||||
if (!me.getTerm(CSL.STATUTE_SUBDIV_STRINGS[currentLabelInfo.label])) {
|
||||
values[currentLabelInfo.pos].labelVisibility = true;
|
||||
}
|
||||
}
|
||||
if (["locator", "number"].indexOf(variable) === -1) {
|
||||
if (CSL.STATUTE_SUBDIV_STRINGS[currentLabelInfo.label] !== variable) {
|
||||
values[0].labelVisibility = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
values[currentLabelInfo.pos].labelVisibility = true;
|
||||
}
|
||||
}
|
||||
fixNumericAndCount(values, values.length-1, currentLabelInfo);
|
||||
}
|
||||
function setPluralsAndNumerics(values) {
|
||||
if (values.length === 0) return;
|
||||
var groupStartPos = 0;
|
||||
var groupCount = 1;
|
||||
for (var i=1,ilen=values.length;i<ilen;i++) {
|
||||
var lastVal = values[i-1];
|
||||
var thisVal = values[i];
|
||||
if (lastVal.label === thisVal.label && lastVal.particle === lastVal.particle) {
|
||||
groupCount++;
|
||||
} else {
|
||||
var currentLabelInfo = JSON.parse(JSON.stringify(values[groupStartPos]));
|
||||
currentLabelInfo.pos = groupStartPos;
|
||||
currentLabelInfo.count = groupCount;
|
||||
currentLabelInfo.numeric = true;
|
||||
fixNumericAndCount(values, groupStartPos, currentLabelInfo);
|
||||
if (i === 0 || (lastVal.label !== thisVal.label)) {
|
||||
fixLabelVisibility(values, groupStartPos, currentLabelInfo);
|
||||
}
|
||||
groupStartPos = i;
|
||||
groupCount = 1;
|
||||
}
|
||||
}
|
||||
var currentLabelInfo = JSON.parse(JSON.stringify(values[groupStartPos]));
|
||||
currentLabelInfo.pos = groupStartPos;
|
||||
currentLabelInfo.count = groupCount;
|
||||
currentLabelInfo.numeric = true;
|
||||
fixNumericAndCount(values, groupStartPos, currentLabelInfo);
|
||||
fixLabelVisibility(values, groupStartPos, currentLabelInfo);
|
||||
if (values.length && values[0].numeric && variable.slice(0, 10) === "number-of-") {
|
||||
if (parseInt(ItemObject[variable], 10) > 1) {
|
||||
values[0].plural = 1;
|
||||
}
|
||||
}
|
||||
for (var i=0,ilen=values.length;i<ilen;i++) {
|
||||
if (!values[i].numeric) {
|
||||
var origLabel = values[i].origLabel ? values[i].origLabel : "";
|
||||
values[i].value = (origLabel + values[i].value).trim();
|
||||
if (values[i].label !== values[0].label) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function setStyling(values) {
|
||||
var masterNode = CSL.Util.cloneToken(node);
|
||||
|
@ -14204,13 +14236,16 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable, type)
|
|||
}
|
||||
var val = values[i];
|
||||
var isPage = checkPage(variable, val);
|
||||
if (isPage) {
|
||||
if (isPage && !isNaN(parseInt(values[i-1].value)) && !isNaN(parseInt(values[i].value))) {
|
||||
var str = values[i-1].particle + values[i-1].value + " - " + values[i].particle + values[i].value;
|
||||
str = me.fun.page_mangler(str);
|
||||
} else {
|
||||
if (("" + values[i-1].value).match(/^([0-9]+|[ivxlcmIVXLCM]+)$/) && ("" + values[i].value).match(/^([0-9]+|[ivxlcmIVXLCM]+)$/)) {
|
||||
values[i-1].joiningSuffix = me.getTerm("page-range-delimiter");
|
||||
}
|
||||
str = values[i-1].value + stripHyphenBackslash(values[i-1].joiningSuffix) + values[i].value;
|
||||
}
|
||||
var m = str.match(/^([a-zA-Z]?0*)([0-9]+)(\s*[^0-9]+\s*)([-,a-zA-Z]?0*)([0-9]+)$/);
|
||||
var m = str.match(/^((?:[0-9]*[a-zA-Z]+0*))?([0-9]+)(\s*[^0-9]+\s*)([-,a-zA-Z]?0*)([0-9]+)$/);
|
||||
if (m) {
|
||||
var rangeDelimiter = m[3];
|
||||
rangeDelimiter = fixupRangeDelimiter(variable, val, rangeDelimiter, values[i].numeric);
|
||||
|
@ -14236,9 +14271,6 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable, type)
|
|||
currentInfo.count = 0;
|
||||
currentInfo.label = null;
|
||||
var isNumeric = val.numeric;
|
||||
if (i<(values.length-1) && !isNumeric && val.value.match(/^[ivxlcmIVXLCM]+$/) && values[i+1].value.match(/^[ivxlcmIVXLCM]+$/)) {
|
||||
isNumeric = true;
|
||||
}
|
||||
val.joiningSuffix = fixupRangeDelimiter(variable, val, val.joiningSuffix, isNumeric);
|
||||
} else if (currentInfo.label === val.label && val.joiningSuffix === "-") {
|
||||
currentInfo.count = 1;
|
||||
|
@ -14384,7 +14416,7 @@ CSL.Util.outputNumericField = function(state, varname, itemID) {
|
|||
}
|
||||
}
|
||||
if (num.collapsible) {
|
||||
if (num.value.match(/^[0-9]+$/)) {
|
||||
if (num.value.match(/^[1-9][0-9]*$/)) {
|
||||
var blob = new CSL.NumericBlob(num.particle, parseInt(num.value, 10), numStyling, itemID);
|
||||
} else {
|
||||
var blob = new CSL.NumericBlob(num.particle, num.value, numStyling, itemID);
|
||||
|
@ -14415,7 +14447,7 @@ CSL.Util.PageRangeMangler = {};
|
|||
CSL.Util.PageRangeMangler.getFunction = function (state, rangeType) {
|
||||
var rangerex, pos, len, stringify, listify, expand, minimize, minimize_internal, chicago, lst, m, b, e, ret, begin, end, ret_func, ppos, llen;
|
||||
var range_delimiter = state.getTerm(rangeType + "-range-delimiter");
|
||||
rangerex = /([a-zA-Z]*)([0-9]+)\s*(?:\u2013|-)\s*([a-zA-Z]*)([0-9]+)/;
|
||||
rangerex = /([0-9]*[a-zA-Z]+0*)?([0-9]+)\s*(?:\u2013|-)\s*([0-9]*[a-zA-Z]+0*)?([0-9]+)/;
|
||||
stringify = function (lst) {
|
||||
len = lst.length;
|
||||
for (pos = 1; pos < len; pos += 2) {
|
||||
|
@ -14433,8 +14465,8 @@ CSL.Util.PageRangeMangler.getFunction = function (state, rangeType) {
|
|||
var this_range_delimiter = range_delimiter === "-" ? "" : range_delimiter;
|
||||
var delimRex = new RegExp("([^\\\\])[-" + this_range_delimiter + "\\u2013]", "g");
|
||||
str = str.replace(delimRex, "$1 - ").replace(/\s+-\s+/g, " - ");
|
||||
var rexm = new RegExp("([a-zA-Z]*[0-9]+" + hyphens + "[a-zA-Z]*[0-9]+)", "g");
|
||||
var rexlst = new RegExp("[a-zA-Z]*[0-9]+" + hyphens + "[a-zA-Z]*[0-9]+");
|
||||
var rexm = new RegExp("((?:[0-9]*[a-zA-Z]+0*)?[0-9]+" + hyphens + "(?:[0-9]*[a-zA-Z]+0*)?[0-9]+)", "g");
|
||||
var rexlst = new RegExp("(?:[0-9]*[a-zA-Z]+0*)?[0-9]+" + hyphens + "(?:[0-9]*[a-zA-Z]+0*)?[0-9]+");
|
||||
m = str.match(rexm);
|
||||
lst = str.split(rexlst);
|
||||
if (lst.length === 0) {
|
||||
|
@ -14460,7 +14492,7 @@ CSL.Util.PageRangeMangler.getFunction = function (state, rangeType) {
|
|||
m[4] = m[2].slice(0, (m[2].length - m[4].length)) + m[4];
|
||||
}
|
||||
if (parseInt(m[2], 10) < parseInt(m[4], 10)) {
|
||||
m[3] = range_delimiter + m[1];
|
||||
m[3] = range_delimiter + (m[1] ? m[1] : "");
|
||||
lst[pos] = m.slice(1);
|
||||
}
|
||||
}
|
||||
|
@ -15017,7 +15049,8 @@ CSL.Util.FlipFlopper = function(state) {
|
|||
if (str.slice(0, 1) === " " && !str.match(/^\s+[\'\"]/)) {
|
||||
leadingSpace = true;
|
||||
}
|
||||
var str = " " + str;
|
||||
var rex = new RegExp("(" + CSL.ROMANESQUE_REGEXP.source + ")\u2019(" + CSL.ROMANESQUE_REGEXP.source + ")", "g")
|
||||
var str = " " + str.replace(rex, "$1\'$2");
|
||||
var doppel = _doppelString(str);
|
||||
if (doppel.tags.length === 0) return;
|
||||
var quoteFormSeen = false;
|
||||
|
@ -15113,7 +15146,7 @@ CSL.Output.Formatters = new function () {
|
|||
this.title = title;
|
||||
this["capitalize-first"] = capitalizeFirst;
|
||||
this["capitalize-all"] = capitalizeAll;
|
||||
var rexStr = "(?:\u2018|\u2019|\u201C|\u201D| \"| \'|\"|\'|[-\–\—\/.,;?!:]|\\[|\\]|\\(|\\)|<span style=\"font-variant: small-caps;\">|<span class=\"no(?:case|decor)\">|<\/span>|<\/?(?:i|sc|b|sub|sup)>)";
|
||||
var rexStr = "(?:\u2018|\u2019|\u201C|\u201D| \"| \'|\"|\'|[-\u2013\u2014\/.,;?!:]|\\[|\\]|\\(|\\)|<span style=\"font-variant: small-caps;\">|<span class=\"no(?:case|decor)\">|<\/span>|<\/?(?:i|sc|b|sub|sup)>)";
|
||||
var tagDoppel = new CSL.Doppeler(rexStr, function(str) {
|
||||
return str.replace(/(<span)\s+(class=\"no(?:case|decor)\")[^>]*(>)/g, "$1 $2$3").replace(/(<span)\s+(style=\"font-variant:)\s*(small-caps);?(\")[^>]*(>)/g, "$1 $2 $3;$4$5");
|
||||
});
|
||||
|
|
|
@ -1829,6 +1829,7 @@ Zotero.CollectionTreeView.prototype.canDropCheckAsync = Zotero.Promise.coroutine
|
|||
if (treeRow.ref.libraryID != draggedCollection.libraryID) {
|
||||
// Disallow if linked collection already exists
|
||||
if (yield draggedCollection.getLinkedCollection(treeRow.ref.libraryID, true)) {
|
||||
Zotero.debug("Linked collection already exists in library");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1840,6 +1841,7 @@ Zotero.CollectionTreeView.prototype.canDropCheckAsync = Zotero.Promise.coroutine
|
|||
// If this is allowed in the future for the root collection,
|
||||
// need to allow drag only to root
|
||||
if (yield descendent.getLinkedCollection(treeRow.ref.libraryID, true)) {
|
||||
Zotero.debug("Linked subcollection already exists in library");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
192
chrome/content/zotero/xpcom/connector/httpIntegrationClient.js
Normal file
192
chrome/content/zotero/xpcom/connector/httpIntegrationClient.js
Normal file
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2017 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
http://zotero.org
|
||||
|
||||
This file is part of Zotero.
|
||||
|
||||
Zotero is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Zotero is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is a HTTP-based integration interface for Zotero. The actual
|
||||
* heavy lifting occurs in the connector and/or wherever the connector delegates the heavy
|
||||
* lifting to.
|
||||
*/
|
||||
Zotero.HTTPIntegrationClient = {
|
||||
deferredResponse: null,
|
||||
sendCommandPromise: Zotero.Promise.resolve(),
|
||||
sendCommand: async function(command, args=[]) {
|
||||
let payload = JSON.stringify({command, arguments: args});
|
||||
function sendCommand() {
|
||||
Zotero.HTTPIntegrationClient.deferredResponse = Zotero.Promise.defer();
|
||||
Zotero.HTTPIntegrationClient.sendResponse.apply(Zotero.HTTPIntegrationClient,
|
||||
[200, 'application/json', payload]);
|
||||
return Zotero.HTTPIntegrationClient.deferredResponse.promise;
|
||||
}
|
||||
// Force issued commands to occur sequentially, since these are really just
|
||||
// a sequence of HTTP requests and responses.
|
||||
// We might want to consider something better later, but this has the advantage of
|
||||
// being easy to interface with as a Client, as you don't need SSE or WS.
|
||||
if (command != 'Document.complete') {
|
||||
Zotero.HTTPIntegrationClient.sendCommandPromise =
|
||||
Zotero.HTTPIntegrationClient.sendCommandPromise.then(sendCommand, sendCommand);
|
||||
} else {
|
||||
await Zotero.HTTPIntegrationClient.sendCommandPromise;
|
||||
sendCommand();
|
||||
}
|
||||
return Zotero.HTTPIntegrationClient.sendCommandPromise;
|
||||
}
|
||||
};
|
||||
|
||||
Zotero.HTTPIntegrationClient.Application = function() {
|
||||
this.primaryFieldType = "Http";
|
||||
this.secondaryFieldType = "Http";
|
||||
this.outputFormat = 'html';
|
||||
this.supportedNotes = ['footnotes'];
|
||||
};
|
||||
Zotero.HTTPIntegrationClient.Application.prototype = {
|
||||
getActiveDocument: async function() {
|
||||
let result = await Zotero.HTTPIntegrationClient.sendCommand('Application.getActiveDocument');
|
||||
this.outputFormat = result.outputFormat || this.outputFormat;
|
||||
this.supportedNotes = result.supportedNotes || this.supportedNotes;
|
||||
return new Zotero.HTTPIntegrationClient.Document(result.documentID);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* See integrationTests.js
|
||||
*/
|
||||
Zotero.HTTPIntegrationClient.Document = function(documentID) {
|
||||
this._documentID = documentID;
|
||||
};
|
||||
for (let method of ["activate", "canInsertField", "displayAlert", "getDocumentData",
|
||||
"setDocumentData", "setBibliographyStyle"]) {
|
||||
Zotero.HTTPIntegrationClient.Document.prototype[method] = async function() {
|
||||
return Zotero.HTTPIntegrationClient.sendCommand("Document."+method,
|
||||
[this._documentID].concat(Array.prototype.slice.call(arguments)));
|
||||
};
|
||||
}
|
||||
|
||||
// @NOTE Currently unused, prompts are done using the connector
|
||||
Zotero.HTTPIntegrationClient.Document.prototype._displayAlert = async function(dialogText, icon, buttons) {
|
||||
var ps = Services.prompt;
|
||||
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_OK)
|
||||
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_IS_STRING);
|
||||
|
||||
switch (buttons) {
|
||||
case DIALOG_BUTTONS_OK:
|
||||
buttonFlags = ps.BUTTON_POS_0_DEFAULT + ps.BUTTON_POS_0 * ps.BUTTON_TITLE_OK; break;
|
||||
case DIALOG_BUTTONS_OK_CANCEL:
|
||||
buttonFlags = ps.BUTTON_POS_0_DEFAULT + ps.STD_OK_CANCEL_BUTTONS; break;
|
||||
case DIALOG_BUTTONS_YES_NO:
|
||||
buttonFlags = ps.BUTTON_POS_0_DEFAULT + ps.STD_YES_NO_BUTTONS; break;
|
||||
case DIALOG_BUTTONS_YES_NO_CANCEL:
|
||||
buttonFlags = ps.BUTTON_POS_0_DEFAULT + ps.BUTTON_POS_0 * ps.BUTTON_TITLE_YES +
|
||||
ps.BUTTON_POS_1 * ps.BUTTON_TITLE_NO +
|
||||
ps.BUTTON_POS_2 * ps.BUTTON_TITLE_CANCEL; break;
|
||||
}
|
||||
|
||||
var result = ps.confirmEx(
|
||||
null,
|
||||
"Zotero",
|
||||
dialogText,
|
||||
buttonFlags,
|
||||
null, null, null,
|
||||
null,
|
||||
{}
|
||||
);
|
||||
|
||||
switch (buttons) {
|
||||
default:
|
||||
break;
|
||||
case DIALOG_BUTTONS_OK_CANCEL:
|
||||
case DIALOG_BUTTONS_YES_NO:
|
||||
result = (result+1)%2; break;
|
||||
case DIALOG_BUTTONS_YES_NO_CANCEL:
|
||||
result = result == 0 ? 2 : result == 2 ? 0 : 1; break;
|
||||
}
|
||||
await this.activate();
|
||||
return result;
|
||||
}
|
||||
Zotero.HTTPIntegrationClient.Document.prototype.cleanup = async function() {};
|
||||
Zotero.HTTPIntegrationClient.Document.prototype.cursorInField = async function(fieldType) {
|
||||
var retVal = await Zotero.HTTPIntegrationClient.sendCommand("Document.cursorInField", [this._documentID, fieldType]);
|
||||
if (!retVal) return null;
|
||||
return new Zotero.HTTPIntegrationClient.Field(this._documentID, retVal);
|
||||
};
|
||||
Zotero.HTTPIntegrationClient.Document.prototype.insertField = async function(fieldType, noteType) {
|
||||
var retVal = await Zotero.HTTPIntegrationClient.sendCommand("Document.insertField", [this._documentID, fieldType, parseInt(noteType) || 0]);
|
||||
return new Zotero.HTTPIntegrationClient.Field(this._documentID, retVal);
|
||||
};
|
||||
Zotero.HTTPIntegrationClient.Document.prototype.getFields = async function(fieldType) {
|
||||
var retVal = await Zotero.HTTPIntegrationClient.sendCommand("Document.getFields", [this._documentID, fieldType]);
|
||||
return retVal.map(field => new Zotero.HTTPIntegrationClient.Field(this._documentID, field));
|
||||
};
|
||||
Zotero.HTTPIntegrationClient.Document.prototype.convert = async function(fields, fieldType, noteTypes) {
|
||||
fields = fields.map((f) => f._id);
|
||||
await Zotero.HTTPIntegrationClient.sendCommand("Field.convert", [this._documentID, fields, fieldType, noteTypes]);
|
||||
};
|
||||
Zotero.HTTPIntegrationClient.Document.prototype.complete = async function() {
|
||||
Zotero.HTTPIntegrationClient.inProgress = false;
|
||||
Zotero.HTTPIntegrationClient.sendCommand("Document.complete", [this._documentID]);
|
||||
};
|
||||
|
||||
/**
|
||||
* See integrationTests.js
|
||||
*/
|
||||
Zotero.HTTPIntegrationClient.Field = function(documentID, json) {
|
||||
this._documentID = documentID;
|
||||
this._id = json.id;
|
||||
this._code = json.code;
|
||||
this._text = json.text;
|
||||
this._noteIndex = json.noteIndex;
|
||||
};
|
||||
Zotero.HTTPIntegrationClient.Field.prototype = {};
|
||||
|
||||
for (let method of ["delete", "select", "removeCode"]) {
|
||||
Zotero.HTTPIntegrationClient.Field.prototype[method] = async function() {
|
||||
return Zotero.HTTPIntegrationClient.sendCommand("Field."+method,
|
||||
[this._documentID, this._id].concat(Array.prototype.slice.call(arguments)));
|
||||
};
|
||||
}
|
||||
Zotero.HTTPIntegrationClient.Field.prototype.getText = async function() {
|
||||
return this._text;
|
||||
};
|
||||
Zotero.HTTPIntegrationClient.Field.prototype.setText = async function(text, isRich) {
|
||||
// The HTML will be stripped by Google Docs and and since we're
|
||||
// caching this value, we need to strip it ourselves
|
||||
var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
|
||||
.createInstance(Components.interfaces.nsIDOMParser);
|
||||
var doc = parser.parseFromString(text, "text/html");
|
||||
this._text = doc.documentElement.textContent;
|
||||
return Zotero.HTTPIntegrationClient.sendCommand("Field.setText", [this._documentID, this._id, text, true]);
|
||||
};
|
||||
Zotero.HTTPIntegrationClient.Field.prototype.getCode = async function() {
|
||||
return this._code;
|
||||
};
|
||||
Zotero.HTTPIntegrationClient.Field.prototype.setCode = async function(code) {
|
||||
this._code = code;
|
||||
return Zotero.HTTPIntegrationClient.sendCommand("Field.setCode", [this._documentID, this._id, code]);
|
||||
};
|
||||
Zotero.HTTPIntegrationClient.Field.prototype.getNoteIndex = async function() {
|
||||
return this._noteIndex;
|
||||
};
|
||||
Zotero.HTTPIntegrationClient.Field.prototype.equals = async function(arg) {
|
||||
return this._id === arg._id;
|
||||
};
|
|
@ -27,35 +27,47 @@ const CONNECTOR_API_VERSION = 2;
|
|||
Zotero.Server.Connector = {
|
||||
_waitingForSelection: {},
|
||||
|
||||
getSaveTarget: function () {
|
||||
var zp = Zotero.getActiveZoteroPane(),
|
||||
library = null,
|
||||
collection = null,
|
||||
editable = true;
|
||||
try {
|
||||
library = Zotero.Libraries.get(zp.getSelectedLibraryID());
|
||||
collection = zp.getSelectedCollection();
|
||||
editable = zp.collectionsView.editable;
|
||||
getSaveTarget: function (allowReadOnly) {
|
||||
var zp = Zotero.getActiveZoteroPane();
|
||||
var library = null;
|
||||
var collection = null;
|
||||
var editable = null;
|
||||
|
||||
if (zp && zp.collectionsView) {
|
||||
if (zp.collectionsView.editable || allowReadOnly) {
|
||||
library = Zotero.Libraries.get(zp.getSelectedLibraryID());
|
||||
collection = zp.getSelectedCollection();
|
||||
editable = zp.collectionsView.editable;
|
||||
}
|
||||
// If not editable, switch to My Library if it exists and is editable
|
||||
else {
|
||||
let userLibrary = Zotero.Libraries.userLibrary;
|
||||
if (userLibrary && userLibrary.editable) {
|
||||
Zotero.debug("Save target isn't editable -- switching to My Library");
|
||||
|
||||
// Don't wait for this, because we don't want to slow down all conenctor
|
||||
// requests by making this function async
|
||||
zp.collectionsView.selectByID(userLibrary.treeViewID);
|
||||
|
||||
library = userLibrary;
|
||||
collection = null;
|
||||
editable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
else {
|
||||
let id = Zotero.Prefs.get('lastViewedFolder');
|
||||
if (id) {
|
||||
let type = id[0];
|
||||
Zotero.debug(type);
|
||||
id = parseInt(('' + id).substr(1));
|
||||
|
||||
switch (type) {
|
||||
case 'L':
|
||||
library = Zotero.Libraries.get(id);
|
||||
editable = library.editable;
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
collection = Zotero.Collections.get(id);
|
||||
library = collection.library;
|
||||
editable = collection.editable;
|
||||
break;
|
||||
({ library, collection, editable } = this.resolveTarget(id));
|
||||
if (!editable && !allowReadOnly) {
|
||||
let userLibrary = Zotero.Libraries.userLibrary;
|
||||
if (userLibrary && userLibrary.editable) {
|
||||
Zotero.debug("Save target isn't editable -- switching to My Library");
|
||||
let treeViewID = userLibrary.treeViewID;
|
||||
Zotero.Prefs.set('lastViewedFolder', treeViewID);
|
||||
({ library, collection, editable } = this.resolveTarget(treeViewID));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,11 +75,38 @@ Zotero.Server.Connector = {
|
|||
// (which should never be the case anymore)
|
||||
if (!library) {
|
||||
let userLibrary = Zotero.Libraries.userLibrary;
|
||||
if (userLibrary) {
|
||||
if (userLibrary && userLibrary.editable) {
|
||||
library = userLibrary;
|
||||
}
|
||||
}
|
||||
|
||||
return { library, collection, editable };
|
||||
},
|
||||
|
||||
resolveTarget: function (targetID) {
|
||||
var library;
|
||||
var collection;
|
||||
var editable;
|
||||
|
||||
var type = targetID[0];
|
||||
var id = parseInt(('' + targetID).substr(1));
|
||||
|
||||
switch (type) {
|
||||
case 'L':
|
||||
library = Zotero.Libraries.get(id);
|
||||
editable = library.editable;
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
collection = Zotero.Collections.get(id);
|
||||
library = collection.library;
|
||||
editable = collection.editable;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Unsupported target type '${type}'`);
|
||||
}
|
||||
|
||||
return { library, collection, editable };
|
||||
}
|
||||
};
|
||||
|
@ -80,8 +119,8 @@ Zotero.Server.Connector.SessionManager = {
|
|||
return this._sessions.get(id);
|
||||
},
|
||||
|
||||
create: function (id) {
|
||||
// Legacy client
|
||||
create: function (id, action, requestData) {
|
||||
// Legacy connector
|
||||
if (!id) {
|
||||
Zotero.debug("No session id provided by client", 2);
|
||||
id = Zotero.Utilities.randomString();
|
||||
|
@ -90,7 +129,7 @@ Zotero.Server.Connector.SessionManager = {
|
|||
throw new Error(`Session ID ${id} exists`);
|
||||
}
|
||||
Zotero.debug("Creating connector save session " + id);
|
||||
var session = new Zotero.Server.Connector.SaveSession(id);
|
||||
var session = new Zotero.Server.Connector.SaveSession(id, action, requestData);
|
||||
this._sessions.set(id, session);
|
||||
this.gc();
|
||||
return session;
|
||||
|
@ -110,100 +149,169 @@ Zotero.Server.Connector.SessionManager = {
|
|||
};
|
||||
|
||||
|
||||
Zotero.Server.Connector.SaveSession = function (id) {
|
||||
Zotero.Server.Connector.SaveSession = function (id, action, requestData) {
|
||||
this.id = id;
|
||||
this.created = new Date();
|
||||
this._objects = {};
|
||||
this._action = action;
|
||||
this._requestData = requestData;
|
||||
this._items = new Set();
|
||||
};
|
||||
|
||||
Zotero.Server.Connector.SaveSession.prototype.addItem = async function (item) {
|
||||
return this._addObjects('item', [item]);
|
||||
return this.addItems([item]);
|
||||
};
|
||||
|
||||
Zotero.Server.Connector.SaveSession.prototype.addItems = async function (items) {
|
||||
return this._addObjects('item', items);
|
||||
for (let item of items) {
|
||||
this._items.add(item);
|
||||
}
|
||||
|
||||
// Update the items with the current target data, in case it changed since the save began
|
||||
await this._updateItems(items);
|
||||
};
|
||||
|
||||
Zotero.Server.Connector.SaveSession.prototype.update = async function (libraryID, collectionID, tags) {
|
||||
this._currentLibraryID = libraryID;
|
||||
this._currentCollectionID = collectionID;
|
||||
/**
|
||||
* Change the target data for this session and update any items that have already been saved
|
||||
*/
|
||||
Zotero.Server.Connector.SaveSession.prototype.update = async function (targetID, tags) {
|
||||
var previousTargetID = this._currentTargetID;
|
||||
this._currentTargetID = targetID;
|
||||
this._currentTags = tags || "";
|
||||
|
||||
// Select new destination in collections pane
|
||||
var win = Zotero.getActiveZoteroPane();
|
||||
if (win && win.collectionsView) {
|
||||
if (collectionID) {
|
||||
var targetID = "C" + collectionID;
|
||||
}
|
||||
else {
|
||||
var targetID = "L" + libraryID;
|
||||
}
|
||||
await win.collectionsView.selectByID(targetID);
|
||||
var zp = Zotero.getActiveZoteroPane();
|
||||
if (zp && zp.collectionsView) {
|
||||
await zp.collectionsView.selectByID(targetID);
|
||||
}
|
||||
// If window is closed, select target collection re-open
|
||||
else {
|
||||
Zotero.Prefs.set('lastViewedFolder', targetID);
|
||||
}
|
||||
|
||||
await this._updateObjects(this._objects);
|
||||
|
||||
// TODO: Update active item saver
|
||||
|
||||
// If a single item was saved, select it
|
||||
if (win && win.collectionsView) {
|
||||
if (this._objects && this._objects.item) {
|
||||
let items = Array.from(this._objects.item).filter(item => item.isTopLevelItem());
|
||||
if (items.length == 1) {
|
||||
await win.selectItem(items[0].id);
|
||||
// If moving from a non-filesEditable library to a filesEditable library, resave from
|
||||
// original data, since there might be files that weren't saved or were removed
|
||||
if (previousTargetID && previousTargetID != targetID) {
|
||||
let { library: oldLibrary } = Zotero.Server.Connector.resolveTarget(previousTargetID);
|
||||
let { library: newLibrary } = Zotero.Server.Connector.resolveTarget(targetID);
|
||||
if (oldLibrary != newLibrary && !oldLibrary.filesEditable && newLibrary.filesEditable) {
|
||||
Zotero.debug("Resaving items to filesEditable library");
|
||||
if (this._action == 'saveItems' || this._action == 'saveSnapshot') {
|
||||
// Delete old items
|
||||
for (let item of this._items) {
|
||||
await item.eraseTx();
|
||||
}
|
||||
let actionUC = Zotero.Utilities.capitalize(this._action);
|
||||
let newItems = await Zotero.Server.Connector[actionUC].prototype[this._action](
|
||||
targetID, this._requestData
|
||||
);
|
||||
// saveSnapshot only returns a single item
|
||||
if (this._action == 'saveSnapshot') {
|
||||
newItems = [newItems];
|
||||
}
|
||||
this._items = new Set(newItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Zotero.Server.Connector.SaveSession.prototype._addObjects = async function (objectType, objects) {
|
||||
if (!this._objects[objectType]) {
|
||||
this._objects[objectType] = new Set();
|
||||
}
|
||||
|
||||
// If target has changed since the save began, update the objects
|
||||
await this._updateObjects({
|
||||
[objectType]: objects
|
||||
});
|
||||
await this._updateItems(this._items);
|
||||
|
||||
for (let object of objects) {
|
||||
this._objects[objectType].add(object);
|
||||
// If a single item was saved, select it (or its parent, if it now has one)
|
||||
if (zp && zp.collectionsView && this._items.size == 1) {
|
||||
let item = Array.from(this._items)[0];
|
||||
item = item.isTopLevelItem() ? item : item.parentItem;
|
||||
// Don't select if in trash
|
||||
if (!item.deleted) {
|
||||
await zp.selectItem(item.id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Zotero.Server.Connector.SaveSession.prototype._updateObjects = async function (objects) {
|
||||
if (Object.keys(objects).every(type => objects[type].length == 0)) {
|
||||
/**
|
||||
* Update the passed items with the current target and tags
|
||||
*/
|
||||
Zotero.Server.Connector.SaveSession.prototype._updateItems = Zotero.serial(async function (items) {
|
||||
if (items.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var libraryID = this._currentLibraryID;
|
||||
var collectionID = this._currentCollectionID;
|
||||
var { library, collection, editable } = Zotero.Server.Connector.resolveTarget(this._currentTargetID);
|
||||
var libraryID = library.libraryID;
|
||||
|
||||
var tags = this._currentTags.trim();
|
||||
tags = tags ? tags.split(/\s*,\s*/) : [];
|
||||
|
||||
Zotero.debug("Updating objects for connector save session " + this.id);
|
||||
Zotero.debug("Updating items for connector save session " + this.id);
|
||||
|
||||
return Zotero.DB.executeTransaction(async function () {
|
||||
for (let objectType in objects) {
|
||||
for (let object of objects[objectType]) {
|
||||
Zotero.debug(object.libraryID);
|
||||
Zotero.debug(libraryID);
|
||||
if (object.libraryID != libraryID) {
|
||||
throw new Error("Can't move objects between libraries");
|
||||
}
|
||||
|
||||
// Keep automatic tags
|
||||
let originalTags = object.getTags().filter(tag => tag.type == 1);
|
||||
|
||||
// Assign manual tags and collections to top-level items
|
||||
if (objectType == 'item' && object.isTopLevelItem()) {
|
||||
object.setTags(originalTags.concat(tags));
|
||||
object.setCollections(collectionID ? [collectionID] : []);
|
||||
await object.save();
|
||||
}
|
||||
for (let item of items) {
|
||||
let newLibrary = Zotero.Libraries.get(library.libraryID);
|
||||
|
||||
if (item.libraryID != libraryID) {
|
||||
let newItem = await item.moveToLibrary(libraryID);
|
||||
// Replace item in session
|
||||
this._items.delete(item);
|
||||
this._items.add(newItem);
|
||||
}
|
||||
|
||||
// If the item is now a child item (e.g., from Retrieve Metadata for PDF), update the
|
||||
// parent item instead
|
||||
if (!item.isTopLevelItem()) {
|
||||
item = item.parentItem;
|
||||
}
|
||||
// Skip deleted items
|
||||
if (!Zotero.Items.exists(item.id)) {
|
||||
Zotero.debug(`Item ${item.id} in save session no longer exists`);
|
||||
continue;
|
||||
}
|
||||
// Keep automatic tags
|
||||
let originalTags = item.getTags().filter(tag => tag.type == 1);
|
||||
item.setTags(originalTags.concat(tags));
|
||||
item.setCollections(collection ? [collection.id] : []);
|
||||
await item.saveTx();
|
||||
}
|
||||
|
||||
this._updateRecents();
|
||||
});
|
||||
|
||||
|
||||
Zotero.Server.Connector.SaveSession.prototype._updateRecents = function () {
|
||||
var targetID = this._currentTargetID;
|
||||
try {
|
||||
let numRecents = 7;
|
||||
let recents = Zotero.Prefs.get('recentSaveTargets') || '[]';
|
||||
recents = JSON.parse(recents);
|
||||
// If there's already a target from this session in the list, update it
|
||||
for (let recent of recents) {
|
||||
if (recent.sessionID == this.id) {
|
||||
recent.id = targetID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
// If a session is found with the same target, move it to the end without changing
|
||||
// the sessionID. This could be the current session that we updated above or a different
|
||||
// one. (We need to leave the old sessionID for the same target or we'll end up removing
|
||||
// the previous target from the history if it's changed in the current one.)
|
||||
let pos = recents.findIndex(r => r.id == targetID);
|
||||
if (pos != -1) {
|
||||
recents = [
|
||||
...recents.slice(0, pos),
|
||||
...recents.slice(pos + 1),
|
||||
recents[pos]
|
||||
];
|
||||
}
|
||||
// Otherwise just add this one to the end
|
||||
else {
|
||||
recents = recents.concat([{
|
||||
id: targetID,
|
||||
sessionID: this.id
|
||||
}]);
|
||||
}
|
||||
recents = recents.slice(-1 * numRecents);
|
||||
Zotero.Prefs.set('recentSaveTargets', JSON.stringify(recents));
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
Zotero.Prefs.clear('recentSaveTargets');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -404,6 +512,8 @@ Zotero.Server.Connector.SavePage.prototype = {
|
|||
*/
|
||||
init: function(url, data, sendResponseCallback) {
|
||||
var { library, collection, editable } = Zotero.Server.Connector.getSaveTarget();
|
||||
|
||||
// Shouldn't happen as long as My Library exists
|
||||
if (!library.editable) {
|
||||
Zotero.logError("Can't add item to read-only library " + library.name);
|
||||
return sendResponseCallback(500, "application/json", JSON.stringify({ libraryEditable: false }));
|
||||
|
@ -490,9 +600,9 @@ Zotero.Server.Connector.SavePage.prototype = {
|
|||
* Returns:
|
||||
* 201 response code with item in body.
|
||||
*/
|
||||
Zotero.Server.Connector.SaveItem = function() {};
|
||||
Zotero.Server.Endpoints["/connector/saveItems"] = Zotero.Server.Connector.SaveItem;
|
||||
Zotero.Server.Connector.SaveItem.prototype = {
|
||||
Zotero.Server.Connector.SaveItems = function() {};
|
||||
Zotero.Server.Endpoints["/connector/saveItems"] = Zotero.Server.Connector.SaveItems;
|
||||
Zotero.Server.Connector.SaveItems.prototype = {
|
||||
supportedMethods: ["POST"],
|
||||
supportedDataTypes: ["application/json"],
|
||||
permitBookmarklet: true,
|
||||
|
@ -501,46 +611,77 @@ Zotero.Server.Connector.SaveItem.prototype = {
|
|||
* Either loads HTML into a hidden browser and initiates translation, or saves items directly
|
||||
* to the database
|
||||
*/
|
||||
init: Zotero.Promise.coroutine(function* (options) {
|
||||
var data = options.data;
|
||||
init: Zotero.Promise.coroutine(function* (requestData) {
|
||||
var data = requestData.data;
|
||||
|
||||
var { library, collection, editable } = Zotero.Server.Connector.getSaveTarget();
|
||||
var libraryID = library.libraryID;
|
||||
var targetID = collection ? collection.treeViewID : library.treeViewID;
|
||||
|
||||
try {
|
||||
var session = Zotero.Server.Connector.SessionManager.create(data.sessionID);
|
||||
var session = Zotero.Server.Connector.SessionManager.create(
|
||||
data.sessionID,
|
||||
'saveItems',
|
||||
requestData
|
||||
);
|
||||
}
|
||||
catch (e) {
|
||||
return [409, "application/json", JSON.stringify({ error: "SESSION_EXISTS" })];
|
||||
}
|
||||
yield session.update(libraryID, collection ? collection.id : false);
|
||||
yield session.update(targetID);
|
||||
|
||||
// TODO: Default to My Library root, since it's changeable
|
||||
// Shouldn't happen as long as My Library exists
|
||||
if (!library.editable) {
|
||||
Zotero.logError("Can't add item to read-only library " + library.name);
|
||||
return [500, "application/json", JSON.stringify({ libraryEditable: false })];
|
||||
}
|
||||
|
||||
return new Zotero.Promise((resolve) => {
|
||||
try {
|
||||
this.saveItems(
|
||||
targetID,
|
||||
requestData,
|
||||
function (topLevelItems) {
|
||||
resolve([201, "application/json", JSON.stringify({items: topLevelItems})]);
|
||||
}
|
||||
)
|
||||
// Add items to session once all attachments have been saved
|
||||
.then(function (items) {
|
||||
session.addItems(items);
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
resolve(500);
|
||||
}
|
||||
});
|
||||
}),
|
||||
|
||||
saveItems: async function (target, requestData, onTopLevelItemsDone) {
|
||||
var { library, collection, editable } = Zotero.Server.Connector.resolveTarget(target);
|
||||
|
||||
var data = requestData.data;
|
||||
var cookieSandbox = data.uri
|
||||
? new Zotero.CookieSandbox(
|
||||
null,
|
||||
data.uri,
|
||||
data.detailedCookies ? "" : data.cookie || "",
|
||||
options.headers["User-Agent"]
|
||||
requestData.headers["User-Agent"]
|
||||
)
|
||||
: null;
|
||||
if(cookieSandbox && data.detailedCookies) {
|
||||
if (cookieSandbox && data.detailedCookies) {
|
||||
cookieSandbox.addCookiesFromHeader(data.detailedCookies);
|
||||
}
|
||||
|
||||
for(var i=0; i<data.items.length; i++) {
|
||||
Zotero.Server.Connector.AttachmentProgressManager.add(data.items[i].attachments);
|
||||
for (let item of data.items) {
|
||||
Zotero.Server.Connector.AttachmentProgressManager.add(item.attachments);
|
||||
}
|
||||
|
||||
let proxy = data.proxy && new Zotero.Proxy(data.proxy);
|
||||
// save items
|
||||
var proxy = data.proxy && new Zotero.Proxy(data.proxy);
|
||||
|
||||
// Save items
|
||||
var itemSaver = new Zotero.Translate.ItemSaver({
|
||||
libraryID,
|
||||
libraryID: library.libraryID,
|
||||
collections: collection ? [collection.id] : undefined,
|
||||
attachmentMode: Zotero.Translate.ItemSaver.ATTACHMENT_MODE_DOWNLOAD,
|
||||
forceTagType: 1,
|
||||
|
@ -548,35 +689,25 @@ Zotero.Server.Connector.SaveItem.prototype = {
|
|||
cookieSandbox,
|
||||
proxy
|
||||
});
|
||||
try {
|
||||
var deferred = Zotero.Promise.defer();
|
||||
itemSaver.saveItems(
|
||||
data.items,
|
||||
Zotero.Server.Connector.AttachmentProgressManager.onProgress,
|
||||
function() {
|
||||
// Remove attachments not being saved from item.attachments
|
||||
for(var i=0; i<data.items.length; i++) {
|
||||
var item = data.items[i];
|
||||
for(var j=0; j<item.attachments.length; j++) {
|
||||
if(!Zotero.Server.Connector.AttachmentProgressManager.has(item.attachments[j])) {
|
||||
item.attachments.splice(j--, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deferred.resolve([201, "application/json", JSON.stringify({items: data.items})]);
|
||||
return itemSaver.saveItems(
|
||||
data.items,
|
||||
Zotero.Server.Connector.AttachmentProgressManager.onProgress,
|
||||
function () {
|
||||
// Remove attachments from item.attachments that aren't being saved. We have to
|
||||
// clone the items so that we don't mutate the data stored in the session.
|
||||
var savedItems = [...data.items.map(item => Object.assign({}, item))];
|
||||
for (let item of savedItems) {
|
||||
item.attachments = item.attachments
|
||||
.filter(attachment => {
|
||||
return Zotero.Server.Connector.AttachmentProgressManager.has(attachment);
|
||||
});
|
||||
}
|
||||
)
|
||||
.then(function (items) {
|
||||
session.addItems(items);
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
return 500;
|
||||
}
|
||||
})
|
||||
if (onTopLevelItemsDone) {
|
||||
onTopLevelItemsDone(savedItems);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -599,85 +730,84 @@ Zotero.Server.Connector.SaveSnapshot.prototype = {
|
|||
/**
|
||||
* Save snapshot
|
||||
*/
|
||||
init: Zotero.Promise.coroutine(function* (options) {
|
||||
var data = options.data;
|
||||
|
||||
Zotero.Server.Connector.Data[data["url"]] = "<html>"+data["html"]+"</html>";
|
||||
init: async function (requestData) {
|
||||
var data = requestData.data;
|
||||
|
||||
var { library, collection, editable } = Zotero.Server.Connector.getSaveTarget();
|
||||
var libraryID = library.libraryID;
|
||||
var targetID = collection ? collection.treeViewID : library.treeViewID;
|
||||
|
||||
try {
|
||||
var session = Zotero.Server.Connector.SessionManager.create(data.sessionID);
|
||||
var session = Zotero.Server.Connector.SessionManager.create(
|
||||
data.sessionID,
|
||||
'saveSnapshot',
|
||||
requestData
|
||||
);
|
||||
}
|
||||
catch (e) {
|
||||
return [409, "application/json", JSON.stringify({ error: "SESSION_EXISTS" })];
|
||||
}
|
||||
yield session.update(libraryID, collection ? collection.id : false);
|
||||
await session.update(collection ? collection.treeViewID : library.treeViewID);
|
||||
|
||||
// TODO: Default to My Library root, since it's changeable
|
||||
// Shouldn't happen as long as My Library exists
|
||||
if (!library.editable) {
|
||||
Zotero.logError("Can't add item to read-only library " + library.name);
|
||||
return [500, "application/json", JSON.stringify({ libraryEditable: false })];
|
||||
}
|
||||
|
||||
// determine whether snapshot can be saved
|
||||
var filesEditable;
|
||||
if (libraryID) {
|
||||
let group = Zotero.Groups.getByLibraryID(libraryID);
|
||||
filesEditable = group.filesEditable;
|
||||
try {
|
||||
let item = await this.saveSnapshot(targetID, requestData);
|
||||
await session.addItem(item);
|
||||
}
|
||||
else {
|
||||
filesEditable = true;
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
return 500;
|
||||
}
|
||||
|
||||
return 201;
|
||||
},
|
||||
|
||||
saveSnapshot: async function (target, requestData) {
|
||||
var { library, collection, editable } = Zotero.Server.Connector.resolveTarget(target);
|
||||
var libraryID = library.libraryID;
|
||||
var data = requestData.data;
|
||||
|
||||
var cookieSandbox = data.url
|
||||
? new Zotero.CookieSandbox(
|
||||
null,
|
||||
data.url,
|
||||
data.detailedCookies ? "" : data.cookie || "",
|
||||
options.headers["User-Agent"]
|
||||
requestData.headers["User-Agent"]
|
||||
)
|
||||
: null;
|
||||
if(cookieSandbox && data.detailedCookies) {
|
||||
if (cookieSandbox && data.detailedCookies) {
|
||||
cookieSandbox.addCookiesFromHeader(data.detailedCookies);
|
||||
}
|
||||
|
||||
if (data.pdf && filesEditable) {
|
||||
delete Zotero.Server.Connector.Data[data.url];
|
||||
if (data.pdf && library.filesEditable) {
|
||||
let item = await Zotero.Attachments.importFromURL({
|
||||
libraryID,
|
||||
url: data.url,
|
||||
collections: collection ? [collection.id] : undefined,
|
||||
contentType: "application/pdf",
|
||||
cookieSandbox
|
||||
});
|
||||
|
||||
try {
|
||||
let item = yield Zotero.Attachments.importFromURL({
|
||||
libraryID,
|
||||
url: data.url,
|
||||
collections: collection ? [collection.id] : undefined,
|
||||
contentType: "application/pdf",
|
||||
cookieSandbox
|
||||
});
|
||||
if (item) {
|
||||
yield session.addItem(item);
|
||||
|
||||
// Automatically recognize PDF
|
||||
Zotero.RecognizePDF.autoRecognizeItems([item]);
|
||||
}
|
||||
|
||||
return 201;
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
return 500;
|
||||
}
|
||||
// Automatically recognize PDF
|
||||
Zotero.RecognizePDF.autoRecognizeItems([item]);
|
||||
|
||||
return item;
|
||||
}
|
||||
else {
|
||||
let deferred = Zotero.Promise.defer();
|
||||
|
||||
return new Zotero.Promise((resolve, reject) => {
|
||||
Zotero.Server.Connector.Data[data.url] = "<html>" + data.html + "</html>";
|
||||
Zotero.HTTP.loadDocuments(
|
||||
["zotero://connector/" + encodeURIComponent(data.url)],
|
||||
Zotero.Promise.coroutine(function* (doc) {
|
||||
async function (doc) {
|
||||
delete Zotero.Server.Connector.Data[data.url];
|
||||
|
||||
try {
|
||||
// create new webpage item
|
||||
var item = new Zotero.Item("webpage");
|
||||
// Create new webpage item
|
||||
let item = new Zotero.Item("webpage");
|
||||
item.libraryID = libraryID;
|
||||
item.setField("title", doc.title);
|
||||
item.setField("url", data.url);
|
||||
|
@ -685,29 +815,29 @@ Zotero.Server.Connector.SaveSnapshot.prototype = {
|
|||
if (collection) {
|
||||
item.setCollections([collection.id]);
|
||||
}
|
||||
var itemID = yield item.saveTx();
|
||||
yield session.addItem(item);
|
||||
var itemID = await item.saveTx();
|
||||
|
||||
// save snapshot
|
||||
if (filesEditable && !data.skipSnapshot) {
|
||||
yield Zotero.Attachments.importFromDocument({
|
||||
// Save snapshot
|
||||
if (library.filesEditable && !data.skipSnapshot) {
|
||||
await Zotero.Attachments.importFromDocument({
|
||||
document: doc,
|
||||
parentItemID: itemID
|
||||
});
|
||||
}
|
||||
|
||||
deferred.resolve(201);
|
||||
} catch(e) {
|
||||
Zotero.debug(e, 1);
|
||||
deferred.resolve(500);
|
||||
throw e;
|
||||
resolve(item);
|
||||
}
|
||||
}),
|
||||
null, null, false, cookieSandbox
|
||||
catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
},
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
cookieSandbox
|
||||
);
|
||||
return deferred.promise;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -763,8 +893,8 @@ Zotero.Server.Connector.UpdateSession.prototype = {
|
|||
supportedDataTypes: ["application/json"],
|
||||
permitBookmarklet: true,
|
||||
|
||||
init: async function (options) {
|
||||
var data = options.data
|
||||
init: async function (requestData) {
|
||||
var data = requestData.data
|
||||
|
||||
if (!data.sessionID) {
|
||||
return [400, "application/json", JSON.stringify({ error: "SESSION_ID_NOT_PROVIDED" })];
|
||||
|
@ -780,25 +910,30 @@ Zotero.Server.Connector.UpdateSession.prototype = {
|
|||
var [type, id] = [data.target[0], parseInt(data.target.substr(1))];
|
||||
var tags = data.tags;
|
||||
|
||||
if (type == 'L') {
|
||||
let library = Zotero.Libraries.get(id);
|
||||
await session.update(library.libraryID, null, tags);
|
||||
}
|
||||
else if (type == 'C') {
|
||||
if (type == 'C') {
|
||||
let collection = await Zotero.Collections.getAsync(id);
|
||||
if (!collection) {
|
||||
return [400, "application/json", JSON.stringify({ error: "COLLECTION_NOT_FOUND" })];
|
||||
}
|
||||
await session.update(collection.libraryID, collection.id, tags);
|
||||
}
|
||||
else {
|
||||
throw new Error(`Invalid identifier '${data.target}'`);
|
||||
}
|
||||
|
||||
await session.update(data.target, tags);
|
||||
|
||||
return [200, "application/json", JSON.stringify({})];
|
||||
}
|
||||
};
|
||||
|
||||
Zotero.Server.Connector.DelaySync = function () {};
|
||||
Zotero.Server.Endpoints["/connector/delaySync"] = Zotero.Server.Connector.DelaySync;
|
||||
Zotero.Server.Connector.DelaySync.prototype = {
|
||||
supportedMethods: ["POST"],
|
||||
|
||||
init: async function (requestData) {
|
||||
Zotero.Sync.Runner.delaySync(10000);
|
||||
return [204];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets progress for an attachment that is currently being saved
|
||||
*
|
||||
|
@ -839,29 +974,44 @@ Zotero.Server.Connector.Import.prototype = {
|
|||
supportedDataTypes: '*',
|
||||
permitBookmarklet: false,
|
||||
|
||||
init: Zotero.Promise.coroutine(function* (options) {
|
||||
init: async function (requestData) {
|
||||
let translate = new Zotero.Translate.Import();
|
||||
translate.setString(options.data);
|
||||
let translators = yield translate.getTranslators();
|
||||
translate.setString(requestData.data);
|
||||
let translators = await translate.getTranslators();
|
||||
if (!translators || !translators.length) {
|
||||
return 400;
|
||||
}
|
||||
translate.setTranslator(translators[0]);
|
||||
var { library, collection, editable } = Zotero.Server.Connector.getSaveTarget();
|
||||
var libraryID = library.libraryID;
|
||||
|
||||
// Shouldn't happen as long as My Library exists
|
||||
if (!library.editable) {
|
||||
Zotero.logError("Can't import into read-only library " + library.name);
|
||||
return [500, "application/json", JSON.stringify({ libraryEditable: false })];
|
||||
}
|
||||
let items = yield translate.translate({
|
||||
libraryID: library.libraryID,
|
||||
|
||||
try {
|
||||
var session = Zotero.Server.Connector.SessionManager.create(requestData.query.session);
|
||||
}
|
||||
catch (e) {
|
||||
return [409, "application/json", JSON.stringify({ error: "SESSION_EXISTS" })];
|
||||
}
|
||||
await session.update(collection ? collection.treeViewID : library.treeViewID);
|
||||
|
||||
let items = await translate.translate({
|
||||
libraryID,
|
||||
collections: collection ? [collection.id] : null,
|
||||
forceTagType: 1,
|
||||
// Import translation skips selection by default, so force it to occur
|
||||
saveOptions: {
|
||||
skipSelect: false
|
||||
}
|
||||
});
|
||||
session.addItems(items);
|
||||
|
||||
return [201, "application/json", JSON.stringify(items)];
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -878,9 +1028,11 @@ Zotero.Server.Connector.InstallStyle.prototype = {
|
|||
supportedDataTypes: '*',
|
||||
permitBookmarklet: false,
|
||||
|
||||
init: Zotero.Promise.coroutine(function* (options) {
|
||||
init: Zotero.Promise.coroutine(function* (requestData) {
|
||||
try {
|
||||
var styleName = yield Zotero.Styles.install(options.data, options.query.origin || null, true);
|
||||
var styleName = yield Zotero.Styles.install(
|
||||
requestData.data, requestData.query.origin || null, true
|
||||
);
|
||||
} catch (e) {
|
||||
return [400, "text/plain", e.message];
|
||||
}
|
||||
|
@ -940,7 +1092,7 @@ Zotero.Server.Connector.GetSelectedCollection.prototype = {
|
|||
* @param {Function} sendResponseCallback function to send HTTP response
|
||||
*/
|
||||
init: function(postData, sendResponseCallback) {
|
||||
var { library, collection, editable } = Zotero.Server.Connector.getSaveTarget();
|
||||
var { library, collection, editable } = Zotero.Server.Connector.getSaveTarget(true);
|
||||
var response = {
|
||||
libraryID: library.libraryID,
|
||||
libraryName: library.name,
|
||||
|
@ -961,15 +1113,14 @@ Zotero.Server.Connector.GetSelectedCollection.prototype = {
|
|||
var originalLibraryID = library.libraryID;
|
||||
for (let library of Zotero.Libraries.getAll()) {
|
||||
if (!library.editable) continue;
|
||||
// TEMP: For now, don't allow library changing
|
||||
if (library.libraryID != originalLibraryID) continue;
|
||||
|
||||
// Add recent: true for recent targets
|
||||
|
||||
collections.push(
|
||||
{
|
||||
id: library.treeViewID,
|
||||
name: library.name
|
||||
name: library.name,
|
||||
level: 0
|
||||
},
|
||||
...Zotero.Collections.getByLibrary(library.libraryID, true).map(c => ({
|
||||
id: c.treeViewID,
|
||||
|
@ -980,8 +1131,41 @@ Zotero.Server.Connector.GetSelectedCollection.prototype = {
|
|||
}
|
||||
response.targets = collections;
|
||||
|
||||
// TODO: Limit debug size
|
||||
sendResponseCallback(200, "application/json", JSON.stringify(response));
|
||||
// Mark recent targets
|
||||
try {
|
||||
let recents = Zotero.Prefs.get('recentSaveTargets');
|
||||
if (recents) {
|
||||
recents = new Set(JSON.parse(recents).map(o => o.id));
|
||||
for (let target of response.targets) {
|
||||
if (recents.has(target.id)) {
|
||||
target.recent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
Zotero.Prefs.clear('recentSaveTargets');
|
||||
}
|
||||
|
||||
sendResponseCallback(
|
||||
200,
|
||||
"application/json",
|
||||
JSON.stringify(response),
|
||||
{
|
||||
// Filter out collection names in debug output
|
||||
logFilter: function (str) {
|
||||
try {
|
||||
let json = JSON.parse(str.match(/^{"libraryID"[^]+/m)[0]);
|
||||
json.targets.forEach(t => t.name = "\u2026");
|
||||
return JSON.stringify(json);
|
||||
}
|
||||
catch (e) {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1004,7 +1188,7 @@ Zotero.Server.Connector.GetClientHostnames.prototype = {
|
|||
/**
|
||||
* Returns a 200 response to say the server is alive
|
||||
*/
|
||||
init: Zotero.Promise.coroutine(function* (options) {
|
||||
init: Zotero.Promise.coroutine(function* (requestData) {
|
||||
try {
|
||||
var hostnames = yield Zotero.Proxies.DNS.getHostnames();
|
||||
} catch(e) {
|
||||
|
@ -1080,8 +1264,108 @@ Zotero.Server.Connector.Ping.prototype = {
|
|||
response.prefs.reportActiveURL = true;
|
||||
}
|
||||
|
||||
this.versionWarning(req);
|
||||
|
||||
return [200, 'application/json', JSON.stringify(response)];
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Warn on outdated connector version
|
||||
*
|
||||
* We can remove this once the connector checks and warns on its own and most people are on
|
||||
* a version that does that.
|
||||
*/
|
||||
versionWarning: function (req) {
|
||||
try {
|
||||
if (!Zotero.Prefs.get('showConnectorVersionWarning')) return;
|
||||
if (!req.headers) return;
|
||||
|
||||
var minVersion = ZOTERO_CONFIG.CONNECTOR_MIN_VERSION;
|
||||
var appName = ZOTERO_CONFIG.CLIENT_NAME;
|
||||
var domain = ZOTERO_CONFIG.DOMAIN_NAME;
|
||||
var origin = req.headers.Origin;
|
||||
|
||||
var browser;
|
||||
var message;
|
||||
var showDownloadButton = false;
|
||||
if (origin && origin.startsWith('safari-extension')) {
|
||||
browser = 'safari';
|
||||
message = `An update is available for the ${appName} Connector for Safari.\n\n`
|
||||
+ 'You can upgrade from the Extensions pane of the Safari preferences.';
|
||||
}
|
||||
else if (origin && origin.startsWith('chrome-extension')) {
|
||||
browser = 'chrome';
|
||||
message = `An update is available for the ${appName} Connector for Chrome.\n\n`
|
||||
+ `You can upgrade to the latest version from ${domain}.`;
|
||||
showDownloadButton = true;
|
||||
}
|
||||
else if (req.headers['User-Agent'] && req.headers['User-Agent'].includes('Firefox/')) {
|
||||
browser = 'firefox';
|
||||
message = `An update is available for the ${appName} Connector for Firefox.\n\n`
|
||||
+ `You can upgrade to the latest version from ${domain}.`;
|
||||
showDownloadButton = true;
|
||||
}
|
||||
else {
|
||||
Zotero.debug("Unknown browser");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Zotero.Server.Connector['skipVersionWarning-' + browser]) return;
|
||||
|
||||
var version = req.headers['X-Zotero-Version'];
|
||||
if (!version || version == '4.999.0') return;
|
||||
|
||||
// If connector is up to date, bail
|
||||
if (Services.vc.compare(version, minVersion) >= 0) return;
|
||||
|
||||
var showNextPref = `nextConnectorVersionWarning.${browser}`;
|
||||
var showNext = Zotero.Prefs.get(showNextPref);
|
||||
if (showNext && new Date() < new Date(showNext * 1000)) return;
|
||||
|
||||
// Don't show again for this browser until restart
|
||||
Zotero.Server.Connector['skipVersionWarning-' + browser] = true;
|
||||
var ps = Services.prompt;
|
||||
var buttonFlags;
|
||||
if (showDownloadButton) {
|
||||
buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING
|
||||
+ ps.BUTTON_POS_1 * ps.BUTTON_TITLE_IS_STRING;
|
||||
}
|
||||
else {
|
||||
buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_OK;
|
||||
}
|
||||
setTimeout(function () {
|
||||
var dontShow = {};
|
||||
var index = ps.confirmEx(null,
|
||||
Zotero.getString('general.updateAvailable'),
|
||||
message,
|
||||
buttonFlags,
|
||||
showDownloadButton ? Zotero.getString('general.upgrade') : null,
|
||||
showDownloadButton ? Zotero.getString('general.notNow') : null,
|
||||
null,
|
||||
"Don\u0027t show again for a month",
|
||||
dontShow
|
||||
);
|
||||
|
||||
var nextShowDays;
|
||||
if (dontShow.value) {
|
||||
nextShowDays = 30;
|
||||
}
|
||||
// Don't show again for at least a day, even after a restart
|
||||
else {
|
||||
nextShowDays = 1;
|
||||
}
|
||||
Zotero.Prefs.set(showNextPref, Math.round(Date.now() / 1000) + 86400 * nextShowDays);
|
||||
|
||||
if (showDownloadButton && index == 0) {
|
||||
Zotero.launchURL(ZOTERO_CONFIG.CONNECTORS_URL);
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.debug(e, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2017 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
http://zotero.org
|
||||
|
||||
This file is part of Zotero.
|
||||
|
||||
Zotero is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Zotero is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adds integration endpoints related to doc integration via HTTP/connector.
|
||||
*
|
||||
* document/execCommand initiates an integration command and responds with the
|
||||
* next request for the http client (e.g. 'Application.getDocument').
|
||||
* The client should respond to document/respond with the payload and expect
|
||||
* another response with the next request, until it receives 'Document.complete'
|
||||
* at which point the integration transaction is considered complete.
|
||||
*/
|
||||
Zotero.Server.Endpoints['/connector/document/execCommand'] = function() {};
|
||||
Zotero.Server.Endpoints['/connector/document/execCommand'].prototype = {
|
||||
supportedMethods: ["POST"],
|
||||
supportedDataTypes: ["application/json"],
|
||||
permitBookmarklet: true,
|
||||
init: function(data, sendResponse) {
|
||||
if (Zotero.HTTPIntegrationClient.inProgress) {
|
||||
// This will focus the last integration window if present
|
||||
Zotero.Integration.execCommand('http', data.command, data.docId);
|
||||
sendResponse(503, 'text/plain', 'Integration transaction is already in progress')
|
||||
return;
|
||||
}
|
||||
Zotero.HTTPIntegrationClient.inProgress = true;
|
||||
Zotero.HTTPIntegrationClient.sendResponse = sendResponse;
|
||||
Zotero.Integration.execCommand('http', data.command, data.docId);
|
||||
},
|
||||
};
|
||||
|
||||
Zotero.Server.Endpoints['/connector/document/respond'] = function() {};
|
||||
Zotero.Server.Endpoints['/connector/document/respond'].prototype = {
|
||||
supportedMethods: ["POST"],
|
||||
supportedDataTypes: ["application/json"],
|
||||
permitBookmarklet: true,
|
||||
|
||||
init: function(data, sendResponse) {
|
||||
data = JSON.parse(data);
|
||||
if (data && data.error) {
|
||||
// Apps Script stack is a JSON object
|
||||
if (typeof data.stack != "string") {
|
||||
data.stack = JSON.stringify(data.stack);
|
||||
}
|
||||
Zotero.HTTPIntegrationClient.deferredResponse.reject(data);
|
||||
} else {
|
||||
Zotero.HTTPIntegrationClient.deferredResponse.resolve(data);
|
||||
}
|
||||
Zotero.HTTPIntegrationClient.sendResponse = sendResponse;
|
||||
}
|
||||
};
|
||||
|
||||
// For managing macOS integration and progress window focus
|
||||
Zotero.Server.Endpoints['/connector/sendToBack'] = function() {};
|
||||
Zotero.Server.Endpoints['/connector/sendToBack'].prototype = {
|
||||
supportedMethods: ["POST"],
|
||||
supportedDataTypes: ["application/json"],
|
||||
permitBookmarklet: true,
|
||||
init: function() {
|
||||
Zotero.Utilities.Internal.sendToBack();
|
||||
},
|
||||
};
|
|
@ -698,8 +698,7 @@ Zotero.Collection.prototype.fromJSON = function (json) {
|
|||
this.name = json.name;
|
||||
this.parentKey = json.parentCollection ? json.parentCollection : false;
|
||||
|
||||
// TODO
|
||||
//this.setRelations(json.relations);
|
||||
this.setRelations(json.relations || {});
|
||||
}
|
||||
|
||||
|
||||
|
@ -713,7 +712,7 @@ Zotero.Collection.prototype.toJSON = function (options = {}) {
|
|||
|
||||
obj.name = this.name;
|
||||
obj.parentCollection = this.parentKey ? this.parentKey : false;
|
||||
obj.relations = {}; // TEMP
|
||||
obj.relations = this.getRelations();
|
||||
|
||||
return this._postToJSON(env);
|
||||
}
|
||||
|
|
|
@ -285,7 +285,7 @@ Zotero.DataObject.prototype._setParentKey = function(key) {
|
|||
/**
|
||||
* Returns all relations of the object
|
||||
*
|
||||
* @return {Object} - Object with predicates as keys and arrays of URIs as values
|
||||
* @return {Object} - Object with predicates as keys and arrays of values
|
||||
*/
|
||||
Zotero.DataObject.prototype.getRelations = function () {
|
||||
this._requireData('relations');
|
||||
|
@ -410,7 +410,7 @@ Zotero.DataObject.prototype.setRelations = function (newRelations) {
|
|||
|
||||
// Limit predicates to letters and colons for now
|
||||
for (let p in newRelations) {
|
||||
if (!/[a-z]+:[a-z]+/.test(p)) {
|
||||
if (!/^[a-z]+:[a-z]+$/i.test(p)) {
|
||||
throw new Error(`Invalid relation predicate '${p}'`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -234,14 +234,9 @@ Zotero.DataObjectUtilities = {
|
|||
},
|
||||
|
||||
_conditionsChanged: function (data1, data2) {
|
||||
if (!data2) return true;
|
||||
var pred1 = Object.keys(data1);
|
||||
pred1.sort();
|
||||
var pred2 = Object.keys(data2);
|
||||
pred2.sort();
|
||||
if (!Zotero.Utilities.arrayEquals(pred1, pred2)) return false;
|
||||
for (let i in pred1) {
|
||||
if (!Zotero.Utilities.arrayEquals(pred1[i], pred2[i])) {
|
||||
if (!data2 || data1.length != data2.length) return true;
|
||||
for (let i = 0; i < data1.length; i++) {
|
||||
if (!Zotero.Searches.conditionEquals(data1[i], data2[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,6 +111,10 @@ Zotero.defineProperty(Zotero.Feed.prototype, 'isFeed', {
|
|||
value: true
|
||||
});
|
||||
|
||||
Zotero.defineProperty(Zotero.Feed.prototype, 'allowsLinkedFiles', {
|
||||
value: false
|
||||
});
|
||||
|
||||
Zotero.defineProperty(Zotero.Feed.prototype, 'libraryTypes', {
|
||||
value: Object.freeze(Zotero.Feed._super.prototype.libraryTypes.concat(['feed']))
|
||||
});
|
||||
|
|
|
@ -86,6 +86,10 @@ Zotero.defineProperty(Zotero.Group.prototype, 'id', {
|
|||
set: function(v) { return this.groupID = v; }
|
||||
});
|
||||
|
||||
Zotero.defineProperty(Zotero.Group.prototype, 'allowsLinkedFiles', {
|
||||
value: false
|
||||
});
|
||||
|
||||
// Create accessors
|
||||
(function() {
|
||||
let accessors = ['name', 'description', 'version'];
|
||||
|
|
|
@ -145,6 +145,9 @@ Zotero.defineProperty(Zotero.Item.prototype, 'parentItemKey', {
|
|||
get: function() { return this.parentKey; },
|
||||
set: function(val) { return this.parentKey = val; }
|
||||
});
|
||||
Zotero.defineProperty(Zotero.Item.prototype, 'parentItem', {
|
||||
get: function() { return Zotero.Items.get(this.parentID) || undefined; },
|
||||
});
|
||||
|
||||
|
||||
Zotero.defineProperty(Zotero.Item.prototype, 'firstCreator', {
|
||||
|
@ -1048,8 +1051,7 @@ Zotero.Item.prototype.setCreator = function (orderIndex, data) {
|
|||
var msg = "Creator type '" + Zotero.CreatorTypes.getName(data.creatorTypeID) + "' "
|
||||
+ "isn't valid for " + Zotero.ItemTypes.getName(itemTypeID)
|
||||
+ " -- changing to primary creator";
|
||||
Zotero.debug(msg, 2);
|
||||
Components.utils.reportError(msg);
|
||||
Zotero.warn(msg);
|
||||
data.creatorTypeID = Zotero.CreatorTypes.getPrimaryIDForType(itemTypeID);
|
||||
}
|
||||
|
||||
|
@ -1641,8 +1643,7 @@ Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
|
|||
let noteText = this._noteText ? this._noteText : '';
|
||||
// Add <div> wrapper if not present
|
||||
if (!noteText.match(/^<div class="zotero-note znv[0-9]+">[\s\S]*<\/div>$/)) {
|
||||
// Keep consistent with getNote()
|
||||
noteText = '<div class="zotero-note znv1">' + noteText + '</div>';
|
||||
noteText = Zotero.Notes.notePrefix + noteText + Zotero.Notes.noteSuffix;
|
||||
}
|
||||
|
||||
let params = [
|
||||
|
@ -2810,7 +2811,7 @@ Zotero.defineProperty(Zotero.Item.prototype, 'attachmentFilename', {
|
|||
if (!path) {
|
||||
return '';
|
||||
}
|
||||
var prefixedPath = path.match(/^(?:attachments|storage):(.+)$/);
|
||||
var prefixedPath = path.match(/^(?:attachments|storage):(.*)$/);
|
||||
if (prefixedPath) {
|
||||
return prefixedPath[1];
|
||||
}
|
||||
|
@ -3986,6 +3987,89 @@ Zotero.Item.prototype.clone = function (libraryID, options = {}) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {Zotero.Item} item
|
||||
* @param {Integer} libraryID
|
||||
* @return {Zotero.Item} - New item
|
||||
*/
|
||||
Zotero.Item.prototype.moveToLibrary = async function (libraryID, onSkippedAttachment) {
|
||||
if (!this.isEditable) {
|
||||
throw new Error("Can't move item in read-only library");
|
||||
}
|
||||
var library = Zotero.Libraries.get(libraryID);
|
||||
Zotero.debug("Moving item to " + library.name);
|
||||
if (!library.editable) {
|
||||
throw new Error("Can't move item to read-only library");
|
||||
}
|
||||
var filesEditable = library.filesEditable;
|
||||
var allowsLinkedFiles = library.allowsLinkedFiles;
|
||||
|
||||
var newItem = await Zotero.DB.executeTransaction(async function () {
|
||||
// Create new clone item in target library
|
||||
var newItem = this.clone(libraryID);
|
||||
var newItemID = await newItem.save({
|
||||
skipSelect: true
|
||||
});
|
||||
|
||||
if (this.isNote()) {
|
||||
// Delete old item
|
||||
await this.erase();
|
||||
return newItem;
|
||||
}
|
||||
|
||||
// For regular items, add child items
|
||||
|
||||
// Child notes
|
||||
var noteIDs = this.getNotes();
|
||||
var notes = Zotero.Items.get(noteIDs);
|
||||
for (let note of notes) {
|
||||
let newNote = note.clone(libraryID);
|
||||
newNote.parentID = newItemID;
|
||||
await newNote.save({
|
||||
skipSelect: true
|
||||
});
|
||||
}
|
||||
|
||||
// Child attachments
|
||||
var attachmentIDs = this.getAttachments();
|
||||
var attachments = Zotero.Items.get(attachmentIDs);
|
||||
for (let attachment of attachments) {
|
||||
let linkMode = attachment.attachmentLinkMode;
|
||||
|
||||
// Skip linked files if not allowed in destination
|
||||
if (!allowsLinkedFiles && linkMode == Zotero.Attachments.LINK_MODE_LINKED_FILE) {
|
||||
Zotero.debug("Target library doesn't support linked files -- skipping attachment");
|
||||
if (onSkippedAttachment) {
|
||||
await onSkippedAttachment(attachment);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip files if not allowed in destination
|
||||
if (!filesEditable && linkMode != Zotero.Attachments.LINK_MODE_LINKED_URL) {
|
||||
Zotero.debug("Target library doesn't allow file editing -- skipping attachment");
|
||||
if (onSkippedAttachment) {
|
||||
await onSkippedAttachment(attachment);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
await Zotero.Attachments.moveAttachmentToLibrary(
|
||||
attachment, libraryID, newItemID
|
||||
);
|
||||
}
|
||||
|
||||
return newItem;
|
||||
}.bind(this));
|
||||
|
||||
// Delete old item. Do this outside of a transaction so we don't leave stranded files
|
||||
// in the target library if deleting fails.
|
||||
await this.eraseTx();
|
||||
|
||||
return newItem;
|
||||
};
|
||||
|
||||
|
||||
Zotero.Item.prototype._eraseData = Zotero.Promise.coroutine(function* (env) {
|
||||
Zotero.DB.requireTransaction();
|
||||
|
||||
|
@ -4110,6 +4194,9 @@ Zotero.Item.prototype.fromJSON = function (json) {
|
|||
case 'mtime':
|
||||
// Handled below
|
||||
case 'collections':
|
||||
case 'parentItem':
|
||||
case 'deleted':
|
||||
case 'inPublications':
|
||||
break;
|
||||
|
||||
case 'accessDate':
|
||||
|
@ -4138,15 +4225,6 @@ Zotero.Item.prototype.fromJSON = function (json) {
|
|||
this[field] = val;
|
||||
break;
|
||||
|
||||
case 'parentItem':
|
||||
this.parentKey = val;
|
||||
break;
|
||||
|
||||
case 'deleted':
|
||||
case 'inPublications':
|
||||
this[field] = !!val;
|
||||
break;
|
||||
|
||||
case 'creators':
|
||||
this.setCreators(json.creators);
|
||||
break;
|
||||
|
@ -4234,6 +4312,13 @@ Zotero.Item.prototype.fromJSON = function (json) {
|
|||
let note = json.note;
|
||||
this.setNote(note !== undefined ? note : "");
|
||||
}
|
||||
|
||||
// Update boolean fields that might not be present in JSON
|
||||
['deleted', 'inPublications'].forEach(field => {
|
||||
if (json[field] || this[field]) {
|
||||
this[field] = !!json[field];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -260,6 +260,11 @@ Zotero.ItemFields = new function() {
|
|||
throw new Error("Invalid field '" + baseField + '" for base field');
|
||||
}
|
||||
|
||||
// If field isn't a base field, return it if it's valid for the type
|
||||
if (!this.isBaseField(baseFieldID)) {
|
||||
return this.isValidForType(baseFieldID, itemTypeID) ? baseFieldID : false;
|
||||
}
|
||||
|
||||
return _baseTypeFields[itemTypeID][baseFieldID];
|
||||
}
|
||||
|
||||
|
|
|
@ -315,7 +315,21 @@ Zotero.Items = function() {
|
|||
item._clearChanged('itemData');
|
||||
|
||||
// Display titles
|
||||
item.updateDisplayTitle()
|
||||
try {
|
||||
item.updateDisplayTitle()
|
||||
}
|
||||
catch (e) {
|
||||
// A few item types need creators to be loaded. Instead of making
|
||||
// updateDisplayTitle() async and loading conditionally, just catch the error
|
||||
// and load on demand
|
||||
if (e instanceof Zotero.Exception.UnloadedDataException) {
|
||||
yield item.loadDataType('creators');
|
||||
item.updateDisplayTitle()
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -735,6 +749,8 @@ Zotero.Items = function() {
|
|||
|
||||
|
||||
this.merge = function (item, otherItems) {
|
||||
Zotero.debug("Merging items");
|
||||
|
||||
return Zotero.DB.executeTransaction(function* () {
|
||||
var otherItemIDs = [];
|
||||
var itemURI = Zotero.URI.getItemURI(item);
|
||||
|
@ -758,7 +774,10 @@ Zotero.Items = function() {
|
|||
}
|
||||
|
||||
// Add relations to master
|
||||
item.setRelations(otherItem.getRelations());
|
||||
let oldRelations = otherItem.getRelations();
|
||||
for (let pred in oldRelations) {
|
||||
oldRelations[pred].forEach(obj => item.addRelation(pred, obj));
|
||||
}
|
||||
|
||||
// Remove merge-tracking relations from other item, so that there aren't two
|
||||
// subjects for a given deleted object
|
||||
|
|
|
@ -190,6 +190,10 @@ Zotero.defineProperty(Zotero.Library.prototype, 'hasTrash', {
|
|||
value: true
|
||||
});
|
||||
|
||||
Zotero.defineProperty(Zotero.Library.prototype, 'allowsLinkedFiles', {
|
||||
value: true
|
||||
});
|
||||
|
||||
// Create other accessors
|
||||
(function() {
|
||||
let accessors = ['editable', 'filesEditable', 'storageVersion', 'archived'];
|
||||
|
|
|
@ -32,7 +32,8 @@ Zotero.Relations = new function () {
|
|||
|
||||
this._namespaces = {
|
||||
dc: 'http://purl.org/dc/elements/1.1/',
|
||||
owl: 'http://www.w3.org/2002/07/owl#'
|
||||
owl: 'http://www.w3.org/2002/07/owl#',
|
||||
mendeleyDB: 'http://zotero.org/namespaces/mendeleyDB#'
|
||||
};
|
||||
|
||||
var _types = ['collection', 'item'];
|
||||
|
@ -146,7 +147,7 @@ Zotero.Relations = new function () {
|
|||
* @return {Object[]} - An array of objects with a Zotero.DataObject as 'subject'
|
||||
* and a predicate string as 'predicate'
|
||||
*/
|
||||
this.getByObject = function (objectType, object) {
|
||||
this.getByObject = Zotero.Promise.coroutine(function* (objectType, object) {
|
||||
var objectsClass = Zotero.DataObjectUtilities.getObjectsClassForObjectType(objectType);
|
||||
var predicateIDs = [];
|
||||
var o = _subjectPredicatesByObject[objectType]
|
||||
|
@ -156,13 +157,16 @@ Zotero.Relations = new function () {
|
|||
}
|
||||
var toReturn = [];
|
||||
for (let predicateID in o) {
|
||||
o[predicateID].forEach(subjectID => toReturn.push({
|
||||
subject: objectsClass.get(subjectID),
|
||||
predicate: Zotero.RelationPredicates.getName(predicateID)
|
||||
}));
|
||||
for (let subjectID of o[predicateID]) {
|
||||
var subject = yield objectsClass.getAsync(subjectID);
|
||||
toReturn.push({
|
||||
subject: subject,
|
||||
predicate: Zotero.RelationPredicates.getName(predicateID)
|
||||
});
|
||||
};
|
||||
}
|
||||
return toReturn;
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
this.updateUser = Zotero.Promise.coroutine(function* (fromUserID, toUserID) {
|
||||
|
@ -179,9 +183,9 @@ Zotero.Relations = new function () {
|
|||
let objects = yield Zotero.DB.columnQueryAsync(
|
||||
sql, 'http://zotero.org/users/' + fromUserID + '/%'
|
||||
);
|
||||
Zotero.DB.addCurrentCallback("commit", function () {
|
||||
Zotero.DB.addCurrentCallback("commit", function* () {
|
||||
for (let object of objects) {
|
||||
let subPrefs = this.getByObject(type, object);
|
||||
let subPrefs = yield this.getByObject(type, object);
|
||||
let newObject = object.replace(
|
||||
new RegExp("^http://zotero.org/users/" + fromUserID + "/(.*)"),
|
||||
"http://zotero.org/users/" + toUserID + "/$1"
|
||||
|
@ -265,4 +269,4 @@ Zotero.Relations = new function () {
|
|||
}
|
||||
throw ("Invalid namespace in URI '" + uri + "' in Zotero.Relations._getPrefixAndValue()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -338,7 +338,9 @@ Zotero.SearchConditions = new function(){
|
|||
doesNotContain: true
|
||||
},
|
||||
table: 'itemNotes',
|
||||
field: 'note'
|
||||
// Exclude note prefix and suffix
|
||||
field: `SUBSTR(note, ${1 + Zotero.Notes.notePrefix.length}, `
|
||||
+ `LENGTH(note) - ${Zotero.Notes.notePrefix.length + Zotero.Notes.noteSuffix.length})`
|
||||
},
|
||||
|
||||
{
|
||||
|
@ -348,7 +350,9 @@ Zotero.SearchConditions = new function(){
|
|||
doesNotContain: true
|
||||
},
|
||||
table: 'items',
|
||||
field: 'note'
|
||||
// Exclude note prefix and suffix
|
||||
field: `SUBSTR(note, ${1 + Zotero.Notes.notePrefix.length}, `
|
||||
+ `LENGTH(note) - ${Zotero.Notes.notePrefix.length + Zotero.Notes.noteSuffix.length})`
|
||||
},
|
||||
|
||||
{
|
||||
|
|
|
@ -96,6 +96,13 @@ Zotero.Searches = function() {
|
|||
}
|
||||
|
||||
|
||||
this.conditionEquals = function (data1, data2) {
|
||||
return data1.condition === data2.condition
|
||||
&& data1.operator === data2.operator
|
||||
&& data1.value === data2.value;
|
||||
},
|
||||
|
||||
|
||||
this._loadConditions = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) {
|
||||
var sql = "SELECT savedSearchID, searchConditionID, condition, operator, value, required "
|
||||
+ "FROM savedSearches LEFT JOIN savedSearchConditions USING (savedSearchID) "
|
||||
|
|
|
@ -758,12 +758,13 @@ Zotero.Tags = new function() {
|
|||
* @return {Q Promise} A Q promise for a data: URL for a PNG
|
||||
*/
|
||||
this.generateItemsListImage = function (colors, extraImage) {
|
||||
var multiplier = (extraImage && extraImage.indexOf('2x') != -1) ? 2 : 1;
|
||||
var multiplier = Zotero.hiDPI ? 2 : 1;
|
||||
|
||||
var swatchWidth = 8 * multiplier;
|
||||
var separator = 3 * multiplier;
|
||||
var extraImageSeparator = 1 * multiplier;
|
||||
var extraImageWidth = 16 * multiplier;
|
||||
var extraImageHeight = 16 * multiplier;
|
||||
var canvasHeight = 16 * multiplier;
|
||||
var swatchHeight = 8 * multiplier;
|
||||
var prependExtraImage = true;
|
||||
|
@ -831,7 +832,7 @@ Zotero.Tags = new function() {
|
|||
|
||||
// When extra image has loaded, draw it
|
||||
img.onload = function () {
|
||||
ctx.drawImage(img, x, 0);
|
||||
ctx.drawImage(img, x, 0, extraImageWidth, extraImageHeight);
|
||||
|
||||
var dataURI = canvas.toDataURL("image/png");
|
||||
var dataURIPromise = Zotero.Promise.resolve(dataURI);
|
||||
|
@ -852,7 +853,7 @@ Zotero.Tags = new function() {
|
|||
// for the composite image once it's ready
|
||||
return _itemsListExtraImagePromises[extraImage]
|
||||
.then(function (img) {
|
||||
ctx.drawImage(img, x, 0);
|
||||
ctx.drawImage(img, x, 0, extraImageWidth, extraImageHeight);
|
||||
|
||||
var dataURI = canvas.toDataURL("image/png");
|
||||
var dataURIPromise = Zotero.Promise.resolve(dataURI);
|
||||
|
|
|
@ -182,6 +182,23 @@ Zotero.DataDirectory = {
|
|||
|
||||
dataDir = this.defaultDir;
|
||||
|
||||
// If there's already a profile pointing to the default location, use a different
|
||||
// data directory named after the profile, as long as one either doesn't exist yet or
|
||||
// one does and it contains a database
|
||||
try {
|
||||
if ((yield Zotero.Profile.findOtherProfilesUsingDataDirectory(dataDir, false)).length) {
|
||||
let profileName = OS.Path.basename(Zotero.Profile.dir).match(/[^.]+\.(.+)/)[1];
|
||||
let newDataDir = this.defaultDir + ' ' + profileName;
|
||||
if (!(yield OS.File.exists(newDataDir))
|
||||
|| (yield OS.File.exists(OS.Path.join(newDataDir, dbFilename)))) {
|
||||
dataDir = newDataDir;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
}
|
||||
|
||||
// Check for ~/Zotero/zotero.sqlite
|
||||
let dbFile = OS.Path.join(dataDir, dbFilename);
|
||||
if (yield OS.File.exists(dbFile)) {
|
||||
|
@ -238,35 +255,7 @@ Zotero.DataDirectory = {
|
|||
// Read in prefs
|
||||
let prefsFile = OS.Path.join(profileDir, "prefs.js");
|
||||
if (yield OS.File.exists(prefsFile)) {
|
||||
// build sandbox
|
||||
var sandbox = new Components.utils.Sandbox("http://www.example.com/");
|
||||
Components.utils.evalInSandbox(
|
||||
"var prefs = {};"+
|
||||
"function user_pref(key, val) {"+
|
||||
"prefs[key] = val;"+
|
||||
"}"
|
||||
, sandbox);
|
||||
|
||||
(yield Zotero.File.getContentsAsync(prefsFile))
|
||||
.split(/\n/)
|
||||
.filter((line) => {
|
||||
// Strip comments
|
||||
return !line.startsWith('#')
|
||||
// Only process lines in our pref branch
|
||||
&& line.includes(ZOTERO_CONFIG.PREF_BRANCH);
|
||||
})
|
||||
// Process each line individually
|
||||
.forEach((line) => {
|
||||
try {
|
||||
Zotero.debug("Processing " + line);
|
||||
Components.utils.evalInSandbox(line, sandbox);
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError("Error processing prefs line: " + line);
|
||||
}
|
||||
});
|
||||
|
||||
var prefs = sandbox.prefs;
|
||||
let prefs = yield Zotero.Profile.readPrefsFromFile(prefsFile);
|
||||
|
||||
// Check for data dir pref
|
||||
if (prefs['extensions.zotero.dataDir'] && prefs['extensions.zotero.useDataDir']) {
|
||||
|
|
|
@ -296,13 +296,13 @@ Zotero.Date = new function(){
|
|||
|
||||
// Parse 'yesterday'/'today'/'tomorrow'
|
||||
var lc = (string + '').toLowerCase();
|
||||
if (lc == 'yesterday' || (Zotero.getString && lc === Zotero.getString('date.yesterday'))) {
|
||||
if (lc == 'yesterday' || (Zotero.isClient && lc === Zotero.getString('date.yesterday'))) {
|
||||
string = Zotero.Date.dateToSQL(new Date(Date.now() - 1000*60*60*24)).substr(0, 10); // no 'this' for translator sandbox
|
||||
}
|
||||
else if (lc == 'today' || (Zotero.getString && lc == Zotero.getString('date.today'))) {
|
||||
else if (lc == 'today' || (Zotero.isClient && lc == Zotero.getString('date.today'))) {
|
||||
string = Zotero.Date.dateToSQL(new Date()).substr(0, 10);
|
||||
}
|
||||
else if (lc == 'tomorrow' || (Zotero.getString && lc == Zotero.getString('date.tomorrow'))) {
|
||||
else if (lc == 'tomorrow' || (Zotero.isClient && lc == Zotero.getString('date.tomorrow'))) {
|
||||
string = Zotero.Date.dateToSQL(new Date(Date.now() + 1000*60*60*24)).substr(0, 10);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -31,8 +31,8 @@
|
|||
// the same database is accessed simultaneously by multiple Zotero instances.
|
||||
const DB_LOCK_EXCLUSIVE = true;
|
||||
|
||||
Zotero.DBConnection = function(dbName) {
|
||||
if (!dbName) {
|
||||
Zotero.DBConnection = function(dbNameOrPath) {
|
||||
if (!dbNameOrPath) {
|
||||
throw ('DB name not provided in Zotero.DBConnection()');
|
||||
}
|
||||
|
||||
|
@ -70,8 +70,18 @@ Zotero.DBConnection = function(dbName) {
|
|||
return Zotero.Date.toUnixTimestamp(d);
|
||||
});
|
||||
|
||||
// Private members
|
||||
this._dbName = dbName;
|
||||
// Absolute path to DB
|
||||
if (dbNameOrPath.startsWith('/') || (Zotero.isWin && dbNameOrPath.includes('\\'))) {
|
||||
this._dbName = OS.Path.basename(dbNameOrPath).replace(/\.sqlite$/, '');
|
||||
this._dbPath = dbNameOrPath;
|
||||
this._externalDB = true;
|
||||
}
|
||||
// DB name in data directory
|
||||
else {
|
||||
this._dbName = dbNameOrPath;
|
||||
this._dbPath = Zotero.DataDirectory.getDatabase(dbNameOrPath);
|
||||
this._externalDB = false;
|
||||
}
|
||||
this._shutdown = false;
|
||||
this._connection = null;
|
||||
this._transactionID = null;
|
||||
|
@ -91,6 +101,14 @@ Zotero.DBConnection = function(dbName) {
|
|||
this._dbIsCorrupt = null
|
||||
|
||||
this._transactionPromise = null;
|
||||
|
||||
if (dbNameOrPath == 'zotero') {
|
||||
this.IncompatibleVersionException = function (msg, dbClientVersion) {
|
||||
this.message = msg;
|
||||
this.dbClientVersion = dbClientVersion;
|
||||
}
|
||||
this.IncompatibleVersionException.prototype = Object.create(Error.prototype);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
@ -105,7 +123,7 @@ Zotero.DBConnection = function(dbName) {
|
|||
* @return void
|
||||
*/
|
||||
Zotero.DBConnection.prototype.test = function () {
|
||||
return this._getConnectionAsync().return();
|
||||
return this._getConnectionAsync().then(() => {});
|
||||
}
|
||||
|
||||
Zotero.DBConnection.prototype.getAsyncStatement = Zotero.Promise.coroutine(function* (sql) {
|
||||
|
@ -818,9 +836,10 @@ Zotero.DBConnection.prototype.logQuery = function (sql, params = [], options) {
|
|||
}
|
||||
|
||||
|
||||
Zotero.DBConnection.prototype.tableExists = Zotero.Promise.coroutine(function* (table) {
|
||||
Zotero.DBConnection.prototype.tableExists = Zotero.Promise.coroutine(function* (table, db) {
|
||||
yield this._getConnectionAsync();
|
||||
var sql = "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND tbl_name=?";
|
||||
var prefix = db ? db + '.' : '';
|
||||
var sql = `SELECT COUNT(*) FROM ${prefix}sqlite_master WHERE type='table' AND tbl_name=?`;
|
||||
var count = yield this.valueQueryAsync(sql, [table]);
|
||||
return !!count;
|
||||
});
|
||||
|
@ -879,7 +898,7 @@ Zotero.DBConnection.prototype.vacuum = function () {
|
|||
// TEMP
|
||||
Zotero.DBConnection.prototype.info = Zotero.Promise.coroutine(function* () {
|
||||
var info = {};
|
||||
var pragmas = ['auto_vacuum', 'cache_size', 'locking_mode', 'page_size'];
|
||||
var pragmas = ['auto_vacuum', 'cache_size', 'main.locking_mode', 'page_size'];
|
||||
for (let p of pragmas) {
|
||||
info[p] = yield Zotero.DB.valueQueryAsync(`PRAGMA ${p}`);
|
||||
}
|
||||
|
@ -894,9 +913,13 @@ Zotero.DBConnection.prototype.integrityCheck = Zotero.Promise.coroutine(function
|
|||
|
||||
|
||||
Zotero.DBConnection.prototype.checkException = function (e) {
|
||||
if (this._externalDB) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (e.message.includes(this.DB_CORRUPTION_STRING)) {
|
||||
// Write corrupt marker to data directory
|
||||
var file = Zotero.File.pathToFile(Zotero.DataDirectory.getDatabase(this._dbName, 'is.corrupt'));
|
||||
var file = Zotero.File.pathToFile(this._dbPath + '.is.corrupt');
|
||||
Zotero.File.putContents(file, '');
|
||||
|
||||
this._dbIsCorrupt = true;
|
||||
|
@ -947,6 +970,11 @@ Zotero.DBConnection.prototype.closeDatabase = Zotero.Promise.coroutine(function*
|
|||
|
||||
|
||||
Zotero.DBConnection.prototype.backupDatabase = Zotero.Promise.coroutine(function* (suffix, force) {
|
||||
if (this.skipBackup || this._externalDB || Zotero.skipLoading) {
|
||||
this._debug("Skipping backup of database '" + this._dbName + "'", 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
var storageService = Components.classes["@mozilla.org/storage/service;1"]
|
||||
.getService(Components.interfaces.mozIStorageService);
|
||||
|
||||
|
@ -980,27 +1008,21 @@ Zotero.DBConnection.prototype.backupDatabase = Zotero.Promise.coroutine(function
|
|||
});
|
||||
|
||||
try {
|
||||
var corruptMarker = Zotero.File.pathToFile(
|
||||
Zotero.DataDirectory.getDatabase(this._dbName, 'is.corrupt')
|
||||
);
|
||||
let corruptMarker = Zotero.File.pathToFile(this._dbPath + '.is.corrupt');
|
||||
|
||||
if (this.skipBackup || Zotero.skipLoading) {
|
||||
this._debug("Skipping backup of database '" + this._dbName + "'", 1);
|
||||
return false;
|
||||
}
|
||||
else if (this._dbIsCorrupt || corruptMarker.exists()) {
|
||||
if (this._dbIsCorrupt || corruptMarker.exists()) {
|
||||
this._debug("Database '" + this._dbName + "' is marked as corrupt -- skipping backup", 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
var file = Zotero.File.pathToFile(Zotero.DataDirectory.getDatabase(this._dbName));
|
||||
let file = this._dbPath;
|
||||
|
||||
// For standard backup, make sure last backup is old enough to replace
|
||||
if (!suffix && !force) {
|
||||
var backupFile = Zotero.File.pathToFile(Zotero.DataDirectory.getDatabase(this._dbName, 'bak'));
|
||||
if (yield OS.File.exists(backupFile.path)) {
|
||||
var currentDBTime = (yield OS.File.stat(file.path)).lastModificationDate;
|
||||
var lastBackupTime = (yield OS.File.stat(backupFile.path)).lastModificationDate;
|
||||
let backupFile = this._dbPath + '.bak';
|
||||
if (yield OS.File.exists(backupFile)) {
|
||||
let currentDBTime = (yield OS.File.stat(file.path)).lastModificationDate;
|
||||
let lastBackupTime = (yield OS.File.stat(backupFile)).lastModificationDate;
|
||||
if (currentDBTime == lastBackupTime) {
|
||||
Zotero.debug("Database '" + this._dbName + "' hasn't changed -- skipping backup");
|
||||
return;
|
||||
|
@ -1021,7 +1043,7 @@ Zotero.DBConnection.prototype.backupDatabase = Zotero.Promise.coroutine(function
|
|||
|
||||
// Copy via a temporary file so we don't run into disk space issues
|
||||
// after deleting the old backup file
|
||||
var tmpFile = Zotero.DataDirectory.getDatabase(this._dbName, 'tmp');
|
||||
var tmpFile = this._dbPath + '.tmp';
|
||||
if (yield OS.File.exists(tmpFile)) {
|
||||
try {
|
||||
yield OS.File.remove(tmpFile);
|
||||
|
@ -1038,18 +1060,21 @@ Zotero.DBConnection.prototype.backupDatabase = Zotero.Promise.coroutine(function
|
|||
// the lock is lost
|
||||
try {
|
||||
if (DB_LOCK_EXCLUSIVE) {
|
||||
yield this.queryAsync("PRAGMA locking_mode=NORMAL", false, { inBackup: true });
|
||||
yield this.queryAsync("PRAGMA main.locking_mode=NORMAL", false, { inBackup: true });
|
||||
}
|
||||
storageService.backupDatabaseFile(file, OS.Path.basename(tmpFile), file.parent);
|
||||
storageService.backupDatabaseFile(
|
||||
Zotero.File.pathToFile(file),
|
||||
OS.Path.basename(tmpFile),
|
||||
Zotero.File.pathToFile(file).parent
|
||||
);
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.debug(e);
|
||||
Components.utils.reportError(e);
|
||||
Zotero.logError(e);
|
||||
return false;
|
||||
}
|
||||
finally {
|
||||
if (DB_LOCK_EXCLUSIVE) {
|
||||
yield this.queryAsync("PRAGMA locking_mode=EXCLUSIVE", false, { inBackup: true });
|
||||
yield this.queryAsync("PRAGMA main.locking_mode=EXCLUSIVE", false, { inBackup: true });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1080,7 +1105,7 @@ Zotero.DBConnection.prototype.backupDatabase = Zotero.Promise.coroutine(function
|
|||
// Special backup
|
||||
if (!suffix && numBackups > 1) {
|
||||
// Remove oldest backup file
|
||||
var targetFile = Zotero.DataDirectory.getDatabase(this._dbName, (numBackups - 1) + '.bak');
|
||||
let targetFile = this._dbPath + '.' + (numBackups - 1) + '.bak';
|
||||
if (yield OS.File.exists(targetFile)) {
|
||||
yield OS.File.remove(targetFile);
|
||||
}
|
||||
|
@ -1090,12 +1115,8 @@ Zotero.DBConnection.prototype.backupDatabase = Zotero.Promise.coroutine(function
|
|||
var targetNum = i;
|
||||
var sourceNum = targetNum - 1;
|
||||
|
||||
var targetFile = Zotero.DataDirectory.getDatabase(
|
||||
this._dbName, targetNum + '.bak'
|
||||
);
|
||||
var sourceFile = Zotero.DataDirectory.getDatabase(
|
||||
this._dbName, sourceNum ? sourceNum + '.bak' : 'bak'
|
||||
);
|
||||
let targetFile = this._dbPath + '.' + targetNum + '.bak';
|
||||
let sourceFile = this._dbPath + '.' + (sourceNum ? sourceNum + '.bak' : 'bak')
|
||||
|
||||
if (!(yield OS.File.exists(sourceFile))) {
|
||||
continue;
|
||||
|
@ -1107,9 +1128,7 @@ Zotero.DBConnection.prototype.backupDatabase = Zotero.Promise.coroutine(function
|
|||
}
|
||||
}
|
||||
|
||||
var backupFile = Zotero.DataDirectory.getDatabase(
|
||||
this._dbName, (suffix ? suffix + '.' : '') + 'bak'
|
||||
);
|
||||
let backupFile = this._dbPath + '.' + (suffix ? suffix + '.' : '') + 'bak';
|
||||
|
||||
// Remove old backup file
|
||||
if (yield OS.File.exists(backupFile)) {
|
||||
|
@ -1146,11 +1165,11 @@ Zotero.DBConnection.prototype._getConnection = function (options) {
|
|||
/*
|
||||
* Retrieve a link to the data store asynchronously
|
||||
*/
|
||||
Zotero.DBConnection.prototype._getConnectionAsync = Zotero.Promise.coroutine(function* (options) {
|
||||
Zotero.DBConnection.prototype._getConnectionAsync = async function (options) {
|
||||
// If a backup is in progress, wait until it's done
|
||||
if (this._backupPromise && this._backupPromise.isPending() && (!options || !options.inBackup)) {
|
||||
Zotero.debug("Waiting for database backup to complete", 2);
|
||||
yield this._backupPromise;
|
||||
await this._backupPromise;
|
||||
}
|
||||
|
||||
if (this._connection) {
|
||||
|
@ -1161,48 +1180,50 @@ Zotero.DBConnection.prototype._getConnectionAsync = Zotero.Promise.coroutine(fun
|
|||
}
|
||||
|
||||
this._debug("Asynchronously opening database '" + this._dbName + "'");
|
||||
Zotero.debug(this._dbPath);
|
||||
|
||||
// Get the storage service
|
||||
var store = Components.classes["@mozilla.org/storage/service;1"].
|
||||
getService(Components.interfaces.mozIStorageService);
|
||||
|
||||
var file = Zotero.File.pathToFile(Zotero.DataDirectory.getDatabase(this._dbName));
|
||||
var backupFile = Zotero.File.pathToFile(Zotero.DataDirectory.getDatabase(this._dbName, 'bak'));
|
||||
|
||||
var fileName = this._dbName + '.sqlite';
|
||||
var file = this._dbPath;
|
||||
var backupFile = this._dbPath + '.bak';
|
||||
var fileName = OS.Path.basename(file);
|
||||
var corruptMarker = this._dbPath + '.is.corrupt';
|
||||
|
||||
catchBlock: try {
|
||||
var corruptMarker = Zotero.File.pathToFile(Zotero.DataDirectory.getDatabase(this._dbName, 'is.corrupt'));
|
||||
if (corruptMarker.exists()) {
|
||||
if (await OS.File.exists(corruptMarker)) {
|
||||
throw new Error(this.DB_CORRUPTION_STRING);
|
||||
}
|
||||
this._connection = yield Zotero.Promise.resolve(this.Sqlite.openConnection({
|
||||
path: file.path
|
||||
this._connection = await Zotero.Promise.resolve(this.Sqlite.openConnection({
|
||||
path: file
|
||||
}));
|
||||
}
|
||||
catch (e) {
|
||||
// Don't deal with corrupted external dbs
|
||||
if (this._externalDB) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
Zotero.logError(e);
|
||||
|
||||
if (e.message.includes(this.DB_CORRUPTION_STRING)) {
|
||||
this._debug("Database file '" + file.leafName + "' corrupted", 1);
|
||||
this._debug(`Database file '${fileName}' corrupted`, 1);
|
||||
|
||||
// No backup file! Eek!
|
||||
if (!backupFile.exists()) {
|
||||
if (!await OS.File.exists(backupFile)) {
|
||||
this._debug("No backup file for DB '" + this._dbName + "' exists", 1);
|
||||
|
||||
// Save damaged filed
|
||||
this._debug('Saving damaged DB file with .damaged extension', 1);
|
||||
var damagedFile = Zotero.File.pathToFile(
|
||||
Zotero.DataDirectory.getDatabase(this._dbName, 'damaged')
|
||||
);
|
||||
let damagedFile = this._dbPath + '.damaged';
|
||||
Zotero.moveToUnique(file, damagedFile);
|
||||
|
||||
// Create new main database
|
||||
var file = Zotero.File.pathToFile(Zotero.DataDirectory.getDatabase(this._dbName));
|
||||
this._connection = store.openDatabase(file);
|
||||
|
||||
if (corruptMarker.exists()) {
|
||||
corruptMarker.remove(null);
|
||||
if (await OS.File.exists(corruptMarker)) {
|
||||
await OS.File.remove(corruptMarker);
|
||||
}
|
||||
|
||||
Zotero.alert(
|
||||
|
@ -1215,24 +1236,21 @@ Zotero.DBConnection.prototype._getConnectionAsync = Zotero.Promise.coroutine(fun
|
|||
|
||||
// Save damaged file
|
||||
this._debug('Saving damaged DB file with .damaged extension', 1);
|
||||
var damagedFile = Zotero.File.pathToFile(
|
||||
Zotero.DataDirectory.getDatabase(this._dbName, 'damaged')
|
||||
);
|
||||
let damagedFile = this._dbPath + '.damaged';
|
||||
Zotero.moveToUnique(file, damagedFile);
|
||||
|
||||
// Test the backup file
|
||||
try {
|
||||
Zotero.debug("Asynchronously opening DB connection");
|
||||
this._connection = yield Zotero.Promise.resolve(this.Sqlite.openConnection({
|
||||
path: backupFile.path
|
||||
this._connection = await Zotero.Promise.resolve(this.Sqlite.openConnection({
|
||||
path: backupFile
|
||||
}));
|
||||
}
|
||||
// Can't open backup either
|
||||
catch (e) {
|
||||
// Create new main database
|
||||
var file = Zotero.File.pathToFile(Zotero.DataDirectory.getDatabase(this._dbName));
|
||||
this._connection = yield Zotero.Promise.resolve(this.Sqlite.openConnection({
|
||||
path: file.path
|
||||
this._connection = await Zotero.Promise.resolve(this.Sqlite.openConnection({
|
||||
path: file
|
||||
}));
|
||||
|
||||
Zotero.alert(
|
||||
|
@ -1241,8 +1259,8 @@ Zotero.DBConnection.prototype._getConnectionAsync = Zotero.Promise.coroutine(fun
|
|||
Zotero.getString('db.dbRestoreFailed', fileName)
|
||||
);
|
||||
|
||||
if (corruptMarker.exists()) {
|
||||
corruptMarker.remove(null);
|
||||
if (await OS.File.exists(corruptMarker)) {
|
||||
await OS.File.remove(corruptMarker);
|
||||
}
|
||||
|
||||
break catchBlock;
|
||||
|
@ -1253,7 +1271,7 @@ Zotero.DBConnection.prototype._getConnectionAsync = Zotero.Promise.coroutine(fun
|
|||
// Copy backup file to main DB file
|
||||
this._debug("Restoring database '" + this._dbName + "' from backup file", 1);
|
||||
try {
|
||||
backupFile.copyTo(backupFile.parent, fileName);
|
||||
await OS.File.copy(backupFile, file);
|
||||
}
|
||||
catch (e) {
|
||||
// TODO: deal with low disk space
|
||||
|
@ -1261,8 +1279,7 @@ Zotero.DBConnection.prototype._getConnectionAsync = Zotero.Promise.coroutine(fun
|
|||
}
|
||||
|
||||
// Open restored database
|
||||
var file = OS.Path.join(Zotero.DataDirectory.dir, fileName);
|
||||
this._connection = yield Zotero.Promise.resolve(this.Sqlite.openConnection({
|
||||
this._connection = await Zotero.Promise.resolve(this.Sqlite.openConnection({
|
||||
path: file
|
||||
}));
|
||||
this._debug('Database restored', 1);
|
||||
|
@ -1271,13 +1288,13 @@ Zotero.DBConnection.prototype._getConnectionAsync = Zotero.Promise.coroutine(fun
|
|||
Zotero.getString('general.warning'),
|
||||
Zotero.getString('db.dbRestored', [
|
||||
fileName,
|
||||
Zotero.Date.getFileDateString(backupFile),
|
||||
Zotero.Date.getFileTimeString(backupFile)
|
||||
Zotero.Date.getFileDateString(Zotero.File.pathToFile(backupFile)),
|
||||
Zotero.Date.getFileTimeString(Zotero.File.pathToFile(backupFile))
|
||||
])
|
||||
);
|
||||
|
||||
if (corruptMarker.exists()) {
|
||||
corruptMarker.remove(null);
|
||||
if (await OS.File.exists(corruptMarker)) {
|
||||
await OS.File.remove(corruptMarker);
|
||||
}
|
||||
|
||||
break catchBlock;
|
||||
|
@ -1287,44 +1304,36 @@ Zotero.DBConnection.prototype._getConnectionAsync = Zotero.Promise.coroutine(fun
|
|||
throw (e);
|
||||
}
|
||||
|
||||
if (DB_LOCK_EXCLUSIVE) {
|
||||
yield this.queryAsync("PRAGMA locking_mode=EXCLUSIVE");
|
||||
if (!this._externalDB) {
|
||||
if (DB_LOCK_EXCLUSIVE) {
|
||||
await this.queryAsync("PRAGMA main.locking_mode=EXCLUSIVE");
|
||||
}
|
||||
else {
|
||||
await this.queryAsync("PRAGMA main.locking_mode=NORMAL");
|
||||
}
|
||||
|
||||
// Set page cache size to 8MB
|
||||
let pageSize = await this.valueQueryAsync("PRAGMA page_size");
|
||||
let cacheSize = 8192000 / pageSize;
|
||||
await this.queryAsync("PRAGMA cache_size=" + cacheSize);
|
||||
|
||||
// Enable foreign key checks
|
||||
await this.queryAsync("PRAGMA foreign_keys=true");
|
||||
|
||||
// Register idle observer for DB backup
|
||||
Zotero.Schema.schemaUpdatePromise.then(() => {
|
||||
Zotero.debug("Initializing DB backup idle observer");
|
||||
var idleService = Components.classes["@mozilla.org/widget/idleservice;1"]
|
||||
.getService(Components.interfaces.nsIIdleService);
|
||||
idleService.addIdleObserver(this, 300);
|
||||
});
|
||||
}
|
||||
else {
|
||||
yield this.queryAsync("PRAGMA locking_mode=NORMAL");
|
||||
}
|
||||
|
||||
// Set page cache size to 8MB
|
||||
var pageSize = yield this.valueQueryAsync("PRAGMA page_size");
|
||||
var cacheSize = 8192000 / pageSize;
|
||||
yield this.queryAsync("PRAGMA cache_size=" + cacheSize);
|
||||
|
||||
// Enable foreign key checks
|
||||
yield this.queryAsync("PRAGMA foreign_keys=true");
|
||||
|
||||
// Register idle observer for DB backup
|
||||
Zotero.Schema.schemaUpdatePromise.then(() => {
|
||||
Zotero.debug("Initializing DB backup idle observer");
|
||||
var idleService = Components.classes["@mozilla.org/widget/idleservice;1"]
|
||||
.getService(Components.interfaces.nsIIdleService);
|
||||
idleService.addIdleObserver(this, 300);
|
||||
});
|
||||
|
||||
return this._connection;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Zotero.DBConnection.prototype._debug = function (str, level) {
|
||||
var prefix = this._dbName == 'zotero' ? '' : '[' + this._dbName + '] ';
|
||||
Zotero.debug(prefix + str, level);
|
||||
}
|
||||
|
||||
|
||||
// Initialize main database connection
|
||||
Zotero.DB = new Zotero.DBConnection('zotero');
|
||||
|
||||
Zotero.DB.IncompatibleVersionException = function (msg, dbClientVersion) {
|
||||
this.message = msg;
|
||||
this.dbClientVersion = dbClientVersion;
|
||||
}
|
||||
Zotero.DB.IncompatibleVersionException.prototype = Object.create(Error.prototype);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -26,7 +26,6 @@
|
|||
Zotero.MIME = new function(){
|
||||
this.isTextType = isTextType;
|
||||
this.getPrimaryExtension = getPrimaryExtension;
|
||||
this.sniffForMIMEType = sniffForMIMEType;
|
||||
this.sniffForBinary = sniffForBinary;
|
||||
this.hasNativeHandler = hasNativeHandler;
|
||||
this.hasInternalHandler = hasInternalHandler;
|
||||
|
@ -48,8 +47,9 @@ Zotero.MIME = new function(){
|
|||
["\uFFFDPNG", 'image/png', 0],
|
||||
["JFIF", 'image/jpeg'],
|
||||
["FLV", "video/x-flv", 0],
|
||||
["\u0000\u0000\u0001\u0000", "image/vnd.microsoft.icon", 0]
|
||||
|
||||
["\u0000\u0000\u0001\u0000", "image/vnd.microsoft.icon", 0],
|
||||
["\u0053\u0051\u004C\u0069\u0074\u0065\u0020\u0066"
|
||||
+ "\u006F\u0072\u006D\u0061\u0074\u0020\u0033\u0000", "application/x-sqlite3", 0]
|
||||
];
|
||||
|
||||
var _extensions = {
|
||||
|
@ -228,12 +228,12 @@ Zotero.MIME = new function(){
|
|||
/*
|
||||
* Searches string for magic numbers
|
||||
*/
|
||||
function sniffForMIMEType(str){
|
||||
for (var i in _snifferEntries){
|
||||
var match = false;
|
||||
this.sniffForMIMEType = function (str) {
|
||||
for (let i in _snifferEntries) {
|
||||
let match = false;
|
||||
// If an offset is defined, match only from there
|
||||
if (typeof _snifferEntries[i][2] != 'undefined') {
|
||||
if (str.substr(i[2]).indexOf(_snifferEntries[i][0]) == 0) {
|
||||
if (_snifferEntries[i][2] != undefined) {
|
||||
if (str.substr(_snifferEntries[i][2]).indexOf(_snifferEntries[i][0]) == 0) {
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
|
@ -274,7 +274,7 @@ Zotero.MIME = new function(){
|
|||
* ext is an optional file extension hint if data sniffing is unsuccessful
|
||||
*/
|
||||
this.getMIMETypeFromData = function (str, ext){
|
||||
var mimeType = sniffForMIMEType(str);
|
||||
var mimeType = this.sniffForMIMEType(str);
|
||||
if (mimeType){
|
||||
Zotero.debug('Detected MIME type ' + mimeType);
|
||||
return mimeType;
|
||||
|
|
287
chrome/content/zotero/xpcom/openPDF.js
Normal file
287
chrome/content/zotero/xpcom/openPDF.js
Normal file
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2018 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
https://zotero.org
|
||||
|
||||
This file is part of Zotero.
|
||||
|
||||
Zotero is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Zotero is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
Zotero.OpenPDF = {
|
||||
openToPage: async function (path, page) {
|
||||
var handler = Zotero.Prefs.get("fileHandler.pdf");
|
||||
var opened = false;
|
||||
if (Zotero.isMac) {
|
||||
if (handler.includes('Preview')) {
|
||||
this._openWithPreview(path, page);
|
||||
}
|
||||
else if (handler.includes('Skim')) {
|
||||
this._openWithSkim(path, page);
|
||||
}
|
||||
else if (handler.includes('PDF Expert')) {
|
||||
this._openWithPDFExpert(path, page);
|
||||
}
|
||||
else {
|
||||
// Try to detect default app
|
||||
handler = this._getPDFHandlerName();
|
||||
Zotero.debug(`Handler is ${handler}`);
|
||||
if (handler && handler == 'Skim') {
|
||||
this._openWithSkim(path, page);
|
||||
}
|
||||
else if (handler && handler == 'PDF Expert') {
|
||||
this._openWithPDFExpert(path, page);
|
||||
}
|
||||
// Fall back to Preview
|
||||
else {
|
||||
this._openWithPreview(path, page);
|
||||
}
|
||||
}
|
||||
opened = true;
|
||||
}
|
||||
else if (Zotero.isWin) {
|
||||
handler = handler || this._getPDFHandlerWindows();
|
||||
// Include flags to open the PDF on a given page in various apps
|
||||
//
|
||||
// Adobe Acrobat: http://partners.adobe.com/public/developer/en/acrobat/PDFOpenParameters.pdf
|
||||
// PDF-XChange: http://help.tracker-software.com/eu/default.aspx?pageid=PDFXView25:command_line_options
|
||||
let args = ['/A', 'page=' + page, path];
|
||||
Zotero.Utilities.Internal.exec(handler, args);
|
||||
opened = true;
|
||||
}
|
||||
else if (Zotero.isLinux) {
|
||||
if (handler.includes('evince') || handler.includes('okular')) {
|
||||
this._openWithEvinceOrOkular(handler, path, page);
|
||||
opened = true;
|
||||
}
|
||||
else {
|
||||
let handler = await this._getPDFHandlerLinux();
|
||||
if (handler.includes('evince') || handler.includes('okular')) {
|
||||
this._openWithEvinceOrOkular(handler, path, page);
|
||||
opened = true;
|
||||
}
|
||||
// Fall back to okular and then evince if unknown handler
|
||||
else if (await OS.File.exists('/usr/bin/okular')) {
|
||||
this._openWithEvinceOrOkular('/usr/bin/okular', path, page);
|
||||
opened = true;
|
||||
}
|
||||
else if (await OS.File.exists('/usr/bin/evince')) {
|
||||
this._openWithEvinceOrOkular('/usr/bin/evince', path, page);
|
||||
opened = true;
|
||||
}
|
||||
else {
|
||||
Zotero.debug("No handler found");
|
||||
}
|
||||
}
|
||||
}
|
||||
return opened;
|
||||
},
|
||||
|
||||
_getPDFHandlerName: function () {
|
||||
var handlerService = Cc["@mozilla.org/uriloader/handler-service;1"]
|
||||
.getService(Ci.nsIHandlerService);
|
||||
var handlers = handlerService.enumerate();
|
||||
var handler;
|
||||
while (handlers.hasMoreElements()) {
|
||||
let handlerInfo = handlers.getNext().QueryInterface(Ci.nsIHandlerInfo);
|
||||
if (handlerInfo.type == 'application/pdf') {
|
||||
handler = handlerInfo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!handler) {
|
||||
// We can't get the name of the system default handler unless we add an entry
|
||||
Zotero.debug("Default handler not found -- adding default entry");
|
||||
let mimeService = Components.classes["@mozilla.org/mime;1"]
|
||||
.getService(Components.interfaces.nsIMIMEService);
|
||||
let mimeInfo = mimeService.getFromTypeAndExtension("application/pdf", "");
|
||||
mimeInfo.preferredAction = 4;
|
||||
mimeInfo.alwaysAskBeforeHandling = false;
|
||||
handlerService.store(mimeInfo);
|
||||
|
||||
// And once we do that, we can get the name (but not the path, unfortunately)
|
||||
let handlers = handlerService.enumerate();
|
||||
while (handlers.hasMoreElements()) {
|
||||
let handlerInfo = handlers.getNext().QueryInterface(Ci.nsIHandlerInfo);
|
||||
if (handlerInfo.type == 'application/pdf') {
|
||||
handler = handlerInfo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (handler) {
|
||||
Zotero.debug(`Default handler is ${handler.defaultDescription}`);
|
||||
return handler.defaultDescription;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
//
|
||||
// Mac
|
||||
//
|
||||
_openWithPreview: async function (filePath, page) {
|
||||
await Zotero.Utilities.Internal.exec('/usr/bin/open', ['-a', 'Preview', filePath]);
|
||||
// Go to page using AppleScript
|
||||
let args = [
|
||||
'-e', 'tell app "Preview" to activate',
|
||||
'-e', 'tell app "System Events" to keystroke "g" using {option down, command down}',
|
||||
'-e', `tell app "System Events" to keystroke "${page}"`,
|
||||
'-e', 'tell app "System Events" to keystroke return'
|
||||
];
|
||||
await Zotero.Utilities.Internal.exec('/usr/bin/osascript', args);
|
||||
},
|
||||
|
||||
_openWithSkim: async function (filePath, page) {
|
||||
// Escape double-quotes in path
|
||||
var quoteRE = /"/g;
|
||||
filePath = filePath.replace(quoteRE, '\\"');
|
||||
let filename = OS.Path.basename(filePath).replace(quoteRE, '\\"');
|
||||
let args = [
|
||||
'-e', 'tell app "Skim" to activate',
|
||||
'-e', `tell app "Skim" to open "${filePath}"`
|
||||
];
|
||||
args.push('-e', `tell document "${filename}" of application "Skim" to go to page ${page}`);
|
||||
await Zotero.Utilities.Internal.exec('/usr/bin/osascript', args);
|
||||
},
|
||||
|
||||
_openWithPDFExpert: async function (filePath, page) {
|
||||
await Zotero.Utilities.Internal.exec('/usr/bin/open', ['-a', 'PDF Expert', filePath]);
|
||||
// Go to page using AppleScript (same as Preview)
|
||||
let args = [
|
||||
'-e', 'tell app "PDF Expert" to activate',
|
||||
'-e', 'tell app "System Events" to keystroke "g" using {option down, command down}',
|
||||
'-e', `tell app "System Events" to keystroke "${page}"`,
|
||||
'-e', 'tell app "System Events" to keystroke return'
|
||||
];
|
||||
await Zotero.Utilities.Internal.exec('/usr/bin/osascript', args);
|
||||
},
|
||||
|
||||
//
|
||||
// Windows
|
||||
//
|
||||
/**
|
||||
* Get path to default pdf reader application on windows
|
||||
* @return {string} Path to default pdf reader application
|
||||
*
|
||||
* From getPDFReader() in ZotFile (GPL)
|
||||
* https://github.com/jlegewie/zotfile/blob/master/chrome/content/zotfile/utils.js
|
||||
*/
|
||||
_getPDFHandlerWindows: function () {
|
||||
var wrk = Components.classes["@mozilla.org/windows-registry-key;1"]
|
||||
.createInstance(Components.interfaces.nsIWindowsRegKey);
|
||||
// Get handler for PDFs
|
||||
var tryKeys = [
|
||||
{
|
||||
root: wrk.ROOT_KEY_CURRENT_USER,
|
||||
path: 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\.pdf\\UserChoice',
|
||||
value: 'Progid'
|
||||
},
|
||||
{
|
||||
root: wrk.ROOT_KEY_CLASSES_ROOT,
|
||||
path: '.pdf',
|
||||
value: ''
|
||||
}
|
||||
];
|
||||
var progId;
|
||||
for (let i = 0; !progId && i < tryKeys.length; i++) {
|
||||
try {
|
||||
wrk.open(
|
||||
tryKeys[i].root,
|
||||
tryKeys[i].path,
|
||||
wrk.ACCESS_READ
|
||||
);
|
||||
progId = wrk.readStringValue(tryKeys[i].value);
|
||||
}
|
||||
catch (e) {}
|
||||
}
|
||||
|
||||
if (!progId) {
|
||||
wrk.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get version specific handler, if it exists
|
||||
try {
|
||||
wrk.open(
|
||||
wrk.ROOT_KEY_CLASSES_ROOT,
|
||||
progId + '\\CurVer',
|
||||
wrk.ACCESS_READ
|
||||
);
|
||||
progId = wrk.readStringValue('') || progId;
|
||||
}
|
||||
catch (e) {}
|
||||
|
||||
// Get command
|
||||
var success = false;
|
||||
tryKeys = [
|
||||
progId + '\\shell\\Read\\command',
|
||||
progId + '\\shell\\Open\\command'
|
||||
];
|
||||
for (let i = 0; !success && i < tryKeys.length; i++) {
|
||||
try {
|
||||
wrk.open(
|
||||
wrk.ROOT_KEY_CLASSES_ROOT,
|
||||
tryKeys[i],
|
||||
wrk.ACCESS_READ
|
||||
);
|
||||
success = true;
|
||||
}
|
||||
catch (e) {}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
wrk.close();
|
||||
return;
|
||||
}
|
||||
|
||||
var command = wrk.readStringValue('').match(/^(?:".+?"|[^"]\S+)/);
|
||||
|
||||
wrk.close();
|
||||
|
||||
if (!command) return;
|
||||
return command[0].replace(/"/g, '');
|
||||
},
|
||||
|
||||
//
|
||||
// Linux
|
||||
//
|
||||
_getPDFHandlerLinux: async function () {
|
||||
var name = this._getPDFHandlerName();
|
||||
switch (name.toLowerCase()) {
|
||||
case 'okular':
|
||||
return `/usr/bin/${name}`;
|
||||
|
||||
// It's "Document Viewer" on stock Ubuntu
|
||||
case 'document viewer':
|
||||
case 'evince':
|
||||
return `/usr/bin/evince`;
|
||||
}
|
||||
|
||||
// TODO: Try to get default from mimeapps.list, etc., in case system default is okular
|
||||
// or evince somewhere other than /usr/bin
|
||||
var homeDir = OS.Constants.Path.homeDir;
|
||||
|
||||
return false;
|
||||
|
||||
},
|
||||
|
||||
_openWithEvinceOrOkular: function (appPath, filePath, page) {
|
||||
var args = ['-p', page, filePath];
|
||||
Zotero.Utilities.Internal.exec(appPath, args);
|
||||
}
|
||||
}
|
|
@ -129,10 +129,12 @@ Zotero.Profile = {
|
|||
/**
|
||||
* Find other profile directories (for this app or the other app) using the given data directory
|
||||
*
|
||||
* @param {String} dataDir
|
||||
* @param {Boolean} [includeOtherApps=false] - Check Firefox profiles
|
||||
* @return {String[]}
|
||||
*/
|
||||
findOtherProfilesUsingDataDirectory: Zotero.Promise.coroutine(function* (dataDir) {
|
||||
let otherAppProfiles = yield this._findOtherAppProfiles();
|
||||
findOtherProfilesUsingDataDirectory: Zotero.Promise.coroutine(function* (dataDir, includeOtherApps = true) {
|
||||
let otherAppProfiles = includeOtherApps ? (yield this._findOtherAppProfiles()) : [];
|
||||
let otherProfiles = (yield this._findOtherProfiles()).concat(otherAppProfiles);
|
||||
|
||||
// First get profiles pointing at this directory
|
||||
|
@ -238,6 +240,38 @@ Zotero.Profile = {
|
|||
},
|
||||
|
||||
|
||||
readPrefsFromFile: async function (prefsFile) {
|
||||
var sandbox = new Components.utils.Sandbox("http://www.example.com/");
|
||||
Components.utils.evalInSandbox(
|
||||
"var prefs = {};"+
|
||||
"function user_pref(key, val) {"+
|
||||
"prefs[key] = val;"+
|
||||
"}"
|
||||
, sandbox);
|
||||
|
||||
(await Zotero.File.getContentsAsync(prefsFile))
|
||||
.split(/\n/)
|
||||
.filter((line) => {
|
||||
// Strip comments
|
||||
return !line.startsWith('#')
|
||||
// Only process lines in our pref branch
|
||||
&& line.includes(ZOTERO_CONFIG.PREF_BRANCH);
|
||||
})
|
||||
// Process each line individually
|
||||
.forEach((line) => {
|
||||
try {
|
||||
Zotero.debug("Processing " + line);
|
||||
Components.utils.evalInSandbox(line, sandbox);
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError("Error processing prefs line: " + line);
|
||||
}
|
||||
});
|
||||
|
||||
return sandbox.prefs;
|
||||
},
|
||||
|
||||
|
||||
//
|
||||
// Private methods
|
||||
//
|
||||
|
|
|
@ -154,6 +154,9 @@ Zotero.ProgressWindow = function(options = {}) {
|
|||
_progressWindow.addEventListener("mouseover", _onMouseOver, false);
|
||||
_progressWindow.addEventListener("mouseout", _onMouseOut, false);
|
||||
_progressWindow.addEventListener("mouseup", _onMouseUp, false);
|
||||
_window.addEventListener('close', () => {
|
||||
this.close();
|
||||
});
|
||||
|
||||
_windowLoading = true;
|
||||
|
||||
|
@ -281,7 +284,10 @@ Zotero.ProgressWindow = function(options = {}) {
|
|||
|
||||
try {
|
||||
_progressWindow.close();
|
||||
} catch(ex) {}
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -321,7 +321,11 @@ Zotero.Proxy.prototype.compileRegexp = function() {
|
|||
})
|
||||
|
||||
// now replace with regexp fragment in reverse order
|
||||
var re = "^"+Zotero.Utilities.quotemeta(this.scheme)+"$";
|
||||
if (this.scheme.includes('://')) {
|
||||
re = "^"+Zotero.Utilities.quotemeta(this.scheme)+"$";
|
||||
} else {
|
||||
re = "^https?"+Zotero.Utilities.quotemeta('://'+this.scheme)+"$";
|
||||
}
|
||||
for(var i=this.parameters.length-1; i>=0; i--) {
|
||||
var param = this.parameters[i];
|
||||
re = re.replace(Zotero_Proxy_schemeParameterRegexps[param], "$1"+parametersToCheck[param]);
|
||||
|
|
|
@ -186,7 +186,7 @@ Zotero.RecognizePDF = new function () {
|
|||
};
|
||||
|
||||
|
||||
this.report = async function (item) {
|
||||
this.report = async function (item, description) {
|
||||
var attachment = Zotero.Items.get(item.getAttachments()[0]);
|
||||
var filePath = attachment.getFilePath();
|
||||
if (!filePath || !await OS.File.exists(filePath)) {
|
||||
|
@ -197,7 +197,7 @@ Zotero.RecognizePDF = new function () {
|
|||
var json = await extractJSON(filePath, MAX_PAGES);
|
||||
var metadata = item.toJSON();
|
||||
|
||||
var data = { version, json, metadata };
|
||||
var data = { description, version, json, metadata };
|
||||
var uri = ZOTERO_CONFIG.RECOGNIZE_URL + 'report';
|
||||
return Zotero.HTTP.request(
|
||||
"POST",
|
||||
|
@ -539,16 +539,46 @@ Zotero.RecognizePDF = new function () {
|
|||
let res = await _query(json);
|
||||
if (!res) return null;
|
||||
|
||||
if (res.doi) {
|
||||
Zotero.debug('RecognizePDF: Getting metadata by DOI');
|
||||
let translateDOI = new Zotero.Translate.Search();
|
||||
translateDOI.setTranslator('11645bd1-0420-45c1-badb-53fb41eeb753');
|
||||
translateDOI.setSearch({'itemType': 'journalArticle', 'DOI': res.doi});
|
||||
if (res.arxiv) {
|
||||
Zotero.debug('RecognizePDF: Getting metadata by arXiv');
|
||||
let translate = new Zotero.Translate.Search();
|
||||
translate.setIdentifier({arXiv: res.arxiv});
|
||||
let translators = await translate.getTranslators();
|
||||
translate.setTranslator(translators);
|
||||
|
||||
try {
|
||||
let newItem = await _promiseTranslate(translateDOI, libraryID);
|
||||
let newItem = await _promiseTranslate(translate, libraryID);
|
||||
if (!newItem.abstractNote && res.abstract) {
|
||||
newItem.setField('abstractNote', res.abstract);
|
||||
}
|
||||
if (!newItem.language && res.language) {
|
||||
newItem.setField('language', res.language);
|
||||
}
|
||||
newItem.saveTx();
|
||||
return newItem;
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.debug('RecognizePDF: ' + e);
|
||||
}
|
||||
}
|
||||
|
||||
if (res.doi) {
|
||||
Zotero.debug('RecognizePDF: Getting metadata by DOI');
|
||||
let translate = new Zotero.Translate.Search();
|
||||
translate.setIdentifier({
|
||||
DOI: res.doi
|
||||
});
|
||||
let translators = await translate.getTranslators();
|
||||
translate.setTranslator(translators);
|
||||
|
||||
try {
|
||||
let newItem = await _promiseTranslate(translate, libraryID);
|
||||
if (!newItem.abstractNote && res.abstract) {
|
||||
newItem.setField('abstractNote', res.abstract);
|
||||
}
|
||||
if (!newItem.language && res.language) {
|
||||
newItem.setField('language', res.language);
|
||||
}
|
||||
newItem.saveTx();
|
||||
return newItem;
|
||||
}
|
||||
|
@ -570,11 +600,27 @@ Zotero.RecognizePDF = new function () {
|
|||
Zotero.debug(translatedItems);
|
||||
if (translatedItems.length) {
|
||||
let newItem = new Zotero.Item;
|
||||
newItem.fromJSON(translatedItems[0]);
|
||||
newItem.libraryID = libraryID;
|
||||
// Convert tags to automatic. For other items this is done automatically in
|
||||
// translate.js for other items, but for ISBNs we just get the data
|
||||
// (libraryID=false) and do the saving manually.
|
||||
translatedItems[0].tags = translatedItems[0].tags.map(tag => {
|
||||
if (typeof tag == 'string') {
|
||||
return {
|
||||
tag,
|
||||
type: 1
|
||||
};
|
||||
}
|
||||
tag.type = 1;
|
||||
return tag;
|
||||
});
|
||||
newItem.fromJSON(translatedItems[0]);
|
||||
if (!newItem.abstractNote && res.abstract) {
|
||||
newItem.setField('abstractNote', res.abstract);
|
||||
}
|
||||
if (!newItem.language && res.language) {
|
||||
newItem.setField('language', res.language);
|
||||
}
|
||||
newItem.saveTx();
|
||||
return newItem;
|
||||
}
|
||||
|
@ -585,7 +631,6 @@ Zotero.RecognizePDF = new function () {
|
|||
}
|
||||
|
||||
if (res.title) {
|
||||
|
||||
let type = 'journalArticle';
|
||||
|
||||
if (res.type === 'book-chapter') {
|
||||
|
@ -593,6 +638,7 @@ Zotero.RecognizePDF = new function () {
|
|||
}
|
||||
|
||||
let newItem = new Zotero.Item(type);
|
||||
newItem.libraryID = libraryID;
|
||||
newItem.setField('title', res.title);
|
||||
|
||||
let creators = [];
|
||||
|
@ -611,6 +657,7 @@ Zotero.RecognizePDF = new function () {
|
|||
if (res.pages) newItem.setField('pages', res.pages);
|
||||
if (res.volume) newItem.setField('volume', res.volume);
|
||||
if (res.url) newItem.setField('url', res.url);
|
||||
if (res.language) newItem.setField('language', res.language);
|
||||
|
||||
if (type === 'journalArticle') {
|
||||
if (res.issue) newItem.setField('issue', res.issue);
|
||||
|
|
|
@ -801,9 +801,8 @@ Zotero.Schema = new function(){
|
|||
catch (e) {
|
||||
if (e instanceof OS.File.Error && e.becauseExists) {
|
||||
// Could overwrite automatically, but we want to log this
|
||||
let msg = "Overwriting translator with same filename '" + entry.fileName + "'";
|
||||
Zotero.debug(msg, 1);
|
||||
Components.utils.reportError(msg);
|
||||
Zotero.warn("Overwriting translator with same filename '"
|
||||
+ entry.fileName + "'");
|
||||
yield OS.File.move(tmpFile, destFile);
|
||||
}
|
||||
else {
|
||||
|
@ -965,10 +964,8 @@ Zotero.Schema = new function(){
|
|||
catch (e) {
|
||||
if (e instanceof OS.File.Error && e.becauseExists) {
|
||||
// Could overwrite automatically, but we want to log this
|
||||
let msg = "Overwriting " + modeType + " with same filename "
|
||||
+ "'" + fileName + "'";
|
||||
Zotero.debug(msg, 1);
|
||||
Components.utils.reportError(msg);
|
||||
Zotero.warn("Overwriting " + modeType + " with same filename "
|
||||
+ "'" + fileName + "'", 1);
|
||||
yield OS.File.copy(entry.path, destFile);
|
||||
}
|
||||
else {
|
||||
|
@ -1549,8 +1546,13 @@ Zotero.Schema = new function(){
|
|||
return false;
|
||||
}
|
||||
if (dbVersion > schemaVersion) {
|
||||
throw new Error("Zotero '" + schema + "' DB version (" + dbVersion
|
||||
+ ") is newer than SQL file (" + schemaVersion + ")");
|
||||
let dbClientVersion = yield Zotero.DB.valueQueryAsync(
|
||||
"SELECT value FROM settings WHERE setting='client' AND key='lastCompatibleVersion'"
|
||||
);
|
||||
throw new Zotero.DB.IncompatibleVersionException(
|
||||
`Zotero '${schema}' DB version (${dbVersion}) is newer than SQL file (${schemaVersion})`,
|
||||
dbClientVersion
|
||||
);
|
||||
}
|
||||
let sql = yield _getSchemaSQL(schema);
|
||||
yield Zotero.DB.executeSQLFile(sql);
|
||||
|
@ -2414,6 +2416,31 @@ Zotero.Schema = new function(){
|
|||
yield Zotero.DB.queryAsync("DELETE FROM relationPredicates WHERE predicate='dc:isReplacedBy'");
|
||||
}
|
||||
|
||||
else if (i == 100) {
|
||||
let userID = yield Zotero.DB.valueQueryAsync("SELECT value FROM settings WHERE setting='account' AND key='userID'");
|
||||
let predicateID = yield Zotero.DB.valueQueryAsync("SELECT predicateID FROM relationPredicates WHERE predicate='dc:relation'");
|
||||
if (userID && predicateID) {
|
||||
let rows = yield Zotero.DB.queryAsync("SELECT itemID, object FROM items JOIN itemRelations IR USING (itemID) WHERE libraryID=? AND predicateID=?", [1, predicateID]);
|
||||
for (let row of rows) {
|
||||
let matches = row.object.match(/^http:\/\/zotero.org\/users\/(\d+)\/items\/([A-Z0-9]+)$/);
|
||||
if (matches) {
|
||||
// Wrong libraryID
|
||||
if (matches[1] != userID) {
|
||||
yield Zotero.DB.queryAsync(`UPDATE OR REPLACE itemRelations SET object='http://zotero.org/users/${userID}/items/${matches[2]}' WHERE itemID=? AND predicateID=?`, [row.itemID, predicateID]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (i == 101) {
|
||||
Components.utils.import("chrome://zotero/content/import/mendeley/mendeleyImport.js");
|
||||
let importer = new Zotero_Import_Mendeley();
|
||||
if (yield importer.hasImportedFiles()) {
|
||||
yield importer.queueFileCleanup();
|
||||
}
|
||||
}
|
||||
|
||||
// If breaking compatibility or doing anything dangerous, clear minorUpdateFrom
|
||||
}
|
||||
|
||||
|
|
|
@ -28,12 +28,15 @@ Zotero.Server = new function() {
|
|||
this.responseCodes = {
|
||||
200:"OK",
|
||||
201:"Created",
|
||||
204:"No Content",
|
||||
300:"Multiple Choices",
|
||||
400:"Bad Request",
|
||||
404:"Not Found",
|
||||
409:"Conflict",
|
||||
412:"Precondition Failed",
|
||||
500:"Internal Server Error",
|
||||
501:"Not Implemented",
|
||||
503:"Service Unavailable",
|
||||
504:"Gateway Timeout"
|
||||
};
|
||||
|
||||
|
@ -240,17 +243,18 @@ Zotero.Server.DataListener.prototype._headerFinished = function() {
|
|||
const hostRe = /[\r\n]Host: *(localhost|127\.0\.0\.1)(:[0-9]+)?[\r\n]/i;
|
||||
const contentTypeRe = /[\r\n]Content-Type: *([^ \r\n]+)/i;
|
||||
|
||||
if(!Zotero.isServer) {
|
||||
const originRe = /[\r\n]Origin: *([^ \r\n]+)/i;
|
||||
var m = originRe.exec(this.header);
|
||||
if(m) {
|
||||
this.origin = m[1];
|
||||
} else {
|
||||
const bookmarkletRe = /[\r\n]Zotero-Bookmarklet: *([^ \r\n]+)/i;
|
||||
var m = bookmarkletRe.exec(this.header);
|
||||
if(m) this.origin = "https://www.zotero.org";
|
||||
}
|
||||
|
||||
const originRe = /[\r\n]Origin: *([^ \r\n]+)/i;
|
||||
var m = originRe.exec(this.header);
|
||||
if (m) {
|
||||
this.origin = m[1];
|
||||
}
|
||||
else {
|
||||
const bookmarkletRe = /[\r\n]Zotero-Bookmarklet: *([^ \r\n]+)/i;
|
||||
var m = bookmarkletRe.exec(this.header);
|
||||
if (m) this.origin = "https://www.zotero.org";
|
||||
}
|
||||
|
||||
if (!Zotero.isServer) {
|
||||
// Make sure the Host header is set to localhost/127.0.0.1 to prevent DNS rebinding attacks
|
||||
if (!hostRe.exec(this.header)) {
|
||||
this._requestFinished(this._generateResponse(400, "text/plain", "Invalid Host header\n"));
|
||||
|
@ -329,21 +333,46 @@ Zotero.Server.DataListener.prototype._bodyData = function() {
|
|||
/**
|
||||
* Generates the response to an HTTP request
|
||||
*/
|
||||
Zotero.Server.DataListener.prototype._generateResponse = function(status, contentType, body) {
|
||||
Zotero.Server.DataListener.prototype._generateResponse = function (status, contentTypeOrHeaders, body) {
|
||||
var response = "HTTP/1.0 "+status+" "+Zotero.Server.responseCodes[status]+"\r\n";
|
||||
if(!Zotero.isServer) {
|
||||
|
||||
// Translation server
|
||||
if (Zotero.isServer) {
|
||||
// Add CORS headers if Origin header matches the allowed origins
|
||||
if (this.origin) {
|
||||
let allowedOrigins = Zotero.Prefs.get('httpServer.allowedOrigins')
|
||||
.split(/, */).filter(x => x);
|
||||
let allAllowed = allowedOrigins.includes('*');
|
||||
if (allAllowed || allowedOrigins.includes(this.origin)) {
|
||||
response += "Access-Control-Allow-Origin: " + (allAllowed ? '*' : this.origin) + "\r\n";
|
||||
response += "Access-Control-Allow-Methods: POST, GET, OPTIONS\r\n";
|
||||
response += "Access-Control-Allow-Headers: Content-Type\r\n";
|
||||
response += "Access-Control-Expose-Headers: Link\r\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
// Client
|
||||
else {
|
||||
response += "X-Zotero-Version: "+Zotero.version+"\r\n";
|
||||
response += "X-Zotero-Connector-API-Version: "+CONNECTOR_API_VERSION+"\r\n";
|
||||
if(this.origin === ZOTERO_CONFIG.BOOKMARKLET_ORIGIN ||
|
||||
|
||||
if (this.origin === ZOTERO_CONFIG.BOOKMARKLET_ORIGIN ||
|
||||
this.origin === ZOTERO_CONFIG.HTTP_BOOKMARKLET_ORIGIN) {
|
||||
response += "Access-Control-Allow-Origin: "+this.origin+"\r\n";
|
||||
response += "Access-Control-Allow-Origin: " + this.origin + "\r\n";
|
||||
response += "Access-Control-Allow-Methods: POST, GET, OPTIONS\r\n";
|
||||
response += "Access-Control-Allow-Headers: Content-Type,X-Zotero-Connector-API-Version,X-Zotero-Version\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
if(contentType) {
|
||||
response += "Content-Type: "+contentType+"\r\n";
|
||||
if (contentTypeOrHeaders) {
|
||||
if (typeof contentTypeOrHeaders == 'string') {
|
||||
contentTypeOrHeaders = {
|
||||
'Content-Type': contentTypeOrHeaders
|
||||
};
|
||||
}
|
||||
for (let header in contentTypeOrHeaders) {
|
||||
response += `${header}: ${contentTypeOrHeaders[header]}\r\n`;
|
||||
}
|
||||
}
|
||||
|
||||
if(body) {
|
||||
|
@ -418,10 +447,12 @@ Zotero.Server.DataListener.prototype._processEndpoint = Zotero.Promise.coroutine
|
|||
}
|
||||
|
||||
// set up response callback
|
||||
var me = this;
|
||||
var sendResponseCallback = function(code, contentType, arg) {
|
||||
me._requestFinished(me._generateResponse(code, contentType, arg));
|
||||
}
|
||||
var sendResponseCallback = function (code, contentTypeOrHeaders, arg, options) {
|
||||
this._requestFinished(
|
||||
this._generateResponse(code, contentTypeOrHeaders, arg),
|
||||
options
|
||||
);
|
||||
}.bind(this);
|
||||
|
||||
// Pass to endpoint
|
||||
//
|
||||
|
@ -491,7 +522,7 @@ Zotero.Server.DataListener.prototype._processEndpoint = Zotero.Promise.coroutine
|
|||
/*
|
||||
* returns HTTP data from a request
|
||||
*/
|
||||
Zotero.Server.DataListener.prototype._requestFinished = function(response) {
|
||||
Zotero.Server.DataListener.prototype._requestFinished = function (response, options) {
|
||||
if(this._responseSent) {
|
||||
Zotero.debug("Request already finished; not sending another response");
|
||||
return;
|
||||
|
@ -509,8 +540,19 @@ Zotero.Server.DataListener.prototype._requestFinished = function(response) {
|
|||
try {
|
||||
intlStream.init(this.oStream, "UTF-8", 1024, "?".charCodeAt(0));
|
||||
|
||||
// write response
|
||||
Zotero.debug(response, 5);
|
||||
// Filter logged response
|
||||
if (Zotero.Debug.enabled) {
|
||||
let maxLogLength = 2000;
|
||||
let str = response;
|
||||
if (options && options.logFilter) {
|
||||
str = options.logFilter(str);
|
||||
}
|
||||
if (str.length > maxLogLength) {
|
||||
str = str.substr(0, maxLogLength) + `\u2026 (${response.length} chars)`;
|
||||
}
|
||||
Zotero.debug(str, 5);
|
||||
}
|
||||
|
||||
intlStream.writeString(response);
|
||||
} finally {
|
||||
intlStream.close();
|
||||
|
|
|
@ -687,7 +687,7 @@ Zotero.Sync.Storage.Local = {
|
|||
|
||||
var filename = item.attachmentFilename;
|
||||
if (!filename) {
|
||||
throw new Error("Empty path for item " + item.key);
|
||||
Zotero.debug("Empty filename for item " + item.key, 2);
|
||||
}
|
||||
// Don't save Windows aliases
|
||||
if (filename.endsWith('.lnk')) {
|
||||
|
|
|
@ -820,7 +820,7 @@ Zotero.Sync.APIClient.prototype = {
|
|||
|
||||
_checkBackoff: function (xmlhttp) {
|
||||
var backoff = xmlhttp.getResponseHeader("Backoff");
|
||||
if (backoff && Number.isInteger(backoff)) {
|
||||
if (backoff && parseInt(backoff) == backoff) {
|
||||
// TODO: Update status?
|
||||
this.caller.pause(backoff * 1000);
|
||||
}
|
||||
|
@ -831,7 +831,7 @@ Zotero.Sync.APIClient.prototype = {
|
|||
var retryAfter = xmlhttp.getResponseHeader("Retry-After");
|
||||
var delay;
|
||||
if (!retryAfter) return false;
|
||||
if (!Number.isInteger(retryAfter)) {
|
||||
if (parseInt(retryAfter) != retryAfter) {
|
||||
Zotero.logError(`Invalid Retry-After delay ${retryAfter}`);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1691,22 +1691,14 @@ Zotero.Sync.Data.Engine.prototype._fullSync = Zotero.Promise.coroutine(function*
|
|||
for (let key in results.versions) {
|
||||
let version = results.versions[key];
|
||||
let obj = objectsClass.getByLibraryAndKey(this.libraryID, key);
|
||||
// If object already at latest version, skip
|
||||
// If object is already at or above latest version, skip. Local version can be
|
||||
// higher because, as explained in _uploadObjects(), we upload items in batches
|
||||
// and only get the last version to record in the database.
|
||||
let localVersion = localVersions[key];
|
||||
if (localVersion && localVersion === version) {
|
||||
if (localVersion && localVersion >= version) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// This should never happen
|
||||
if (localVersion > version) {
|
||||
Zotero.logError(`Local version of ${objectType} ${this.libraryID}/${key} `
|
||||
+ `is later than remote! (${localVersion} > ${version})`);
|
||||
// Delete cache version if it's there
|
||||
yield Zotero.Sync.Data.Local.deleteCacheObjectVersions(
|
||||
objectType, this.libraryID, key, localVersion, localVersion
|
||||
);
|
||||
}
|
||||
|
||||
if (obj) {
|
||||
Zotero.debug(`${ObjectType} ${obj.libraryKey} is older than remote version`);
|
||||
}
|
||||
|
|
|
@ -794,6 +794,7 @@ Zotero.Sync.Data.Local = {
|
|||
|
||||
// Skip objects with unmet dependencies
|
||||
if (objectType == 'item' || objectType == 'collection') {
|
||||
// Missing parent collection or item
|
||||
let parentProp = 'parent' + objectType[0].toUpperCase() + objectType.substr(1);
|
||||
let parentKey = jsonData[parentProp];
|
||||
if (parentKey) {
|
||||
|
@ -815,17 +816,37 @@ Zotero.Sync.Data.Local = {
|
|||
}
|
||||
}
|
||||
|
||||
/*if (objectType == 'item') {
|
||||
for (let j = 0; j < jsonData.collections.length; i++) {
|
||||
let parentKey = jsonData.collections[j];
|
||||
let parentCollection = Zotero.Collections.getByLibraryAndKey(
|
||||
libraryID, parentKey, { noCache: true }
|
||||
);
|
||||
if (!parentCollection) {
|
||||
// ???
|
||||
// Missing collection -- this could happen if the collection was deleted
|
||||
// locally and an item in it was modified remotely
|
||||
if (objectType == 'item' && jsonData.collections) {
|
||||
let error;
|
||||
for (let key of jsonData.collections) {
|
||||
let collection = Zotero.Collections.getByLibraryAndKey(libraryID, key);
|
||||
if (!collection) {
|
||||
error = new Error(`Collection ${libraryID}/${key} not found `
|
||||
+ `-- skipping item`);
|
||||
error.name = "ZoteroMissingObjectError";
|
||||
Zotero.debug(error.message);
|
||||
results.push({
|
||||
key: objectKey,
|
||||
processed: false,
|
||||
error,
|
||||
retry: false
|
||||
});
|
||||
|
||||
// If the collection is in the delete log, the deletion will upload
|
||||
// after downloads are done. Otherwise, we somehow missed
|
||||
// downloading it and should add it to the queue to try again.
|
||||
if (!(yield this.getDateDeleted('collection', libraryID, key))) {
|
||||
yield this.addObjectsToSyncQueue('collection', libraryID, [key]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
if (error) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Errors have to be thrown in order to roll back the transaction, so catch those here
|
||||
|
|
|
@ -69,6 +69,8 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
|||
|
||||
var _enabled = false;
|
||||
var _autoSyncTimer;
|
||||
var _delaySyncUntil;
|
||||
var _delayPromises = [];
|
||||
var _firstInSession = true;
|
||||
var _syncInProgress = false;
|
||||
var _stopping = false;
|
||||
|
@ -139,6 +141,29 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
|||
|
||||
this.updateIcons('animate');
|
||||
|
||||
// If a delay is set (e.g., from the connector target selector), wait to sync
|
||||
while (_delaySyncUntil && new Date() < _delaySyncUntil) {
|
||||
this.setSyncStatus(Zotero.getString('sync.status.waiting'));
|
||||
let delay = _delaySyncUntil - new Date();
|
||||
Zotero.debug(`Waiting ${delay} ms to sync`);
|
||||
yield Zotero.Promise.delay(delay);
|
||||
}
|
||||
|
||||
// If paused, wait until we're done
|
||||
while (true) {
|
||||
if (_delayPromises.some(p => p.isPending())) {
|
||||
this.setSyncStatus(Zotero.getString('sync.status.waiting'));
|
||||
Zotero.debug("Syncing is paused -- waiting to sync");
|
||||
yield Zotero.Promise.all(_delayPromises);
|
||||
// If more were added, continue
|
||||
if (_delayPromises.some(p => p.isPending())) {
|
||||
continue;
|
||||
}
|
||||
_delayPromises = [];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// purgeDataObjects() starts a transaction, so if there's an active one then show a
|
||||
// nice message and wait until there's not. Another transaction could still start
|
||||
// before purgeDataObjects() and result in a wait timeout, but this should reduce the
|
||||
|
@ -884,11 +909,20 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
|||
|
||||
// Implements nsITimerCallback
|
||||
var callback = {
|
||||
notify: function (timer) {
|
||||
notify: async function (timer) {
|
||||
if (!_getAPIKey()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If a delay is set (e.g., from the connector target selector), wait to sync.
|
||||
// We do this in sync() too for manual syncs, but no need to start spinning if
|
||||
// it's just an auto-sync.
|
||||
while (_delaySyncUntil && new Date() < _delaySyncUntil) {
|
||||
let delay = _delaySyncUntil - new Date();
|
||||
Zotero.debug(`Waiting ${delay} ms to start auto-sync`);
|
||||
await Zotero.Promise.delay(delay);
|
||||
}
|
||||
|
||||
if (Zotero.locked) {
|
||||
Zotero.debug('Zotero is locked -- skipping auto-sync', 4);
|
||||
return;
|
||||
|
@ -935,6 +969,26 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
|||
}
|
||||
|
||||
|
||||
this.delaySync = function (ms) {
|
||||
_delaySyncUntil = new Date(Date.now() + ms);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Delay syncs until the returned function is called
|
||||
*
|
||||
* @return {Function} - Resolve function
|
||||
*/
|
||||
this.delayIndefinite = function () {
|
||||
var resolve;
|
||||
var promise = new Zotero.Promise(function () {
|
||||
resolve = arguments[0];
|
||||
});
|
||||
_delayPromises.push(promise);
|
||||
return resolve;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Trigger updating of the main sync icon, the sync error icon, and
|
||||
* library-specific sync error icons across all windows
|
||||
|
|
|
@ -1299,6 +1299,7 @@ Zotero.Translate.Base.prototype = {
|
|||
}
|
||||
this._collections = options.collections;
|
||||
this._saveAttachments = options.saveAttachments === undefined || options.saveAttachments;
|
||||
this._forceTagType = options.forceTagType;
|
||||
this._saveOptions = options.saveOptions;
|
||||
|
||||
this._savingAttachments = [];
|
||||
|
@ -1342,11 +1343,15 @@ Zotero.Translate.Base.prototype = {
|
|||
Zotero.Promise.resolve(this.translator[0])
|
||||
.then(function (translator) {
|
||||
this.translator[0] = translator;
|
||||
this._loadTranslator(translator).then(() => this._translateTranslatorLoaded());
|
||||
this._loadTranslator(translator)
|
||||
.then(() => this._translateTranslatorLoaded())
|
||||
.catch(e => deferred.reject(e));
|
||||
}.bind(this));
|
||||
}
|
||||
else {
|
||||
this._loadTranslator(this.translator[0]).then(() => this._translateTranslatorLoaded());
|
||||
this._loadTranslator(this.translator[0])
|
||||
.then(() => this._translateTranslatorLoaded())
|
||||
.catch(e => deferred.reject(e));
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
|
@ -1881,14 +1886,17 @@ Zotero.Translate.Base.prototype = {
|
|||
*/
|
||||
_attr: function (selector, attr, index) {
|
||||
if (typeof arguments[0] == 'string') {
|
||||
var doc = this.document;
|
||||
var docOrElem = this.document;
|
||||
}
|
||||
// Support legacy polyfill signature
|
||||
// Document or element passed as first argument
|
||||
else {
|
||||
this._debug("WARNING: attr() no longer requires a document as the first argument");
|
||||
[doc, selector, attr, index] = arguments;
|
||||
// TODO: Warn if Document rather than Element is passed once we drop 4.0 translator
|
||||
// support
|
||||
[docOrElem, selector, attr, index] = arguments;
|
||||
}
|
||||
var elem = index ? doc.querySelectorAll(selector).item(index) : doc.querySelector(selector);
|
||||
var elem = index
|
||||
? docOrElem.querySelectorAll(selector).item(index)
|
||||
: docOrElem.querySelector(selector);
|
||||
return elem ? elem.getAttribute(attr) : null;
|
||||
},
|
||||
|
||||
|
@ -1897,14 +1905,17 @@ Zotero.Translate.Base.prototype = {
|
|||
*/
|
||||
_text: function (selector, index) {
|
||||
if (typeof arguments[0] == 'string') {
|
||||
var doc = this.document;
|
||||
var docOrElem = this.document;
|
||||
}
|
||||
// Support legacy polyfill signature
|
||||
// Document or element passed as first argument
|
||||
else {
|
||||
this._debug("WARNING: text() no longer requires a document as the first argument");
|
||||
[doc, selector, attr, index] = arguments;
|
||||
// TODO: Warn if Document rather than Element is passed once we drop 4.0 translator
|
||||
// support
|
||||
[docOrElem, selector, index] = arguments;
|
||||
}
|
||||
var elem = index ? doc.querySelectorAll(selector).item(index) : doc.querySelector(selector);
|
||||
var elem = index
|
||||
? docOrElem.querySelectorAll(selector).item(index)
|
||||
: docOrElem.querySelector(selector);
|
||||
return elem ? elem.textContent : null;
|
||||
},
|
||||
|
||||
|
@ -2368,6 +2379,7 @@ Zotero.Translate.Import.prototype._prepareTranslation = Zotero.Promise.method(fu
|
|||
this._itemSaver = new Zotero.Translate.ItemSaver({
|
||||
libraryID: this._libraryID,
|
||||
collections: this._collections,
|
||||
forceTagType: this._forceTagType,
|
||||
attachmentMode: Zotero.Translate.ItemSaver[(this._saveAttachments ? "ATTACHMENT_MODE_FILE" : "ATTACHMENT_MODE_IGNORE")],
|
||||
baseURI,
|
||||
saveOptions: Object.assign(
|
||||
|
@ -2624,6 +2636,12 @@ Zotero.Translate.Search.prototype.setIdentifier = function (identifier) {
|
|||
contextObject: "rft_id=info:pmid/" + identifier.PMID
|
||||
};
|
||||
}
|
||||
else if (identifier.arXiv) {
|
||||
search = {
|
||||
itemType: "journalArticle",
|
||||
arXiv: identifier.arXiv
|
||||
};
|
||||
}
|
||||
else {
|
||||
throw new Error("Unrecognized identifier");
|
||||
}
|
||||
|
|
|
@ -142,6 +142,7 @@ Zotero.Translate.ItemSaver.prototype = {
|
|||
for (let i=0; i<specialFields.notes.length; i++) {
|
||||
yield this._saveNote(specialFields.notes[i], myID);
|
||||
}
|
||||
item.notes = specialFields.notes;
|
||||
}
|
||||
|
||||
// handle attachments
|
||||
|
|
|
@ -299,6 +299,7 @@ Zotero.Utilities = {
|
|||
}
|
||||
|
||||
x = x.replace(/<br[^>]*>/gi, "\n");
|
||||
x = x.replace(/<\/p>/gi, "\n\n");
|
||||
return x.replace(/<[^>]+>/g, "");
|
||||
},
|
||||
|
||||
|
|
|
@ -896,15 +896,31 @@ Zotero.Utilities.Internal = {
|
|||
}
|
||||
}
|
||||
|
||||
// Next try arXiv
|
||||
if (!identifiers.length) {
|
||||
// arXiv identifiers are extracted without version number
|
||||
// i.e. 0706.0044v1 is extracted as 0706.0044,
|
||||
// because arXiv OAI API doesn't allow to access individual versions
|
||||
let arXiv_RE = /((?:[^A-Za-z]|^)([\-A-Za-z\.]+\/\d{7})(?:(v[0-9]+)|)(?!\d))|((?:\D|^)(\d{4}\.\d{4,5})(?:(v[0-9]+)|)(?!\d))/g;
|
||||
let m;
|
||||
while ((m = arXiv_RE.exec(text))) {
|
||||
let arXiv = m[2] || m[5];
|
||||
if (arXiv && !foundIDs.has(arXiv)) {
|
||||
identifiers.push({arXiv: arXiv});
|
||||
foundIDs.add(arXiv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally try for PMID
|
||||
if (!identifiers.length) {
|
||||
// PMID; right now, the longest PMIDs are 8 digits, so it doesn't seem like we'll
|
||||
// need to discriminate for a fairly long time
|
||||
let PMID_RE = /(?:\D|^)(\d{1,9})(?!\d)/g;
|
||||
let PMID_RE = /(^|\s|,|:)(\d{1,9})(?=\s|,|$)/g;
|
||||
let pmid;
|
||||
while ((pmid = PMID_RE.exec(text)) && !foundIDs.has(pmid)) {
|
||||
identifiers.push({
|
||||
PMID: pmid[1]
|
||||
PMID: pmid[2]
|
||||
});
|
||||
foundIDs.add(pmid);
|
||||
}
|
||||
|
@ -1644,6 +1660,18 @@ Zotero.Utilities.Internal.activate = new function() {
|
|||
}
|
||||
};
|
||||
|
||||
Zotero.Utilities.Internal.sendToBack = function() {
|
||||
if (Zotero.isMac) {
|
||||
Zotero.Utilities.Internal.executeAppleScript(`
|
||||
tell application "System Events"
|
||||
if frontmost of application id "org.zotero.zotero" then
|
||||
set visible of process "Zotero" to false
|
||||
end if
|
||||
end tell
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 encode / decode
|
||||
* From http://www.webtoolkit.info/
|
||||
|
|
|
@ -877,6 +877,9 @@ Services.scriptloader.loadSubScript("resource://zotero/polyfill.js");
|
|||
* Initializes the DB connection
|
||||
*/
|
||||
var _initDB = Zotero.Promise.coroutine(function* (haveReleasedLock) {
|
||||
// Initialize main database connection
|
||||
Zotero.DB = new Zotero.DBConnection('zotero');
|
||||
|
||||
try {
|
||||
// Test read access
|
||||
yield Zotero.DB.test();
|
||||
|
@ -2606,16 +2609,51 @@ Zotero.VersionHeader = {
|
|||
|
||||
observe: function (subject, topic, data) {
|
||||
try {
|
||||
var channel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);
|
||||
if (channel.URI.host.match(/zotero\.org$/)) {
|
||||
let channel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);
|
||||
let domain = channel.URI.host;
|
||||
if (domain.endsWith(ZOTERO_CONFIG.DOMAIN_NAME)) {
|
||||
channel.setRequestHeader("X-Zotero-Version", Zotero.version, false);
|
||||
}
|
||||
else {
|
||||
let ua = channel.getRequestHeader('User-Agent');
|
||||
ua = this.update(domain, ua);
|
||||
channel.setRequestHeader('User-Agent', ua, false);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.debug(e);
|
||||
Zotero.debug(e, 1);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Add Firefox/[version] to the default user agent and replace Zotero/[version] with
|
||||
* Zotero/[major.minor] (except for requests to zotero.org, where we include the full version)
|
||||
*
|
||||
* @param {String} domain
|
||||
* @param {String} ua - User Agent
|
||||
* @param {String} [testAppName] - App name to look for (necessary in tests, which are
|
||||
* currently run in Firefox)
|
||||
*/
|
||||
update: function (domain, ua, testAppName) {
|
||||
var info = Services.appinfo;
|
||||
var appName = testAppName || info.name;
|
||||
|
||||
var pos = ua.indexOf(appName + '/');
|
||||
|
||||
// Default UA
|
||||
if (pos != -1) {
|
||||
ua = ua.slice(0, pos) + `Firefox/${info.platformVersion.match(/^\d+/)[0]}.0 `
|
||||
+ appName + '/';
|
||||
}
|
||||
// Fake UA from connector
|
||||
else {
|
||||
ua += ' ' + appName + '/';
|
||||
}
|
||||
ua += Zotero.version.replace(/(\d+\.\d+).*/, '$1');
|
||||
|
||||
return ua;
|
||||
},
|
||||
|
||||
unregister: function () {
|
||||
Services.obs.removeObserver(Zotero.VersionHeader, "http-on-modify-request");
|
||||
}
|
||||
|
@ -2730,14 +2768,17 @@ Zotero.DragDrop = {
|
|||
Zotero.Browser = new function() {
|
||||
var nBrowsers = 0;
|
||||
|
||||
this.createHiddenBrowser = createHiddenBrowser;
|
||||
this.deleteHiddenBrowser = deleteHiddenBrowser;
|
||||
|
||||
function createHiddenBrowser(win) {
|
||||
if (!win) {
|
||||
var win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if(!win) {
|
||||
var win = Services.ww.activeWindow;
|
||||
this.createHiddenBrowser = function (win) {
|
||||
if (!win) {
|
||||
win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (!win) {
|
||||
win = Services.ww.activeWindow;
|
||||
}
|
||||
// Use the hidden DOM window on macOS with the main window closed
|
||||
if (!win) {
|
||||
let appShellService = Components.classes["@mozilla.org/appshell/appShellService;1"]
|
||||
.getService(Components.interfaces.nsIAppShellService);
|
||||
win = appShellService.hiddenDOMWindow;
|
||||
}
|
||||
if (!win) {
|
||||
throw new Error("Parent window not available for hidden browser");
|
||||
|
@ -2760,7 +2801,7 @@ Zotero.Browser = new function() {
|
|||
return hiddenBrowser;
|
||||
}
|
||||
|
||||
function deleteHiddenBrowser(myBrowsers) {
|
||||
this.deleteHiddenBrowser = function (myBrowsers) {
|
||||
if(!(myBrowsers instanceof Array)) myBrowsers = [myBrowsers];
|
||||
for(var i=0; i<myBrowsers.length; i++) {
|
||||
var myBrowser = myBrowsers[i];
|
||||
|
|
|
@ -260,6 +260,18 @@ var ZoteroPane = new function()
|
|||
ZoteroPane_Local.show();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// TEMP: Clean up extra files from Mendeley imports <5.0.51
|
||||
setTimeout(async function () {
|
||||
var needsCleanup = await Zotero.DB.valueQueryAsync(
|
||||
"SELECT COUNT(*) FROM settings WHERE setting='mImport' AND key='cleanup'"
|
||||
)
|
||||
if (!needsCleanup) return;
|
||||
|
||||
Components.utils.import("chrome://zotero/content/import/mendeley/mendeleyImport.js");
|
||||
var importer = new Zotero_Import_Mendeley();
|
||||
importer.deleteNonPrimaryFiles();
|
||||
}, 10000)
|
||||
}
|
||||
|
||||
|
||||
|
@ -2380,6 +2392,37 @@ var ZoteroPane = new function()
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show context menu once it's ready
|
||||
*/
|
||||
this.onCollectionsContextMenuOpen = async function (event) {
|
||||
await ZoteroPane.buildCollectionContextMenu();
|
||||
document.getElementById('zotero-collectionmenu').openPopup(
|
||||
null, null, event.clientX + 1, event.clientY + 1, true, false, event
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Show context menu once it's ready
|
||||
*/
|
||||
this.onItemsContextMenuOpen = async function (event) {
|
||||
await ZoteroPane.buildItemContextMenu()
|
||||
document.getElementById('zotero-itemmenu').openPopup(
|
||||
null, null, event.clientX + 1, event.clientY + 1, true, false, event
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
this.onCollectionContextMenuSelect = function (event) {
|
||||
event.stopPropagation();
|
||||
var o = _collectionContextMenuOptions.find(o => o.id == event.target.id)
|
||||
if (o.oncommand) {
|
||||
o.oncommand();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// menuitem configuration
|
||||
//
|
||||
// This has to be kept in sync with zotero-collectionmenu in zoteroPane.xul. We could do this
|
||||
|
@ -2498,7 +2541,7 @@ var ZoteroPane = new function()
|
|||
},
|
||||
];
|
||||
|
||||
this.buildCollectionContextMenu = function (noRepeat) {
|
||||
this.buildCollectionContextMenu = async function () {
|
||||
var libraryID = this.getSelectedLibraryID();
|
||||
var options = _collectionContextMenuOptions;
|
||||
|
||||
|
@ -2508,12 +2551,16 @@ var ZoteroPane = new function()
|
|||
return;
|
||||
}
|
||||
|
||||
// If the items view isn't initialized, this was a right-click on a different collection and
|
||||
// the new collection's items are still loading, so update the menu after loading. This causes
|
||||
// some menu items (e.g., export/createBib/loadReport) to appear gray in the menu at first and
|
||||
// then turn black once there are items. Pass a flag to prevent an accidental infinite loop.
|
||||
if (!collectionTreeRow.isHeader() && !this.itemsView.initialized && !noRepeat) {
|
||||
this.itemsView.onLoad.addListener(() => this.buildCollectionContextMenu(true));
|
||||
// If the items view isn't initialized, this was a right-click on a different collection
|
||||
// and the new collection's items are still loading, so continue menu after loading is
|
||||
// done. This causes some menu items (e.g., export/createBib/loadReport) to appear gray
|
||||
// in the menu at first and then turn black once there are items
|
||||
if (!collectionTreeRow.isHeader() && !this.itemsView.initialized) {
|
||||
await new Promise((resolve) => {
|
||||
this.itemsView.onLoad.addListener(() => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Set attributes on the menu from the configuration object
|
||||
|
@ -2554,7 +2601,12 @@ var ZoteroPane = new function()
|
|||
];
|
||||
|
||||
if (!this.itemsView.rowCount) {
|
||||
disable = ['exportCollection', 'createBibCollection', 'loadReport'];
|
||||
disable = ['createBibCollection', 'loadReport'];
|
||||
|
||||
// If no items in subcollections either, disable export
|
||||
if (!(await collectionTreeRow.ref.getDescendents(false, 'item', false).length)) {
|
||||
disable.push('exportCollection');
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust labels
|
||||
|
@ -2693,25 +2745,6 @@ var ZoteroPane = new function()
|
|||
}
|
||||
};
|
||||
|
||||
this.onCollectionContextMenuSelect = function (event) {
|
||||
event.stopPropagation();
|
||||
var o = _collectionContextMenuOptions.find(o => o.id == event.target.id)
|
||||
if (o.oncommand) {
|
||||
o.oncommand();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Show context menu once it's ready
|
||||
*/
|
||||
this.onItemsContextMenuOpen = function (event) {
|
||||
ZoteroPane.buildItemContextMenu()
|
||||
.then(function () {
|
||||
document.getElementById('zotero-itemmenu').openPopup(
|
||||
null, null, event.clientX + 1, event.clientY + 1, true, false, event
|
||||
);
|
||||
})
|
||||
};
|
||||
|
||||
this.buildItemContextMenu = Zotero.Promise.coroutine(function* () {
|
||||
var options = [
|
||||
|
@ -4579,27 +4612,27 @@ var ZoteroPane = new function()
|
|||
|
||||
|
||||
this.reportMetadataForSelected = async function () {
|
||||
var success = false;
|
||||
var items = ZoteroPane.getSelectedItems();
|
||||
for (let item of items) {
|
||||
try {
|
||||
await Zotero.RecognizePDF.report(item);
|
||||
// If at least one report was submitted, show as success
|
||||
success = true;
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
}
|
||||
}
|
||||
let items = ZoteroPane.getSelectedItems();
|
||||
if(!items.length) return;
|
||||
|
||||
if (success) {
|
||||
let input = {value: ''};
|
||||
Services.prompt.prompt(
|
||||
null,
|
||||
Zotero.getString('recognizePDF.reportMetadata'),
|
||||
Zotero.getString('general.describeProblem'),
|
||||
input, null, {}
|
||||
);
|
||||
|
||||
try {
|
||||
await Zotero.RecognizePDF.report(items[0], input.value);
|
||||
Zotero.alert(
|
||||
window,
|
||||
Zotero.getString('general.submitted'),
|
||||
Zotero.getString('general.thanksForHelpingImprove', Zotero.clientName)
|
||||
);
|
||||
}
|
||||
else {
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
Zotero.alert(
|
||||
window,
|
||||
Zotero.getString('general.error'),
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
|
||||
<commandset id="mainCommandSet">
|
||||
<command id="cmd_zotero_reportErrors" oncommand="ZoteroPane_Local.reportErrors();"/>
|
||||
<command id="cmd_zotero_import" oncommand="Zotero_File_Interface.importFile();"/>
|
||||
<command id="cmd_zotero_import" oncommand="Zotero_File_Interface.showImportWizard();"/>
|
||||
<command id="cmd_zotero_importFromClipboard" oncommand="Zotero_File_Interface.importFromClipboard();"/>
|
||||
<command id="cmd_zotero_exportLibrary" oncommand="Zotero_File_Interface.exportFile();"/>
|
||||
<command id="cmd_zotero_advancedSearch" oncommand="ZoteroPane_Local.openAdvancedSearchWindow();"/>
|
||||
|
@ -273,7 +273,6 @@
|
|||
|
||||
<popupset>
|
||||
<menupopup id="zotero-collectionmenu"
|
||||
onpopupshowing="ZoteroPane_Local.buildCollectionContextMenu();"
|
||||
oncommand="ZoteroPane.onCollectionContextMenuSelect(event)">
|
||||
<!-- Keep order in sync with buildCollectionContextMenu, which adds additional attributes -->
|
||||
<menuitem class="zotero-menuitem-sync"/>
|
||||
|
@ -344,10 +343,12 @@
|
|||
<box id="zotero-collections-tree-shim"/>
|
||||
<!-- This extra vbox prevents the toolbar from getting compressed when resizing
|
||||
the tag selector to max height -->
|
||||
<tree id="zotero-collections-tree" hidecolumnpicker="true" context="zotero-collectionmenu"
|
||||
onmouseover="ZoteroPane_Local.collectionsView.setHighlightedRows();"
|
||||
onselect="ZoteroPane_Local.onCollectionSelected();"
|
||||
seltype="cell" flex="1" editable="true">
|
||||
<tree id="zotero-collections-tree"
|
||||
hidecolumnpicker="true"
|
||||
oncontextmenu="ZoteroPane.onCollectionsContextMenuOpen(event)"
|
||||
onmouseover="ZoteroPane_Local.collectionsView.setHighlightedRows();"
|
||||
onselect="ZoteroPane_Local.onCollectionSelected();"
|
||||
seltype="cell" flex="1" editable="true">
|
||||
<treecols>
|
||||
<treecol
|
||||
id="zotero-collections-name-column"
|
||||
|
|
|
@ -10,19 +10,21 @@
|
|||
<!ENTITY zotero.general.cancel "Cancel">
|
||||
<!ENTITY zotero.general.refresh "Refresh">
|
||||
<!ENTITY zotero.general.saveAs "Save As…">
|
||||
<!ENTITY zotero.general.options "Options">
|
||||
<!ENTITY zotero.general.advancedOptions.label "Advanced Options">
|
||||
<!ENTITY zotero.general.tools "Tools">
|
||||
<!ENTITY zotero.general.more "More">
|
||||
<!ENTITY zotero.general.loading "Loading…">
|
||||
<!ENTITY zotero.general.close "Close">
|
||||
<!ENTITY zotero.general.minimize "Minimize">
|
||||
<!ENTITY zotero.general.other "Other…">
|
||||
|
||||
|
||||
<!ENTITY zotero.errorReport.title "Zotero Error Report">
|
||||
<!ENTITY zotero.errorReport.submissionInProgress "Wag asseblief terwyl die foutboodskap voorgelê word">
|
||||
<!ENTITY zotero.errorReport.submitted "Die foutboodskap is voorgelê">
|
||||
<!ENTITY zotero.errorReport.reportID "Verslag-ID:">
|
||||
<!ENTITY zotero.errorReport.postToForums "Pos asseblief 'n boodskap aan die Zotero-forum (forums.zoter.org) met hierdie Verslag-ID, 'n beskrywing van die probleem en enige stappe wat nodig is om dit te reproduseer.">
|
||||
<!ENTITY zotero.errorReport.postToForums "Pos asseblief 'n boodskap aan die Zotero-forum (forums.zotero.org) met hierdie Verslag-ID, 'n beskrywing van die probleem en enige stappe wat nodig is om dit te reproduseer.">
|
||||
<!ENTITY zotero.errorReport.notReviewed "Error reports are generally not reviewed unless referred to in the forums.">
|
||||
|
||||
<!ENTITY zotero.upgrade.title "Zotero Upgrade Wizard">
|
||||
|
@ -175,7 +177,7 @@
|
|||
<!ENTITY zotero.tagColorChooser.setColor "Set Color">
|
||||
<!ENTITY zotero.tagColorChooser.removeColor "Remove Color">
|
||||
|
||||
<!ENTITY zotero.lookup.description "Enter one or more ISBNs, DOIs, or PMIDs to look up in the box below.">
|
||||
<!ENTITY zotero.lookup.description "Enter ISBNs, DOIs, PMIDs, or arXiv IDs to add to your library:">
|
||||
<!ENTITY zotero.lookup.button.search "Search">
|
||||
|
||||
<!ENTITY zotero.selectitems.title "Select Items">
|
||||
|
@ -202,6 +204,15 @@
|
|||
|
||||
<!ENTITY zotero.progress.title "Vordering">
|
||||
|
||||
<!ENTITY zotero.import "Import">
|
||||
<!ENTITY zotero.import.whereToImportFrom "Where do you want to import from?">
|
||||
<!ENTITY zotero.import.source.file "A file (BibTeX, RIS, Zotero RDF, etc.)">
|
||||
<!ENTITY zotero.import.importing "Importing…">
|
||||
<!ENTITY zotero.import.database "Database">
|
||||
<!ENTITY zotero.import.lastModified "Last Modified">
|
||||
<!ENTITY zotero.import.size "Size">
|
||||
<!ENTITY zotero.import.createCollection "Place imported collections and items into new collection">
|
||||
|
||||
<!ENTITY zotero.exportOptions.title "Export...">
|
||||
<!ENTITY zotero.exportOptions.format.label "Formaat:">
|
||||
<!ENTITY zotero.exportOptions.translatorOptions.label "Vertalerskeuses">
|
||||
|
|
|
@ -67,6 +67,8 @@ general.clear=Clear
|
|||
general.processing=Processing
|
||||
general.submitted=Submitted
|
||||
general.thanksForHelpingImprove=Thanks for helping to improve %S!
|
||||
general.describeProblem=Briefly describe the problem:
|
||||
general.nMegabytes=%S MB
|
||||
|
||||
general.operationInProgress=A Zotero operation is currently in progress.
|
||||
general.operationInProgress.waitUntilFinished=Please wait until it has finished.
|
||||
|
@ -609,7 +611,7 @@ db.integrityCheck.errorsFixed=The errors in your Zotero database have been corre
|
|||
db.integrityCheck.errorsNotFixed=Zotero was unable to correct all the errors in your database.
|
||||
db.integrityCheck.reportInForums=You can report this problem in the Zotero Forums.
|
||||
|
||||
zotero.preferences.chooseFileHandler=Choose File Handler
|
||||
zotero.preferences.chooseApplication=Choose Application
|
||||
|
||||
zotero.preferences.update.updated=Updated
|
||||
zotero.preferences.update.upToDate=Up to date
|
||||
|
@ -686,10 +688,13 @@ fileInterface.importComplete=Import Complete
|
|||
fileInterface.itemsWereImported=%1$S item was imported;%1$S items were imported
|
||||
fileInterface.itemsExported=Exporting items...
|
||||
fileInterface.import=Import
|
||||
fileInterface.chooseAppDatabaseToImport=Choose the %S database to import
|
||||
fileInterface.export=Export
|
||||
fileInterface.exportedItems=Exported Items
|
||||
fileInterface.imported=Imported
|
||||
fileInterface.unsupportedFormat=The selected file is not in a supported format.
|
||||
fileInterface.appDatabase=%S Database
|
||||
fileInterface.appImportCollection=%S Import
|
||||
fileInterface.viewSupportedFormats=View Supported Formats…
|
||||
fileInterface.untitledBibliography=Untitled Bibliography
|
||||
fileInterface.bibliographyHTMLTitle=Bibliography
|
||||
|
@ -860,10 +865,14 @@ integration.corruptBibliography.description=All items cited in the text will app
|
|||
integration.citationChanged=You have modified this citation since Zotero generated it. Do you want to keep your modifications and prevent future updates?
|
||||
integration.citationChanged.description=Clicking "Yes" will prevent Zotero from updating this citation if you add additional citations, switch styles, or modify the item to which it refers. Clicking "No" will erase your changes.
|
||||
integration.citationChanged.edit=You have modified this citation since Zotero generated it. Editing will clear your modifications. Do you want to continue?
|
||||
integration.citationChanged.original=Original: %S
|
||||
integration.citationChanged.modified=Modified: %S
|
||||
integration.delayCitationUpdates.alert.text1=Updating citations in this document is taking a long time. Would you like to disable automatic citation updates?
|
||||
integration.delayCitationUpdates.alert.text2=You will need to click Refresh in the Zotero plugin when you are done inserting citations.
|
||||
integration.delayCitationUpdates.alert.text2.toolbar=You will need to click Refresh in the Zotero toolbar when you are done inserting citations.
|
||||
integration.delayCitationUpdates.alert.text2.tab=You will need to click Refresh in the Zotero tab when you are done inserting citations.
|
||||
integration.delayCitationUpdates.alert.text3=You can change this setting later in the document preferences.
|
||||
integration.delayCitationUpdates.bibliography=Automatic citation updates are disabled. To see the bibliography, click Refresh in the Zotero plugin.
|
||||
integration.delayCitationUpdates.bibliography.toolbar=Automatic citation updates are disabled. To see the bibliography, click Refresh in the Zotero toolbar.
|
||||
integration.delayCitationUpdates.bibliography.tab=Automatic citation updates are disabled. To see the bibliography, click Refresh in the Zotero tab.
|
||||
|
||||
styles.install.title=Install Style
|
||||
styles.install.unexpectedError=An unexpected error occurred while installing "%1$S"
|
||||
|
@ -1060,6 +1069,7 @@ recognizePDF.fileNotFound=File not found
|
|||
recognizePDF.error=An unexpected error occurred
|
||||
recognizePDF.recognizing.label=Retrieving Metadata…
|
||||
recognizePDF.complete.label=Metadata Retrieval Complete
|
||||
recognizePDF.reportMetadata=Report Incorrect Metadata
|
||||
|
||||
rtfScan.openTitle=Select a file to scan
|
||||
rtfScan.scanning.label=Scanning RTF Document...
|
||||
|
@ -1068,6 +1078,7 @@ rtfScan.rtf=Rich Text Format (.rtf)
|
|||
rtfScan.saveTitle=Select a location in which to save the formatted file
|
||||
rtfScan.scannedFileSuffix=(Scanned)
|
||||
|
||||
extractedAnnotations=Extracted Annotations
|
||||
|
||||
file.accessError.theFileCannotBeCreated=The file '%S' cannot be created.
|
||||
file.accessError.theFileCannotBeUpdated=The file '%S' cannot be updated.
|
||||
|
|
62
chrome/locale/ar/zotero/connector.json
Normal file
62
chrome/locale/ar/zotero/connector.json
Normal file
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"error_connection_enableSavingToOnlineLibrary": {
|
||||
"message": "تمكين الحفظ إلى مكتبة الشبكة"
|
||||
},
|
||||
"error_connection_save": {
|
||||
"message": " $1 غير قادر على التواصل مع $2 تطبيق سطح المكتب. الموصل يمكن أن يحفظ بعض الصفحات مباشرة إلى حسابك في $3، لكن لنتائجٍ أفضل تأكد أن $2 مفتوح قبل محاولة الحفظ."
|
||||
},
|
||||
"progressWindow_error_translation": {
|
||||
"message": "حدث خطأ أثناء حفظ هذا العنصر. راجع $1 لمزيد من المعلومات."
|
||||
},
|
||||
"general_tryAgain": {
|
||||
"message": "حاول مرة أخرى"
|
||||
},
|
||||
"error_connection_isAppRunning": {
|
||||
"message": "هل $1 يعمل؟"
|
||||
},
|
||||
"progressWindow_tagPlaceholder": {
|
||||
"message": "وسوم ( مفصولة بفواصل)"
|
||||
},
|
||||
"general_more": {
|
||||
"message": "المزيد..."
|
||||
},
|
||||
"progressWindow_error_upgradeClient": {
|
||||
"message": "هذه الميزة ليست مدعومة في إصدارتك من $1. رجاءاً حدث إلى $2"
|
||||
},
|
||||
"general_cancel": {
|
||||
"message": "إلغاء"
|
||||
},
|
||||
"progressWindow_error_upgradeClient_latestVersion": {
|
||||
"message": "الإصدار الأخير"
|
||||
},
|
||||
"progressWindow_error_troubleshootingTranslatorIssues": {
|
||||
"message": "اكتشاف واصلاح مشاكل المترجمات"
|
||||
},
|
||||
"error_connection_downloadOrTroubleshoot": {
|
||||
"message": "يمكنك <a href=\"$1\">تحميل$2</a> أو <a href=\"$3\">استصلاح الموصل</a> إذا لزم الأمر."
|
||||
},
|
||||
"general_done": {
|
||||
"message": "تم"
|
||||
},
|
||||
"progressWindow_error_fallback": {
|
||||
"message": "حدث خطأ اثناء الحفظ باستخدام $1. محاولة الحفظ باستخدام $2 بدلاً من ذلك."
|
||||
},
|
||||
"upgradeApp": {
|
||||
"message": "ترقية $1"
|
||||
},
|
||||
"integration_error_clientUpgrade": {
|
||||
"message": "الاستشهاد المستند على الشبكة يتطلب $1 أو أحدث."
|
||||
},
|
||||
"general_saveTo": {
|
||||
"message": "احفظ في $1"
|
||||
},
|
||||
"appConnector": {
|
||||
"message": "موصل $1"
|
||||
},
|
||||
"progressWindow_savingTo": {
|
||||
"message": "الحفظ في"
|
||||
},
|
||||
"integration_error_connection": {
|
||||
"message": " $1 غير قادر على التواصل مع $2 تطبيق سطح المكتب. يجب فتح $2 لاستخدام الاستشهاد المستند على الشبكة."
|
||||
}
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
<!ENTITY styles.editor "محرر انماط الاستشهاد لزوتيرو">
|
||||
<!ENTITY styles.editor "محرر انماط زوتيرو">
|
||||
|
||||
<!ENTITY styles.editor.citePosition "مكان الاستشهاد:">
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<!ENTITY zotero.preferences.items "عناصر">
|
||||
<!ENTITY zotero.preferences.period ".">
|
||||
<!ENTITY zotero.preferences.settings "اعدادات">
|
||||
<!ENTITY zotero.preferences.custom "Custom…">
|
||||
<!ENTITY zotero.preferences.custom "مخصص...">
|
||||
|
||||
<!ENTITY zotero.preferences.prefpane.general "عام">
|
||||
|
||||
|
@ -19,13 +19,13 @@
|
|||
<!ENTITY zotero.preferences.fontSize.xlarge "كبير جدا">
|
||||
<!ENTITY zotero.preferences.fontSize.notes "حجم خط الملاحظات:">
|
||||
|
||||
<!ENTITY zotero.preferences.fileHandling "File Handling">
|
||||
<!ENTITY zotero.preferences.fileHandling "معاملة الملف">
|
||||
<!ENTITY zotero.preferences.automaticSnapshots "أخذ لقطات تلقائية عند إنشاء العناصر من صفحات الويب">
|
||||
<!ENTITY zotero.preferences.downloadAssociatedFiles "الارفاق التلقائي لملفات PDF والملفات الاخرى عند اضافة عنصر جديد من صفحة ويب">
|
||||
<!ENTITY zotero.preferences.autoRecognizeFiles "Automatically retrieve metadata for PDFs">
|
||||
<!ENTITY zotero.preferences.autoRenameFiles "Automatically rename attachment files using parent metadata">
|
||||
<!ENTITY zotero.preferences.fileHandler.openPDFsUsing "Open PDFs using">
|
||||
<!ENTITY zotero.preferences.fileHandler.systemDefault "System Default">
|
||||
<!ENTITY zotero.preferences.autoRecognizeFiles "استرداد البيانات الوصفية تلقائياً لملفات بي دي إف">
|
||||
<!ENTITY zotero.preferences.autoRenameFiles "إعادة تسمية الملفات المرفقة تلقائيًا مستخدماً بيانات الأصل الوصفية">
|
||||
<!ENTITY zotero.preferences.fileHandler.openPDFsUsing "افتح ملفات بي دي إف مستخدماً">
|
||||
<!ENTITY zotero.preferences.fileHandler.systemDefault "افتراضي النظام">
|
||||
|
||||
<!ENTITY zotero.preferences.miscellaneous "أخرى">
|
||||
<!ENTITY zotero.preferences.autoUpdate "التأكد التلقائي من وجود مترجمات مواقع محدثة">
|
||||
|
@ -75,7 +75,7 @@
|
|||
<!ENTITY zotero.preferences.sync.fileSyncing.download "تحميل الملفات">
|
||||
<!ENTITY zotero.preferences.sync.fileSyncing.download.atSyncTime "في وقت التزامن">
|
||||
<!ENTITY zotero.preferences.sync.fileSyncing.download.onDemand "عند الحاجة">
|
||||
<!ENTITY zotero.preferences.sync.fileSyncing.verifyServer "Verify Server">
|
||||
<!ENTITY zotero.preferences.sync.fileSyncing.verifyServer "تحقق من الخادم">
|
||||
<!ENTITY zotero.preferences.sync.fileSyncing.tos1 "باستخدامك مساحة تخزين زوتيرو، فإنك توافق على الالتزام بكافة">
|
||||
<!ENTITY zotero.preferences.sync.fileSyncing.tos2 "الشروط والأحكام">
|
||||
<!ENTITY zotero.preferences.sync.librariesToSync "المكتبات المراد تزامنها">
|
||||
|
@ -87,12 +87,12 @@
|
|||
<!ENTITY zotero.preferences.sync.reset.warning3 "لمزيد من المعلومات.">
|
||||
<!ENTITY zotero.preferences.sync.reset.resetDataSyncHistory "إعادة تعيين سجل مزامنة البيانات">
|
||||
<!ENTITY zotero.preferences.sync.reset.resetDataSyncHistory.desc "دمج البيانات المحلية مع البيانات البعيدة، متجاهلاً سجل المزامنة">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreFromServer "Restore from Online Library">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreFromServer.desc "Overwrite local Zotero data with data from the online library.">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreToServer "Restore to Online Library">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreToServer.desc "Overwrite online library with local Zotero data">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreFromServer "الاستعاد من مكتبة الشبكة">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreFromServer.desc "استبدل بيانات زوتيرو المحلية ببيانات مكتبة الشبكة.">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreToServer "استعادة إلى مكتبة الشبكة">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreToServer.desc "استبدل مكتبة الشبكة ببيانات زوتيرو المحلية">
|
||||
<!ENTITY zotero.preferences.sync.reset.resetFileSyncHistory "إعادة تعيين ملف سجل المزامنة">
|
||||
<!ENTITY zotero.preferences.sync.reset.resetFileSyncHistory.desc "Compare all attachment files with the storage service">
|
||||
<!ENTITY zotero.preferences.sync.reset.resetFileSyncHistory.desc "مقارنة جميع الملفات المرفقة مع خدمة التخزين">
|
||||
<!ENTITY zotero.preferences.sync.reset "اعادة ضبط">
|
||||
<!ENTITY zotero.preferences.sync.reset.button "إعادة التعيين...">
|
||||
|
||||
|
@ -197,7 +197,7 @@
|
|||
<!ENTITY zotero.preferences.dataDir.useProfile "استخدم مجلد الملف الشخصي لفايرفوكس">
|
||||
<!ENTITY zotero.preferences.dataDir.custom "تخصيص:">
|
||||
<!ENTITY zotero.preferences.dataDir.choose "اختر...">
|
||||
<!ENTITY zotero.preferences.dataDir.viaCommandLine "(specified via command line)">
|
||||
<!ENTITY zotero.preferences.dataDir.viaCommandLine "(محدد عبر سطر الأوامر)">
|
||||
<!ENTITY zotero.preferences.dataDir.reveal "عرض مجلد البيانات">
|
||||
<!ENTITY zotero.preferences.dataDir.migrate "الترحيل إلى الموقع الافتراضي الجديد...">
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<!ENTITY zotero.search.name "الإسم:">
|
||||
<!ENTITY zotero.search.name "الاسم">
|
||||
|
||||
<!ENTITY zotero.search.searchInLibrary "البحث في المكتبة:">
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<!ENTITY preferencesCmdMac.label "تفضيلات ...">
|
||||
<!ENTITY preferencesCmdMac.label "التفضيلات...">
|
||||
<!ENTITY preferencesCmdMac.commandkey ",">
|
||||
<!ENTITY servicesMenuMac.label "خدمات">
|
||||
<!ENTITY hideThisAppCmdMac.label "إخفاء اسم العلامة التجارية القصيرة؛">
|
||||
|
|
|
@ -10,12 +10,14 @@
|
|||
<!ENTITY zotero.general.cancel "الغاء">
|
||||
<!ENTITY zotero.general.refresh "اعادة تحميل">
|
||||
<!ENTITY zotero.general.saveAs "حفظ باسم...">
|
||||
<!ENTITY zotero.general.options "Options">
|
||||
<!ENTITY zotero.general.advancedOptions.label "خيارات متقدمة">
|
||||
<!ENTITY zotero.general.tools "أدوات">
|
||||
<!ENTITY zotero.general.more "المزيد">
|
||||
<!ENTITY zotero.general.loading "التحميل">
|
||||
<!ENTITY zotero.general.close "Close">
|
||||
<!ENTITY zotero.general.minimize "Minimize">
|
||||
<!ENTITY zotero.general.close "أغلق">
|
||||
<!ENTITY zotero.general.minimize "تصغير">
|
||||
<!ENTITY zotero.general.other "Other…">
|
||||
|
||||
|
||||
<!ENTITY zotero.errorReport.title "تقرير خطأ لزوتيرو">
|
||||
|
@ -99,8 +101,8 @@
|
|||
<!ENTITY zotero.items.menu.restoreToLibrary "استعادة المكتبة">
|
||||
<!ENTITY zotero.items.menu.duplicateItem "تكرار العنصر المحدد">
|
||||
<!ENTITY zotero.items.menu.mergeItems "دمج العناصر...">
|
||||
<!ENTITY zotero.items.menu.unrecognize "Undo Retrieve Metadata">
|
||||
<!ENTITY zotero.items.menu.reportMetadata "Report Incorrect Metadata">
|
||||
<!ENTITY zotero.items.menu.unrecognize "تراجع عن استيراد البيانات الوصفية">
|
||||
<!ENTITY zotero.items.menu.reportMetadata "بلغ عن بيانات وصفية غير صحيحة">
|
||||
|
||||
<!ENTITY zotero.duplicatesMerge.versionSelect "اختار اصدارة العنصر المناسبة لاستخدامه كعنصر رئيسي:">
|
||||
<!ENTITY zotero.duplicatesMerge.fieldSelect "اختار الحقول المراد الابقاء عليها من الاصدارات الاخرى للعنصر:">
|
||||
|
@ -175,7 +177,7 @@
|
|||
<!ENTITY zotero.tagColorChooser.setColor "ضبط اللون">
|
||||
<!ENTITY zotero.tagColorChooser.removeColor "ازالة اللون">
|
||||
|
||||
<!ENTITY zotero.lookup.description "ادخل الترقيم الدولي الموحد للكتاب او معرف مقالات الميدلاين او معرف الوثيقة الرقمية.">
|
||||
<!ENTITY zotero.lookup.description "أدخل ردمك أو معرف الوثيقة الرقمي أو بميد أو معرف الأرشيف للإضافة لممكتبتك">
|
||||
<!ENTITY zotero.lookup.button.search "بحث">
|
||||
|
||||
<!ENTITY zotero.selectitems.title "تحديد العناصر">
|
||||
|
@ -202,6 +204,15 @@
|
|||
|
||||
<!ENTITY zotero.progress.title "التقدم">
|
||||
|
||||
<!ENTITY zotero.import "Import">
|
||||
<!ENTITY zotero.import.whereToImportFrom "Where do you want to import from?">
|
||||
<!ENTITY zotero.import.source.file "A file (BibTeX, RIS, Zotero RDF, etc.)">
|
||||
<!ENTITY zotero.import.importing "Importing…">
|
||||
<!ENTITY zotero.import.database "Database">
|
||||
<!ENTITY zotero.import.lastModified "Last Modified">
|
||||
<!ENTITY zotero.import.size "Size">
|
||||
<!ENTITY zotero.import.createCollection "Place imported collections and items into new collection">
|
||||
|
||||
<!ENTITY zotero.exportOptions.title "تصدير...">
|
||||
<!ENTITY zotero.exportOptions.format.label "الصياغة:">
|
||||
<!ENTITY zotero.exportOptions.translatorOptions.label "خيارات المترجم">
|
||||
|
@ -236,9 +247,9 @@
|
|||
<!ENTITY zotero.integration.prefs.bookmarks.label "الاشارات المرجعية">
|
||||
<!ENTITY zotero.integration.prefs.bookmarks.caption "Bookmarks can be shared between Word and LibreOffice, but may cause errors if accidentally modified and cannot be inserted into footnotes.">
|
||||
|
||||
<!ENTITY zotero.integration.prefs.automaticCitationUpdates.label "Automatically update citations">
|
||||
<!ENTITY zotero.integration.prefs.automaticCitationUpdates.tooltip "Citations with pending updates will be highlighted in the document">
|
||||
<!ENTITY zotero.integration.prefs.automaticCitationUpdates.description "Disabling updates can speed up citation insertion in large documents. Click Refresh to update citations manually.">
|
||||
<!ENTITY zotero.integration.prefs.automaticCitationUpdates.label "حدث الاستشهادات آلياً">
|
||||
<!ENTITY zotero.integration.prefs.automaticCitationUpdates.tooltip "سيتم تمييز الاستشهادات ذات التحديثات المعلقة في المستند">
|
||||
<!ENTITY zotero.integration.prefs.automaticCitationUpdates.description "يمكن أن يؤدي تعطيل التحديثات إلى تسريع إدراج الاستشهادات في المستندات الكبيرة. انقر على تحديث لتحديث الاستشهادات يدويًا.">
|
||||
|
||||
|
||||
<!ENTITY zotero.integration.prefs.automaticJournalAbbeviations.label "استخدام اختصارات دورية ميدلين">
|
||||
|
|
|
@ -62,11 +62,13 @@ general.tryLater=Try Later
|
|||
general.showDirectory=Show Directory
|
||||
general.continue=استمر
|
||||
general.copyToClipboard=انسخ إلى الحافظة
|
||||
general.cancel=Cancel
|
||||
general.clear=Clear
|
||||
general.processing=Processing
|
||||
general.submitted=Submitted
|
||||
general.thanksForHelpingImprove=Thanks for helping to improve %S!
|
||||
general.cancel=إلغاء
|
||||
general.clear=تفريغ
|
||||
general.processing=معالجة
|
||||
general.submitted=أُرسل
|
||||
general.thanksForHelpingImprove=شكرًا لمساعدتك في تحسين %S!
|
||||
general.describeProblem=صف المشكلة باختصار:
|
||||
general.nMegabytes=%S MB
|
||||
|
||||
general.operationInProgress=عملية زوتيرو حالياً تحت الإجراء.
|
||||
general.operationInProgress.waitUntilFinished=يرجى الانتظار حتى انتهائها.
|
||||
|
@ -609,7 +611,7 @@ db.integrityCheck.errorsFixed=تم تصحيح الاخطاء بقاعدة بيا
|
|||
db.integrityCheck.errorsNotFixed=لم يستطيع زوتيرو اصلاح جميع الاخطاء بقاعدة بياناتك.
|
||||
db.integrityCheck.reportInForums=يمكنك الابلاغ عن هذه المشكلة في مدونات زوتيرو.
|
||||
|
||||
zotero.preferences.chooseFileHandler=Choose File Handler
|
||||
zotero.preferences.chooseApplication=اختر التطبيق
|
||||
|
||||
zotero.preferences.update.updated=محدَّث
|
||||
zotero.preferences.update.upToDate=حديث
|
||||
|
@ -686,10 +688,13 @@ fileInterface.importComplete=Import Complete
|
|||
fileInterface.itemsWereImported=%1$S item was imported;%1$S items were imported
|
||||
fileInterface.itemsExported=تصدير العناصر...
|
||||
fileInterface.import=استيراد
|
||||
fileInterface.chooseAppDatabaseToImport=Choose the %S database to import
|
||||
fileInterface.export=تصدير
|
||||
fileInterface.exportedItems=العناصر المصدَّرة
|
||||
fileInterface.imported=مستورد
|
||||
fileInterface.unsupportedFormat=The selected file is not in a supported format.
|
||||
fileInterface.appDatabase=%S Database
|
||||
fileInterface.appImportCollection=%S Import
|
||||
fileInterface.viewSupportedFormats=View Supported Formats…
|
||||
fileInterface.untitledBibliography=قائمة مراجع بدون عنوان
|
||||
fileInterface.bibliographyHTMLTitle=قائمة المراجع
|
||||
|
@ -860,10 +865,14 @@ integration.corruptBibliography.description=ستظهر جميع العناصر
|
|||
integration.citationChanged=قمت بتعديل هذا الاقتباس منذ أنشئه Zotero. هل تريد أن تبقي التعديلات ومن التحديثات المستقبلية؟
|
||||
integration.citationChanged.description=بالنقر على "نعم" سيمنع Zotero من تحديث هذا الاقتباس إذا أضفت استشهادات إضافية، وأساليب تبديل، أو تعديل المرجع الذي يشير إليه. النقر على "لا" سيمحو التغييرات.
|
||||
integration.citationChanged.edit=قمت بتعديل هذا الاقتباس منذ أنشئه Zotero. التحرير سيمسح التعديلات. هل ترغب في الاستمرار؟
|
||||
integration.citationChanged.original=الأصل: %S
|
||||
integration.citationChanged.modified=معدل: %S
|
||||
integration.delayCitationUpdates.alert.text1=Updating citations in this document is taking a long time. Would you like to disable automatic citation updates?
|
||||
integration.delayCitationUpdates.alert.text2=You will need to click Refresh in the Zotero plugin when you are done inserting citations.
|
||||
integration.delayCitationUpdates.alert.text2.toolbar=ستحتاج إلى نقر تحديث في شريط أدوات زوتيرو عند الانتهاء من إدراج الاستشهادات.
|
||||
integration.delayCitationUpdates.alert.text2.tab=ستحتاج إلى النقر فوق تحديث في علامة التبويب زوتيرو عندما تنتهي من إدراج الاستشهادات.
|
||||
integration.delayCitationUpdates.alert.text3=You can change this setting later in the document preferences.
|
||||
integration.delayCitationUpdates.bibliography=Automatic citation updates are disabled. To see the bibliography, click Refresh in the Zotero plugin.
|
||||
integration.delayCitationUpdates.bibliography.toolbar=تحديثات الاقتباس التلقائية معطلة. لمشاهدة قائمة المراجع، انقر فوق تحديث في شريط أدوات زوتيرو.
|
||||
integration.delayCitationUpdates.bibliography.tab=تحديثات الاستشهادات التلقائية معطلة. لرؤية قائمة المراجع، انقر تحديث في علامة التبويب زوتيرو.
|
||||
|
||||
styles.install.title=ثبت نمط
|
||||
styles.install.unexpectedError=An unexpected error occurred while installing "%1$S"
|
||||
|
@ -1058,8 +1067,9 @@ recognizePDF.couldNotRead=تعذر قراءة النص في ملف PDF
|
|||
recognizePDF.noMatches=لم يتم العثور على مراجع مطابقة.
|
||||
recognizePDF.fileNotFound=لم يتم العثور على الملف.
|
||||
recognizePDF.error=حدث خطأ غير متوقع
|
||||
recognizePDF.recognizing.label=Retrieving Metadata…
|
||||
recognizePDF.recognizing.label=جارٍ استرداد البيانات الوصفية ...
|
||||
recognizePDF.complete.label=تمت عملية استرجاع البيانات الوصفية.
|
||||
recognizePDF.reportMetadata=بلغ عن بيانات وصفية غير صحيحة
|
||||
|
||||
rtfScan.openTitle=حدد ملف لفحصه
|
||||
rtfScan.scanning.label=جاري فحص مستند RTF...
|
||||
|
@ -1068,6 +1078,7 @@ rtfScan.rtf=تنسيق النص الغني (.rtf)
|
|||
rtfScan.saveTitle=حدد موقعا لحفظ الملف المصاغ
|
||||
rtfScan.scannedFileSuffix=(تم فحصه)
|
||||
|
||||
extractedAnnotations=Extracted Annotations
|
||||
|
||||
file.accessError.theFileCannotBeCreated=The file '%S' cannot be created.
|
||||
file.accessError.theFileCannotBeUpdated=The file '%S' cannot be updated.
|
||||
|
|
|
@ -10,12 +10,14 @@
|
|||
<!ENTITY zotero.general.cancel "Cancel">
|
||||
<!ENTITY zotero.general.refresh "Refresh">
|
||||
<!ENTITY zotero.general.saveAs "Save As…">
|
||||
<!ENTITY zotero.general.options "Options">
|
||||
<!ENTITY zotero.general.advancedOptions.label "Advanced Options">
|
||||
<!ENTITY zotero.general.tools "Tools">
|
||||
<!ENTITY zotero.general.more "More">
|
||||
<!ENTITY zotero.general.loading "Loading…">
|
||||
<!ENTITY zotero.general.close "Close">
|
||||
<!ENTITY zotero.general.minimize "Minimize">
|
||||
<!ENTITY zotero.general.other "Other…">
|
||||
|
||||
|
||||
<!ENTITY zotero.errorReport.title "Zotero Error Report">
|
||||
|
@ -175,7 +177,7 @@
|
|||
<!ENTITY zotero.tagColorChooser.setColor "Set Color">
|
||||
<!ENTITY zotero.tagColorChooser.removeColor "Remove Color">
|
||||
|
||||
<!ENTITY zotero.lookup.description "Въведете ISBN, DOI, или PMID за търсене в полето по-долу.">
|
||||
<!ENTITY zotero.lookup.description "Enter ISBNs, DOIs, PMIDs, or arXiv IDs to add to your library:">
|
||||
<!ENTITY zotero.lookup.button.search "Search">
|
||||
|
||||
<!ENTITY zotero.selectitems.title "Избрани записи">
|
||||
|
@ -202,6 +204,15 @@
|
|||
|
||||
<!ENTITY zotero.progress.title "Напредък">
|
||||
|
||||
<!ENTITY zotero.import "Import">
|
||||
<!ENTITY zotero.import.whereToImportFrom "Where do you want to import from?">
|
||||
<!ENTITY zotero.import.source.file "A file (BibTeX, RIS, Zotero RDF, etc.)">
|
||||
<!ENTITY zotero.import.importing "Importing…">
|
||||
<!ENTITY zotero.import.database "Database">
|
||||
<!ENTITY zotero.import.lastModified "Last Modified">
|
||||
<!ENTITY zotero.import.size "Size">
|
||||
<!ENTITY zotero.import.createCollection "Place imported collections and items into new collection">
|
||||
|
||||
<!ENTITY zotero.exportOptions.title "Износ...">
|
||||
<!ENTITY zotero.exportOptions.format.label "Формат:">
|
||||
<!ENTITY zotero.exportOptions.translatorOptions.label "Параметри на преводачите">
|
||||
|
|
|
@ -67,6 +67,8 @@ general.clear=Clear
|
|||
general.processing=Processing
|
||||
general.submitted=Submitted
|
||||
general.thanksForHelpingImprove=Thanks for helping to improve %S!
|
||||
general.describeProblem=Briefly describe the problem:
|
||||
general.nMegabytes=%S MB
|
||||
|
||||
general.operationInProgress=Операция на Зотеро е активна в момента.
|
||||
general.operationInProgress.waitUntilFinished=Моля изчакайте докато приключи.
|
||||
|
@ -609,7 +611,7 @@ db.integrityCheck.errorsFixed=The errors in your Zotero database have been corre
|
|||
db.integrityCheck.errorsNotFixed=Zotero was unable to correct all the errors in your database.
|
||||
db.integrityCheck.reportInForums=You can report this problem in the Zotero Forums.
|
||||
|
||||
zotero.preferences.chooseFileHandler=Choose File Handler
|
||||
zotero.preferences.chooseApplication=Choose Application
|
||||
|
||||
zotero.preferences.update.updated=Осъвременен
|
||||
zotero.preferences.update.upToDate=Актуален
|
||||
|
@ -686,10 +688,13 @@ fileInterface.importComplete=Import Complete
|
|||
fileInterface.itemsWereImported=%1$S item was imported;%1$S items were imported
|
||||
fileInterface.itemsExported=Износ на записи...
|
||||
fileInterface.import=Внос
|
||||
fileInterface.chooseAppDatabaseToImport=Choose the %S database to import
|
||||
fileInterface.export=Износ
|
||||
fileInterface.exportedItems=Изнесени записи
|
||||
fileInterface.imported=Внесени
|
||||
fileInterface.unsupportedFormat=The selected file is not in a supported format.
|
||||
fileInterface.appDatabase=%S Database
|
||||
fileInterface.appImportCollection=%S Import
|
||||
fileInterface.viewSupportedFormats=View Supported Formats…
|
||||
fileInterface.untitledBibliography=Библиография без име
|
||||
fileInterface.bibliographyHTMLTitle=Библиография
|
||||
|
@ -860,10 +865,14 @@ integration.corruptBibliography.description=Всички записи цитир
|
|||
integration.citationChanged=You have modified this citation since Zotero generated it. Do you want to keep your modifications and prevent future updates?
|
||||
integration.citationChanged.description=Clicking "Yes" will prevent Zotero from updating this citation if you add additional citations, switch styles, or modify the item to which it refers. Clicking "No" will erase your changes.
|
||||
integration.citationChanged.edit=You have modified this citation since Zotero generated it. Editing will clear your modifications. Do you want to continue?
|
||||
integration.citationChanged.original=Original: %S
|
||||
integration.citationChanged.modified=Modified: %S
|
||||
integration.delayCitationUpdates.alert.text1=Updating citations in this document is taking a long time. Would you like to disable automatic citation updates?
|
||||
integration.delayCitationUpdates.alert.text2=You will need to click Refresh in the Zotero plugin when you are done inserting citations.
|
||||
integration.delayCitationUpdates.alert.text2.toolbar=You will need to click Refresh in the Zotero toolbar when you are done inserting citations.
|
||||
integration.delayCitationUpdates.alert.text2.tab=You will need to click Refresh in the Zotero tab when you are done inserting citations.
|
||||
integration.delayCitationUpdates.alert.text3=You can change this setting later in the document preferences.
|
||||
integration.delayCitationUpdates.bibliography=Automatic citation updates are disabled. To see the bibliography, click Refresh in the Zotero plugin.
|
||||
integration.delayCitationUpdates.bibliography.toolbar=Automatic citation updates are disabled. To see the bibliography, click Refresh in the Zotero toolbar.
|
||||
integration.delayCitationUpdates.bibliography.tab=Automatic citation updates are disabled. To see the bibliography, click Refresh in the Zotero tab.
|
||||
|
||||
styles.install.title=Install Style
|
||||
styles.install.unexpectedError=An unexpected error occurred while installing "%1$S"
|
||||
|
@ -1060,6 +1069,7 @@ recognizePDF.fileNotFound=File not found
|
|||
recognizePDF.error=An unexpected error occurred
|
||||
recognizePDF.recognizing.label=Retrieving Metadata…
|
||||
recognizePDF.complete.label=Metadata Retrieval Complete
|
||||
recognizePDF.reportMetadata=Report Incorrect Metadata
|
||||
|
||||
rtfScan.openTitle=Изберете файл, който да бъде сканиран
|
||||
rtfScan.scanning.label=Сканира RTF документ...
|
||||
|
@ -1068,6 +1078,7 @@ rtfScan.rtf=Богат текстов формат (.rtf)
|
|||
rtfScan.saveTitle=Изберете къде да бъде записан форматираният файл
|
||||
rtfScan.scannedFileSuffix=(Сканиран)
|
||||
|
||||
extractedAnnotations=Extracted Annotations
|
||||
|
||||
file.accessError.theFileCannotBeCreated=The file '%S' cannot be created.
|
||||
file.accessError.theFileCannotBeUpdated=The file '%S' cannot be updated.
|
||||
|
|
|
@ -10,12 +10,14 @@
|
|||
<!ENTITY zotero.general.cancel "Cancel·la">
|
||||
<!ENTITY zotero.general.refresh "Refresca">
|
||||
<!ENTITY zotero.general.saveAs "Desa com...">
|
||||
<!ENTITY zotero.general.options "Options">
|
||||
<!ENTITY zotero.general.advancedOptions.label "Advanced Options">
|
||||
<!ENTITY zotero.general.tools "Tools">
|
||||
<!ENTITY zotero.general.more "More">
|
||||
<!ENTITY zotero.general.loading "Loading…">
|
||||
<!ENTITY zotero.general.close "Close">
|
||||
<!ENTITY zotero.general.minimize "Minimize">
|
||||
<!ENTITY zotero.general.other "Other…">
|
||||
|
||||
|
||||
<!ENTITY zotero.errorReport.title "Informe d'error de Zotero">
|
||||
|
@ -175,7 +177,7 @@
|
|||
<!ENTITY zotero.tagColorChooser.setColor "Estableix el color">
|
||||
<!ENTITY zotero.tagColorChooser.removeColor "Elimina el color">
|
||||
|
||||
<!ENTITY zotero.lookup.description "Introduïu l'ISBN, DOI, o PMID per cercar en el quadre inferior.">
|
||||
<!ENTITY zotero.lookup.description "Enter ISBNs, DOIs, PMIDs, or arXiv IDs to add to your library:">
|
||||
<!ENTITY zotero.lookup.button.search "Cerca">
|
||||
|
||||
<!ENTITY zotero.selectitems.title "Selecciona elements">
|
||||
|
@ -202,6 +204,15 @@
|
|||
|
||||
<!ENTITY zotero.progress.title "Progrés">
|
||||
|
||||
<!ENTITY zotero.import "Import">
|
||||
<!ENTITY zotero.import.whereToImportFrom "Where do you want to import from?">
|
||||
<!ENTITY zotero.import.source.file "A file (BibTeX, RIS, Zotero RDF, etc.)">
|
||||
<!ENTITY zotero.import.importing "Importing…">
|
||||
<!ENTITY zotero.import.database "Database">
|
||||
<!ENTITY zotero.import.lastModified "Last Modified">
|
||||
<!ENTITY zotero.import.size "Size">
|
||||
<!ENTITY zotero.import.createCollection "Place imported collections and items into new collection">
|
||||
|
||||
<!ENTITY zotero.exportOptions.title "Exporta...">
|
||||
<!ENTITY zotero.exportOptions.format.label "Format:">
|
||||
<!ENTITY zotero.exportOptions.translatorOptions.label "Opcions del traductor">
|
||||
|
|
|
@ -67,6 +67,8 @@ general.clear=Clear
|
|||
general.processing=Processing
|
||||
general.submitted=Submitted
|
||||
general.thanksForHelpingImprove=Thanks for helping to improve %S!
|
||||
general.describeProblem=Briefly describe the problem:
|
||||
general.nMegabytes=%S MB
|
||||
|
||||
general.operationInProgress=Una operació del Zotero està actualment en curs.
|
||||
general.operationInProgress.waitUntilFinished=Espereu fins que hagi acabat.
|
||||
|
@ -609,7 +611,7 @@ db.integrityCheck.errorsFixed=S'han corregit els errors de la vostra base de dad
|
|||
db.integrityCheck.errorsNotFixed=El Zotero no pot corregir tots els errors de la vostra base de dades.
|
||||
db.integrityCheck.reportInForums=Podeu informar d'aquest problema als Fòrums del Zotero.
|
||||
|
||||
zotero.preferences.chooseFileHandler=Choose File Handler
|
||||
zotero.preferences.chooseApplication=Choose Application
|
||||
|
||||
zotero.preferences.update.updated=Actualitzat
|
||||
zotero.preferences.update.upToDate=Actual
|
||||
|
@ -686,10 +688,13 @@ fileInterface.importComplete=Import Complete
|
|||
fileInterface.itemsWereImported=%1$S item was imported;%1$S items were imported
|
||||
fileInterface.itemsExported=Exportant elements...
|
||||
fileInterface.import=Importa
|
||||
fileInterface.chooseAppDatabaseToImport=Choose the %S database to import
|
||||
fileInterface.export=Exporta
|
||||
fileInterface.exportedItems=Elements exportats
|
||||
fileInterface.imported=Importat
|
||||
fileInterface.unsupportedFormat=El fitxer seleccionat té un format no admès.
|
||||
fileInterface.appDatabase=%S Database
|
||||
fileInterface.appImportCollection=%S Import
|
||||
fileInterface.viewSupportedFormats=Mostra els formats compatibles...
|
||||
fileInterface.untitledBibliography=Bibliografia sense títol
|
||||
fileInterface.bibliographyHTMLTitle=Bibliografia
|
||||
|
@ -860,10 +865,14 @@ integration.corruptBibliography.description=Tots els elements citats al text apa
|
|||
integration.citationChanged=Heu modificat aquesta cita des que Zotero la va generar. Voleu mantenir les modificacions i prevenir futures actualitzacions?
|
||||
integration.citationChanged.description=En feu clic a "Sí" evitareu que el Zotero actualitzi aquesta cita si n'afegiu més de cites, commuteu els estils, o modifiqueu la referència a què es refereix. Si feu clic a "No", s'eliminaran els canvis.
|
||||
integration.citationChanged.edit=Heu modificat aquesta cita des que el Zotero la generà. Si l'editeu netejareu les modificacions. Voleu continuar?
|
||||
integration.citationChanged.original=Original: %S
|
||||
integration.citationChanged.modified=Modified: %S
|
||||
integration.delayCitationUpdates.alert.text1=Updating citations in this document is taking a long time. Would you like to disable automatic citation updates?
|
||||
integration.delayCitationUpdates.alert.text2=You will need to click Refresh in the Zotero plugin when you are done inserting citations.
|
||||
integration.delayCitationUpdates.alert.text2.toolbar=You will need to click Refresh in the Zotero toolbar when you are done inserting citations.
|
||||
integration.delayCitationUpdates.alert.text2.tab=You will need to click Refresh in the Zotero tab when you are done inserting citations.
|
||||
integration.delayCitationUpdates.alert.text3=You can change this setting later in the document preferences.
|
||||
integration.delayCitationUpdates.bibliography=Automatic citation updates are disabled. To see the bibliography, click Refresh in the Zotero plugin.
|
||||
integration.delayCitationUpdates.bibliography.toolbar=Automatic citation updates are disabled. To see the bibliography, click Refresh in the Zotero toolbar.
|
||||
integration.delayCitationUpdates.bibliography.tab=Automatic citation updates are disabled. To see the bibliography, click Refresh in the Zotero tab.
|
||||
|
||||
styles.install.title=Instal·la l'estil
|
||||
styles.install.unexpectedError=S'ha produït un error inesperat durant la instal·lació de "%1$S"
|
||||
|
@ -1060,6 +1069,7 @@ recognizePDF.fileNotFound=No s'ha trobat el fitxer
|
|||
recognizePDF.error=S'ha produït un error inesperat
|
||||
recognizePDF.recognizing.label=Retrieving Metadata…
|
||||
recognizePDF.complete.label=Recuperació de metadades completa
|
||||
recognizePDF.reportMetadata=Report Incorrect Metadata
|
||||
|
||||
rtfScan.openTitle=Selecciona un fitxer per escanejar
|
||||
rtfScan.scanning.label=Scanning RTF Document...
|
||||
|
@ -1068,6 +1078,7 @@ rtfScan.rtf=Format de text enriquit (.rtf)
|
|||
rtfScan.saveTitle=Selecciona una ubicació per desar el fitxer formatat
|
||||
rtfScan.scannedFileSuffix=(Escanejat)
|
||||
|
||||
extractedAnnotations=Extracted Annotations
|
||||
|
||||
file.accessError.theFileCannotBeCreated=The file '%S' cannot be created.
|
||||
file.accessError.theFileCannotBeUpdated=The file '%S' cannot be updated.
|
||||
|
|
|
@ -10,12 +10,14 @@
|
|||
<!ENTITY zotero.general.cancel "Zrušit">
|
||||
<!ENTITY zotero.general.refresh "Obnovit">
|
||||
<!ENTITY zotero.general.saveAs "Uložit jako...">
|
||||
<!ENTITY zotero.general.options "Options">
|
||||
<!ENTITY zotero.general.advancedOptions.label "Pokročilé možnosti">
|
||||
<!ENTITY zotero.general.tools "Nástroje">
|
||||
<!ENTITY zotero.general.more "Více">
|
||||
<!ENTITY zotero.general.loading "Nahrává se...">
|
||||
<!ENTITY zotero.general.close "Close">
|
||||
<!ENTITY zotero.general.minimize "Minimize">
|
||||
<!ENTITY zotero.general.other "Other…">
|
||||
|
||||
|
||||
<!ENTITY zotero.errorReport.title "Chybová zpráva Zotera">
|
||||
|
@ -175,7 +177,7 @@
|
|||
<!ENTITY zotero.tagColorChooser.setColor "Nastavit barvu">
|
||||
<!ENTITY zotero.tagColorChooser.removeColor "Odstranit barvu">
|
||||
|
||||
<!ENTITY zotero.lookup.description "Do políčka níže vložte ISBN, DOI nebo PMID, podle kterého chcete vyhledávat.">
|
||||
<!ENTITY zotero.lookup.description "Enter ISBNs, DOIs, PMIDs, or arXiv IDs to add to your library:">
|
||||
<!ENTITY zotero.lookup.button.search "Hledat">
|
||||
|
||||
<!ENTITY zotero.selectitems.title "Označit položky">
|
||||
|
@ -202,6 +204,15 @@
|
|||
|
||||
<!ENTITY zotero.progress.title "Postup">
|
||||
|
||||
<!ENTITY zotero.import "Import">
|
||||
<!ENTITY zotero.import.whereToImportFrom "Where do you want to import from?">
|
||||
<!ENTITY zotero.import.source.file "A file (BibTeX, RIS, Zotero RDF, etc.)">
|
||||
<!ENTITY zotero.import.importing "Importing…">
|
||||
<!ENTITY zotero.import.database "Database">
|
||||
<!ENTITY zotero.import.lastModified "Last Modified">
|
||||
<!ENTITY zotero.import.size "Size">
|
||||
<!ENTITY zotero.import.createCollection "Place imported collections and items into new collection">
|
||||
|
||||
<!ENTITY zotero.exportOptions.title "Export...">
|
||||
<!ENTITY zotero.exportOptions.format.label "Formát:">
|
||||
<!ENTITY zotero.exportOptions.translatorOptions.label "Možnosti překladače">
|
||||
|
|
|
@ -67,6 +67,8 @@ general.clear=Clear
|
|||
general.processing=Processing
|
||||
general.submitted=Submitted
|
||||
general.thanksForHelpingImprove=Thanks for helping to improve %S!
|
||||
general.describeProblem=Briefly describe the problem:
|
||||
general.nMegabytes=%S MB
|
||||
|
||||
general.operationInProgress=Právě probíhá operace se Zoterem.
|
||||
general.operationInProgress.waitUntilFinished=Počkejte prosím, dokud neskončí.
|
||||
|
@ -609,7 +611,7 @@ db.integrityCheck.errorsFixed=Chyby ve vaší Zotero databázi byly opraveny.
|
|||
db.integrityCheck.errorsNotFixed=Zoteru se nepodařilo opravit všechny chyby ve vaší databázi.
|
||||
db.integrityCheck.reportInForums=Můžete nahlásit problém na fórech Zotero
|
||||
|
||||
zotero.preferences.chooseFileHandler=Choose File Handler
|
||||
zotero.preferences.chooseApplication=Choose Application
|
||||
|
||||
zotero.preferences.update.updated=Aktualizováno
|
||||
zotero.preferences.update.upToDate=Aktuální
|
||||
|
@ -686,10 +688,13 @@ fileInterface.importComplete=Import dokončen
|
|||
fileInterface.itemsWereImported=%1$S položka byla převedena;%1$S položky byly převedeny;%1$S položek bylo převedeno
|
||||
fileInterface.itemsExported=Exportování položek...
|
||||
fileInterface.import=Import
|
||||
fileInterface.chooseAppDatabaseToImport=Choose the %S database to import
|
||||
fileInterface.export=Export
|
||||
fileInterface.exportedItems=Exportované položky
|
||||
fileInterface.imported=Importované
|
||||
fileInterface.unsupportedFormat=Vybraný soubor nemá podporovaný formát.
|
||||
fileInterface.appDatabase=%S Database
|
||||
fileInterface.appImportCollection=%S Import
|
||||
fileInterface.viewSupportedFormats=Zobrazit podporované formáty...
|
||||
fileInterface.untitledBibliography=Nepojmenovaná bibliografie
|
||||
fileInterface.bibliographyHTMLTitle=Bibliografie
|
||||
|
@ -860,10 +865,14 @@ integration.corruptBibliography.description=Všechny položky v textu se objeví
|
|||
integration.citationChanged=Citace byla upravena poté, co ji Zotero nagenerovalo. Přejete si zachovat vaše úpravy a zabránit budoucím aktualizacím?
|
||||
integration.citationChanged.description=Kliknutím na "Ano" zabráníte Zoteru aktualizovat tuto citaci když přidáte další citace, přepnete styly, nebo upravíte odkaz, na které odkazuje. Kliknutím na "Ne" smažete vaše změny.
|
||||
integration.citationChanged.edit=Citace byla po vygenerování Zoterem ručně upravena. Editování smaže vaše úpravy. Přejete si pokračovat?
|
||||
integration.citationChanged.original=Original: %S
|
||||
integration.citationChanged.modified=Modified: %S
|
||||
integration.delayCitationUpdates.alert.text1=Updating citations in this document is taking a long time. Would you like to disable automatic citation updates?
|
||||
integration.delayCitationUpdates.alert.text2=You will need to click Refresh in the Zotero plugin when you are done inserting citations.
|
||||
integration.delayCitationUpdates.alert.text2.toolbar=You will need to click Refresh in the Zotero toolbar when you are done inserting citations.
|
||||
integration.delayCitationUpdates.alert.text2.tab=You will need to click Refresh in the Zotero tab when you are done inserting citations.
|
||||
integration.delayCitationUpdates.alert.text3=You can change this setting later in the document preferences.
|
||||
integration.delayCitationUpdates.bibliography=Automatic citation updates are disabled. To see the bibliography, click Refresh in the Zotero plugin.
|
||||
integration.delayCitationUpdates.bibliography.toolbar=Automatic citation updates are disabled. To see the bibliography, click Refresh in the Zotero toolbar.
|
||||
integration.delayCitationUpdates.bibliography.tab=Automatic citation updates are disabled. To see the bibliography, click Refresh in the Zotero tab.
|
||||
|
||||
styles.install.title=instalovat Styl
|
||||
styles.install.unexpectedError=Při instalaci "%1$S" došlo k neočekávané chybě
|
||||
|
@ -1060,6 +1069,7 @@ recognizePDF.fileNotFound=Soubor nenalezen
|
|||
recognizePDF.error=Došlo k neočekávané chybě
|
||||
recognizePDF.recognizing.label=Retrieving Metadata…
|
||||
recognizePDF.complete.label=Získání metadat dokončeno
|
||||
recognizePDF.reportMetadata=Report Incorrect Metadata
|
||||
|
||||
rtfScan.openTitle=Vyberte soubor k prohledání
|
||||
rtfScan.scanning.label=Prohledávám RTF dokument...
|
||||
|
@ -1068,6 +1078,7 @@ rtfScan.rtf=Rich Text Format (.rtf)
|
|||
rtfScan.saveTitle=Vyberte umístění, do kterého bude uložen naformátovaný soubor
|
||||
rtfScan.scannedFileSuffix=(Prohledán)
|
||||
|
||||
extractedAnnotations=Extracted Annotations
|
||||
|
||||
file.accessError.theFileCannotBeCreated=Soubor '%S' nemůže být vytvořen.
|
||||
file.accessError.theFileCannotBeUpdated=Soubor '%S' nemůže být aktualizován.
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<!ENTITY zotero.preferences.items "Elementer">
|
||||
<!ENTITY zotero.preferences.period ".">
|
||||
<!ENTITY zotero.preferences.settings "Indstillinger">
|
||||
<!ENTITY zotero.preferences.custom "Custom…">
|
||||
<!ENTITY zotero.preferences.custom "Tilpasset...">
|
||||
|
||||
<!ENTITY zotero.preferences.prefpane.general "Generelt">
|
||||
|
||||
|
@ -19,13 +19,13 @@
|
|||
<!ENTITY zotero.preferences.fontSize.xlarge "Ekstra stor">
|
||||
<!ENTITY zotero.preferences.fontSize.notes "Skriftstørrelse i noter:">
|
||||
|
||||
<!ENTITY zotero.preferences.fileHandling "File Handling">
|
||||
<!ENTITY zotero.preferences.fileHandling "Filhåndtering">
|
||||
<!ENTITY zotero.preferences.automaticSnapshots "Tag automatisk et "Snapshot" når der oprettes Elementer for web-sider">
|
||||
<!ENTITY zotero.preferences.downloadAssociatedFiles "Vedhæft automatisk tilhørende PDF-filer og andre filer når Elementer gemmes">
|
||||
<!ENTITY zotero.preferences.autoRecognizeFiles "Automatically retrieve metadata for PDFs">
|
||||
<!ENTITY zotero.preferences.autoRenameFiles "Automatically rename attachment files using parent metadata">
|
||||
<!ENTITY zotero.preferences.fileHandler.openPDFsUsing "Open PDFs using">
|
||||
<!ENTITY zotero.preferences.fileHandler.systemDefault "System Default">
|
||||
<!ENTITY zotero.preferences.autoRecognizeFiles "Hent automatisk metadata i PDF'er">
|
||||
<!ENTITY zotero.preferences.autoRenameFiles "Omdøb automatisk vedhæftede filer ved hjælp af metadata">
|
||||
<!ENTITY zotero.preferences.fileHandler.openPDFsUsing "Åbn PDF'er med brug af">
|
||||
<!ENTITY zotero.preferences.fileHandler.systemDefault "Systemstandard">
|
||||
|
||||
<!ENTITY zotero.preferences.miscellaneous "Forskelligt">
|
||||
<!ENTITY zotero.preferences.autoUpdate "Kontroller automatisk for opdaterede oversættere og bibliografiske formater">
|
||||
|
@ -87,12 +87,12 @@
|
|||
<!ENTITY zotero.preferences.sync.reset.warning3 "for mere information">
|
||||
<!ENTITY zotero.preferences.sync.reset.resetDataSyncHistory "Nulstil historikken for datasynkronisering">
|
||||
<!ENTITY zotero.preferences.sync.reset.resetDataSyncHistory.desc "Flet lokale data med fjerndata og ignorér synkroniseringshistorikken">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreFromServer "Restore from Online Library">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreFromServer.desc "Overwrite local Zotero data with data from the online library.">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreToServer "Restore to Online Library">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreToServer.desc "Overwrite online library with local Zotero data">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreFromServer "Gendan fra onlinebibliotek">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreFromServer.desc "Overskriv lokale Zotero-data med data fra onlinebiblioteket.">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreToServer "Gendan til onlinebibliotek">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreToServer.desc "Overskriv onlinebiblioteket med lokale Zotero-data.">
|
||||
<!ENTITY zotero.preferences.sync.reset.resetFileSyncHistory "Nulstil historik for fil-synkronisering">
|
||||
<!ENTITY zotero.preferences.sync.reset.resetFileSyncHistory.desc "Compare all attachment files with the storage service">
|
||||
<!ENTITY zotero.preferences.sync.reset.resetFileSyncHistory.desc "Sammenlign alle vedhæftede filer med lagringstjenesten">
|
||||
<!ENTITY zotero.preferences.sync.reset "Nulstil">
|
||||
<!ENTITY zotero.preferences.sync.reset.button "Nulstil...">
|
||||
|
||||
|
|
|
@ -10,12 +10,14 @@
|
|||
<!ENTITY zotero.general.cancel "Annullér">
|
||||
<!ENTITY zotero.general.refresh "Opdatér">
|
||||
<!ENTITY zotero.general.saveAs "Gem som...">
|
||||
<!ENTITY zotero.general.options "Options">
|
||||
<!ENTITY zotero.general.advancedOptions.label "Avancerede indstillinger">
|
||||
<!ENTITY zotero.general.tools "Tools">
|
||||
<!ENTITY zotero.general.more "Mere">
|
||||
<!ENTITY zotero.general.loading "Indlæser...">
|
||||
<!ENTITY zotero.general.close "Close">
|
||||
<!ENTITY zotero.general.minimize "Minimize">
|
||||
<!ENTITY zotero.general.close "Luk">
|
||||
<!ENTITY zotero.general.minimize "Minimér">
|
||||
<!ENTITY zotero.general.other "Other…">
|
||||
|
||||
|
||||
<!ENTITY zotero.errorReport.title "Zotero fejlrapport">
|
||||
|
@ -99,8 +101,8 @@
|
|||
<!ENTITY zotero.items.menu.restoreToLibrary "Genindlæs i bibliotek">
|
||||
<!ENTITY zotero.items.menu.duplicateItem "Duplikér valgte element">
|
||||
<!ENTITY zotero.items.menu.mergeItems "Flet elementer...">
|
||||
<!ENTITY zotero.items.menu.unrecognize "Undo Retrieve Metadata">
|
||||
<!ENTITY zotero.items.menu.reportMetadata "Report Incorrect Metadata">
|
||||
<!ENTITY zotero.items.menu.unrecognize "Fortryd indhentning af metadata">
|
||||
<!ENTITY zotero.items.menu.reportMetadata "Rapportér forkerte metadata">
|
||||
|
||||
<!ENTITY zotero.duplicatesMerge.versionSelect "Vælg den version af elementet som du vil bruge som hovedelement:">
|
||||
<!ENTITY zotero.duplicatesMerge.fieldSelect "Vælg felter som skal bevares fra andre versioner af elementet">
|
||||
|
@ -175,7 +177,7 @@
|
|||
<!ENTITY zotero.tagColorChooser.setColor "Sæt farve:">
|
||||
<!ENTITY zotero.tagColorChooser.removeColor "Fjern farve">
|
||||
|
||||
<!ENTITY zotero.lookup.description "Indtast ISBN, DOI eller PMID for opslag nedenfor.">
|
||||
<!ENTITY zotero.lookup.description "Enter ISBNs, DOIs, PMIDs, or arXiv IDs to add to your library:">
|
||||
<!ENTITY zotero.lookup.button.search "Søg">
|
||||
|
||||
<!ENTITY zotero.selectitems.title "Vælg elementer">
|
||||
|
@ -202,6 +204,15 @@
|
|||
|
||||
<!ENTITY zotero.progress.title "Fremdrift">
|
||||
|
||||
<!ENTITY zotero.import "Import">
|
||||
<!ENTITY zotero.import.whereToImportFrom "Where do you want to import from?">
|
||||
<!ENTITY zotero.import.source.file "A file (BibTeX, RIS, Zotero RDF, etc.)">
|
||||
<!ENTITY zotero.import.importing "Importing…">
|
||||
<!ENTITY zotero.import.database "Database">
|
||||
<!ENTITY zotero.import.lastModified "Last Modified">
|
||||
<!ENTITY zotero.import.size "Size">
|
||||
<!ENTITY zotero.import.createCollection "Place imported collections and items into new collection">
|
||||
|
||||
<!ENTITY zotero.exportOptions.title "Eksportér...">
|
||||
<!ENTITY zotero.exportOptions.format.label "Format:">
|
||||
<!ENTITY zotero.exportOptions.translatorOptions.label "Oversættelsesmuligheder">
|
||||
|
@ -236,9 +247,9 @@
|
|||
<!ENTITY zotero.integration.prefs.bookmarks.label "Bogmærker">
|
||||
<!ENTITY zotero.integration.prefs.bookmarks.caption "Bogmærker kan deles mellem Word og LibreOffice, men kan skabe fejl, hvis de utilsigtet ændres, og de kan ikke indsættes i fodnoter.">
|
||||
|
||||
<!ENTITY zotero.integration.prefs.automaticCitationUpdates.label "Automatically update citations">
|
||||
<!ENTITY zotero.integration.prefs.automaticCitationUpdates.tooltip "Citations with pending updates will be highlighted in the document">
|
||||
<!ENTITY zotero.integration.prefs.automaticCitationUpdates.description "Disabling updates can speed up citation insertion in large documents. Click Refresh to update citations manually.">
|
||||
<!ENTITY zotero.integration.prefs.automaticCitationUpdates.label "Opdatér automatisk referencer">
|
||||
<!ENTITY zotero.integration.prefs.automaticCitationUpdates.tooltip "Referencer, som afventer opdatering, vil blive fremhævet i dokumentet.">
|
||||
<!ENTITY zotero.integration.prefs.automaticCitationUpdates.description "Ved at deaktivere opdateringer kan indsættelse af referencer i store dokumenter gøres hurtigere. Klik Opdatér for at opdatere referencer manuelt.">
|
||||
|
||||
|
||||
<!ENTITY zotero.integration.prefs.automaticJournalAbbeviations.label "Brug MEDLINE's tidsskriftforkortelser">
|
||||
|
|
|
@ -62,11 +62,13 @@ general.tryLater=Prøv igen senere
|
|||
general.showDirectory=Vis mappe
|
||||
general.continue=Fortsæt
|
||||
general.copyToClipboard=Kopiér til udklipsholderen
|
||||
general.cancel=Cancel
|
||||
general.clear=Clear
|
||||
general.processing=Processing
|
||||
general.submitted=Submitted
|
||||
general.thanksForHelpingImprove=Thanks for helping to improve %S!
|
||||
general.cancel=Annullér
|
||||
general.clear=Ryd
|
||||
general.processing=Behandler
|
||||
general.submitted=Indsendt
|
||||
general.thanksForHelpingImprove=Tak for din hjælp med at forbedre %S!
|
||||
general.describeProblem=Briefly describe the problem:
|
||||
general.nMegabytes=%S MB
|
||||
|
||||
general.operationInProgress=En handling i Zotero er ved at blive udført.
|
||||
general.operationInProgress.waitUntilFinished=Vent venligst til den er færdig.
|
||||
|
@ -609,7 +611,7 @@ db.integrityCheck.errorsFixed=Fejlene i din Zotero-database er blevet udbedret.
|
|||
db.integrityCheck.errorsNotFixed=Zotero var ikke i stand til at udbedre alle fejl i din database.
|
||||
db.integrityCheck.reportInForums=Du kan rapportere dette problem i Zotero-forummerne.
|
||||
|
||||
zotero.preferences.chooseFileHandler=Choose File Handler
|
||||
zotero.preferences.chooseApplication=Vælg program
|
||||
|
||||
zotero.preferences.update.updated=Opdateret
|
||||
zotero.preferences.update.upToDate=Up-to-date
|
||||
|
@ -686,10 +688,13 @@ fileInterface.importComplete=Import færdig
|
|||
fileInterface.itemsWereImported=%1$S element blev importeret;%1$S elementer blev importeret
|
||||
fileInterface.itemsExported=Eksporterer elementer...
|
||||
fileInterface.import=Importér
|
||||
fileInterface.chooseAppDatabaseToImport=Choose the %S database to import
|
||||
fileInterface.export=Eksportér
|
||||
fileInterface.exportedItems=Eksporterede elementer
|
||||
fileInterface.imported=Importeret
|
||||
fileInterface.unsupportedFormat=Den valgte fil er ikke i et understøttet format.
|
||||
fileInterface.appDatabase=%S Database
|
||||
fileInterface.appImportCollection=%S Import
|
||||
fileInterface.viewSupportedFormats=Vis understøttede formater...
|
||||
fileInterface.untitledBibliography=Referenceliste uden navn
|
||||
fileInterface.bibliographyHTMLTitle=Rererenceliste
|
||||
|
@ -860,10 +865,14 @@ integration.corruptBibliography.description=Alle elementer henvist til i teksten
|
|||
integration.citationChanged=Du har ændret denne henvisning siden Zotero indsatte den. Vil du beholde dine rettelser og forhindre fremtidige opdateringer?
|
||||
integration.citationChanged.description=Hvis du trykker "Ja", vil Zotero ikke opdatere denne henvisning, hvis du tilføjer flere henvisninger, skifter bibliografisk format eller ændrer elementet, som der henvises til. Hvis du trykker "Nej", vil dine ændringer blive slettet.
|
||||
integration.citationChanged.edit=Du har ændret denne henvisning, siden Zotero dannede den. Redigering vil slette dine ændringer. Ønsker du at fortsætte?
|
||||
integration.citationChanged.original=Oprindelig: %S
|
||||
integration.citationChanged.modified=Ændret: %S
|
||||
integration.delayCitationUpdates.alert.text1=Updating citations in this document is taking a long time. Would you like to disable automatic citation updates?
|
||||
integration.delayCitationUpdates.alert.text2=You will need to click Refresh in the Zotero plugin when you are done inserting citations.
|
||||
integration.delayCitationUpdates.alert.text2.toolbar=Du skal klikke Opdatér i Zotero-værktøjslinjen, når du er færdig med at indsætte referencer.
|
||||
integration.delayCitationUpdates.alert.text2.tab=Du skal klikke Opdatér i Zotero-fanebladet, når du er færdig med at indsætte referencer.
|
||||
integration.delayCitationUpdates.alert.text3=You can change this setting later in the document preferences.
|
||||
integration.delayCitationUpdates.bibliography=Automatic citation updates are disabled. To see the bibliography, click Refresh in the Zotero plugin.
|
||||
integration.delayCitationUpdates.bibliography.toolbar=Automatisk opdatering af referencer er deaktiveret. Klik Opdatér i Zotero-værktøjslinjen for at se referencelisten.
|
||||
integration.delayCitationUpdates.bibliography.tab=Automatisk opdatering af referencer er deaktiveret. Klik Opdatér i Zotero-fanebladet for at se referencelisten.
|
||||
|
||||
styles.install.title=Installér bibliografisk format
|
||||
styles.install.unexpectedError=Der opstod en uventet fejl under forsøget på at installere "%1$S"
|
||||
|
@ -1058,8 +1067,9 @@ recognizePDF.couldNotRead=Kunne ikke læse tekst fra PDF
|
|||
recognizePDF.noMatches=Fandt ikke referencer, der matchede
|
||||
recognizePDF.fileNotFound=Fil ikke fundet
|
||||
recognizePDF.error=Der opstod en uventet fejl
|
||||
recognizePDF.recognizing.label=Retrieving Metadata…
|
||||
recognizePDF.recognizing.label=Henter metadata...
|
||||
recognizePDF.complete.label=Hentning af metadata fuldført
|
||||
recognizePDF.reportMetadata=Report Incorrect Metadata
|
||||
|
||||
rtfScan.openTitle=Vælg en fil der skal skannes
|
||||
rtfScan.scanning.label=Skanner RTF-dokument...
|
||||
|
@ -1068,6 +1078,7 @@ rtfScan.rtf=Rich Text Format (.rtf)
|
|||
rtfScan.saveTitle=Vælg et sted at gemme den formaterede fil
|
||||
rtfScan.scannedFileSuffix=(skannet)
|
||||
|
||||
extractedAnnotations=Extracted Annotations
|
||||
|
||||
file.accessError.theFileCannotBeCreated=Filen "%S" kan ikke oprettes.
|
||||
file.accessError.theFileCannotBeUpdated=Filen "%S" kan ikke opdateres.
|
||||
|
|
62
chrome/locale/de/zotero/connector.json
Normal file
62
chrome/locale/de/zotero/connector.json
Normal file
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"error_connection_enableSavingToOnlineLibrary": {
|
||||
"message": "Speichern in der Online-Bibliothek aktivieren"
|
||||
},
|
||||
"error_connection_save": {
|
||||
"message": "Die $1 war nicht in der Lage mit der $2 Desktop Applikation zu kommunizieren. Die Erweiterung kann einige Seiten direkt in Ihr $3 Benutzerkonto speichern, aber für optimale Resultate sollten Sie sicherstellen, dass $2 geöffnet ist vor dem Speichern."
|
||||
},
|
||||
"progressWindow_error_translation": {
|
||||
"message": "Ein Fehler ist aufgetreten beim Versuch, diesen Eintrag zu speichern. Siehe $1 für weitere Informationen."
|
||||
},
|
||||
"general_tryAgain": {
|
||||
"message": "Versuchen Sie es erneut"
|
||||
},
|
||||
"error_connection_isAppRunning": {
|
||||
"message": "Läuft $1?"
|
||||
},
|
||||
"progressWindow_tagPlaceholder": {
|
||||
"message": "Tags (getrennt durch Kommas)"
|
||||
},
|
||||
"general_more": {
|
||||
"message": "Mehr..."
|
||||
},
|
||||
"progressWindow_error_upgradeClient": {
|
||||
"message": "Diese Funktionalität wird nicht unterstützt mit Ihrer Version von $1. Bitte aktualisieren Sie auf die $2."
|
||||
},
|
||||
"general_cancel": {
|
||||
"message": "Abbrechen"
|
||||
},
|
||||
"progressWindow_error_upgradeClient_latestVersion": {
|
||||
"message": "neueste Version"
|
||||
},
|
||||
"progressWindow_error_troubleshootingTranslatorIssues": {
|
||||
"message": "Problembehebung für Translators"
|
||||
},
|
||||
"error_connection_downloadOrTroubleshoot": {
|
||||
"message": "Sie können <a href=\"$1\">$2 herunterladen</a> oder <a href=\"$3\">Verbindungsprobleme beheben</a> falls nötig."
|
||||
},
|
||||
"general_done": {
|
||||
"message": "Erledigt"
|
||||
},
|
||||
"progressWindow_error_fallback": {
|
||||
"message": "Ein Fehler ist aufgetreten beim Speichern mit $1. Es wird versucht stattdessen $2 zu nutzen."
|
||||
},
|
||||
"upgradeApp": {
|
||||
"message": "Upgrade $1"
|
||||
},
|
||||
"integration_error_clientUpgrade": {
|
||||
"message": "Web-basiertes Zitieren benötigt $1 oder später."
|
||||
},
|
||||
"general_saveTo": {
|
||||
"message": "Speichern nach $1"
|
||||
},
|
||||
"appConnector": {
|
||||
"message": "$1 Erweiterung"
|
||||
},
|
||||
"progressWindow_savingTo": {
|
||||
"message": "Speichern nach"
|
||||
},
|
||||
"integration_error_connection": {
|
||||
"message": "Die $1 war nicht in der Lage mit der $2 Desktop Applikation zu kommunizieren. $2 muss geöffnet sein zum web-basierten Zitieren."
|
||||
}
|
||||
}
|
|
@ -10,12 +10,14 @@
|
|||
<!ENTITY zotero.general.cancel "Abbrechen">
|
||||
<!ENTITY zotero.general.refresh "Aktualisieren">
|
||||
<!ENTITY zotero.general.saveAs "Speichern als...">
|
||||
<!ENTITY zotero.general.options "Optionen">
|
||||
<!ENTITY zotero.general.advancedOptions.label "Erweiterte Einstellungen">
|
||||
<!ENTITY zotero.general.tools "Werkzeuge">
|
||||
<!ENTITY zotero.general.more "Mehr">
|
||||
<!ENTITY zotero.general.loading "Lade...">
|
||||
<!ENTITY zotero.general.close "Schließen">
|
||||
<!ENTITY zotero.general.minimize "Minimieren">
|
||||
<!ENTITY zotero.general.other "Andere...">
|
||||
|
||||
|
||||
<!ENTITY zotero.errorReport.title "Zotero Fehlerbericht">
|
||||
|
@ -175,7 +177,7 @@
|
|||
<!ENTITY zotero.tagColorChooser.setColor "Farbe zuweisen">
|
||||
<!ENTITY zotero.tagColorChooser.removeColor "Farbe entfernen">
|
||||
|
||||
<!ENTITY zotero.lookup.description "Geben Sie die nachzuschlagende ISBN, DOI or PMID in das Eingabefeld unten ein.">
|
||||
<!ENTITY zotero.lookup.description "Geben Sie ISBNs, DOIs, PMIDs oder arXiv IDs ein, um diese zu Ihrer Bibliothek hinzuzufügen:">
|
||||
<!ENTITY zotero.lookup.button.search "Suche">
|
||||
|
||||
<!ENTITY zotero.selectitems.title "Einträge auswählen">
|
||||
|
@ -202,6 +204,15 @@
|
|||
|
||||
<!ENTITY zotero.progress.title "Fortschritt">
|
||||
|
||||
<!ENTITY zotero.import "Importieren">
|
||||
<!ENTITY zotero.import.whereToImportFrom "Von wo aus möchten Sie importieren?">
|
||||
<!ENTITY zotero.import.source.file "Einer Datei (BibTeX, RIS, Zotero RDF, etc.)">
|
||||
<!ENTITY zotero.import.importing "Importieren...">
|
||||
<!ENTITY zotero.import.database "Datenbank">
|
||||
<!ENTITY zotero.import.lastModified "Zuletzt geändert">
|
||||
<!ENTITY zotero.import.size "Größe">
|
||||
<!ENTITY zotero.import.createCollection "Importierte Sammlungen und Einträge in neue Sammlungen einstellen">
|
||||
|
||||
<!ENTITY zotero.exportOptions.title "Exportieren...">
|
||||
<!ENTITY zotero.exportOptions.format.label "Format:">
|
||||
<!ENTITY zotero.exportOptions.translatorOptions.label "Übersetzer-Einstellungen">
|
||||
|
|
|
@ -67,6 +67,8 @@ general.clear=Löschen
|
|||
general.processing=Verarbeitung
|
||||
general.submitted=Übermittelt
|
||||
general.thanksForHelpingImprove=Vielen Dank, dass Sie helfen %S zu verbessern!
|
||||
general.describeProblem=Beschreiben Sie kurz das Problem:
|
||||
general.nMegabytes=%S MB
|
||||
|
||||
general.operationInProgress=Zotero ist beschäftigt.
|
||||
general.operationInProgress.waitUntilFinished=Bitte warten Sie, bis der Vorgang abgeschlossen ist.
|
||||
|
@ -609,7 +611,7 @@ db.integrityCheck.errorsFixed=Diese Fehler in Ihrer Zotero-Datenbank wurden beho
|
|||
db.integrityCheck.errorsNotFixed=Zotero konnte nicht alle Fehler in Ihrer Datenbank beheben.
|
||||
db.integrityCheck.reportInForums=Sie können einen Fehlerbericht in den Zotero-Foren erstellen.
|
||||
|
||||
zotero.preferences.chooseFileHandler=Programm auswählen
|
||||
zotero.preferences.chooseApplication=Anwendung auswählen
|
||||
|
||||
zotero.preferences.update.updated=Update durchgeführt
|
||||
zotero.preferences.update.upToDate=Auf dem neuesten Stand
|
||||
|
@ -686,10 +688,13 @@ fileInterface.importComplete=Importieren abgeschlossen
|
|||
fileInterface.itemsWereImported=%1$S Eintrag wurde importiert;%1$S Einträge wurden importiert
|
||||
fileInterface.itemsExported=Einträge werden exportiert...
|
||||
fileInterface.import=Importieren
|
||||
fileInterface.chooseAppDatabaseToImport=Wählen Sie die zu importierende %S-Datenbank aus.
|
||||
fileInterface.export=Exportieren
|
||||
fileInterface.exportedItems=Exportierte Einträge
|
||||
fileInterface.imported=Importiert
|
||||
fileInterface.unsupportedFormat=Das Format der ausgewählten Datei wird nicht unterstützt.
|
||||
fileInterface.appDatabase=%S Datenbank
|
||||
fileInterface.appImportCollection=%S Import
|
||||
fileInterface.viewSupportedFormats=Unterstützte Formate anzeigen...
|
||||
fileInterface.untitledBibliography=Literaturverzeichnis ohne Titel
|
||||
fileInterface.bibliographyHTMLTitle=Literaturverzeichnis
|
||||
|
@ -828,7 +833,7 @@ integration.cited.loading=Lade zitierte Einträge
|
|||
integration.ibid=ebd
|
||||
integration.emptyCitationWarning.title=Leere Zitation
|
||||
integration.emptyCitationWarning.body=Die ausgewählte Zitation wäre im aktuell ausgewählten Stil leer. Sind Sie sicher, dass Sie sie hinzufügen wollen?
|
||||
integration.openInLibrary=Mit %S öffnen
|
||||
integration.openInLibrary=In %S öffnen
|
||||
|
||||
integration.error.incompatibleVersion=Diese Version des Zotero Textverarbeitungs-Plugins ($INTEGRATION_VERSION) ist nicht mit der aktuell installierten Version der Zotero-Firefox-Erweiterung (%1$S) kompatibel. Bitte stellen Sie sicher, dass Sie die aktuellsten Versionen der beiden Komponenten verwenden.
|
||||
integration.error.incompatibleVersion2=Zotero %1$S benötigt %2$S %3$S oder neuer. Bitte laden Sie die neueste Verson von %2$S von zotero.org herunter.
|
||||
|
@ -860,10 +865,14 @@ integration.corruptBibliography.description=Alle im Text zitierten Einträge wer
|
|||
integration.citationChanged=Sie haben Veränderungen an dieser Zitation vorgenommen, nachdem sie von Zotero erstellt wurde. Wollen Sie Ihre Veränderungen beibehalten und spätere Updates verhindern?
|
||||
integration.citationChanged.description=Wenn Sie "Ja" auswählen, wird Zotero diese Zitation nicht aktualisieren, wenn Sie weitere Zitationen hinzufügen, einen anderen Zitationsstil wählen oder die Literaturangabe, auf die sie sich bezieht, verändern. Wenn Sie "Nein" wählen, werden ihre Veränderungen gelöscht.
|
||||
integration.citationChanged.edit=Sie haben Veränderungen an dieser Zitation vorgenommen, nachdem sie von Zotero erstellt wurde. Das Editieren wird Ihre Veränderungen löschen. Wollen Sie fortsetzen?
|
||||
integration.citationChanged.original=Original: %S
|
||||
integration.citationChanged.modified=Geändert: %S
|
||||
integration.delayCitationUpdates.alert.text1=Updating citations in this document is taking a long time. Would you like to disable automatic citation updates?
|
||||
integration.delayCitationUpdates.alert.text2=You will need to click Refresh in the Zotero plugin when you are done inserting citations.
|
||||
integration.delayCitationUpdates.alert.text2.toolbar=Sie müssen auf Aktualisieren in der Zotero-Werkzeugleiste klicken, nachdem Sie die Zitationen eingefügt haben.
|
||||
integration.delayCitationUpdates.alert.text2.tab=Sie müssen auf Aktualisieren im Zotero-Reiter klicken, nachdem Sie die Zitationen eingefügt haben.
|
||||
integration.delayCitationUpdates.alert.text3=You can change this setting later in the document preferences.
|
||||
integration.delayCitationUpdates.bibliography=Automatic citation updates are disabled. To see the bibliography, click Refresh in the Zotero plugin.
|
||||
integration.delayCitationUpdates.bibliography.toolbar=Automatische Updates der Zitationen sind deaktiviert. Um das Literaturverzeichnis anzuzeigen, klicken Sie auf Aktualisieren in der Zotero-Werkzeugleiste.
|
||||
integration.delayCitationUpdates.bibliography.tab=Automatische Updates der Zitationen sind deaktiviert. Um das Literaturverzeichnis anzuzeigen, klicken Sie auf Aktualisieren im Zotero-Reiter.
|
||||
|
||||
styles.install.title=Zitierstil installieren
|
||||
styles.install.unexpectedError=Bei der Installation von "%1$S" trat ein unerwarteter Fehler auf
|
||||
|
@ -1060,6 +1069,7 @@ recognizePDF.fileNotFound=Datei nicht gefunden
|
|||
recognizePDF.error=Ein unerwarteter Fehler ist aufgetreten
|
||||
recognizePDF.recognizing.label=Abruf der Metadaten...
|
||||
recognizePDF.complete.label=Metadaten-Abruf abgeschlossen.
|
||||
recognizePDF.reportMetadata=Falsche Metadaten melden
|
||||
|
||||
rtfScan.openTitle=Wählen Sie eine Datei zum Scannen aus
|
||||
rtfScan.scanning.label=Scanne RTF-Dokument...
|
||||
|
@ -1068,6 +1078,7 @@ rtfScan.rtf=Rich Text Format (.rtf)
|
|||
rtfScan.saveTitle=Wählen Sie einen Speicherort für die formatierte Datei
|
||||
rtfScan.scannedFileSuffix=(Gescannt)
|
||||
|
||||
extractedAnnotations=Extrahierte Anmerkungen
|
||||
|
||||
file.accessError.theFileCannotBeCreated=Die Datei '%S' kann nicht erstellt werden.
|
||||
file.accessError.theFileCannotBeUpdated=Die Datei '%S' kann nicht aktualisiert werden.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!ENTITY zotero.version "έκδοση">
|
||||
<!ENTITY zotero.whatsNew "What’s new">
|
||||
<!ENTITY zotero.whatsNew "Τι νέο υπάρχει">
|
||||
<!ENTITY zotero.createdby "Δημιουργήθηκε Από:">
|
||||
<!ENTITY zotero.director "Διευθυντής:">
|
||||
<!ENTITY zotero.directors "Διευθυντές:">
|
||||
|
|
62
chrome/locale/el-GR/zotero/connector.json
Normal file
62
chrome/locale/el-GR/zotero/connector.json
Normal file
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"error_connection_enableSavingToOnlineLibrary": {
|
||||
"message": "Ενεργοποίηση αποθήκευσης στη Βιβλιοθήκη με απευθείας σύνδεση "
|
||||
},
|
||||
"error_connection_save": {
|
||||
"message": "Το $1 δεν μπόρεσε να επικοινωνήσει με την εφαρμογή γραφείου $2. Ο Σύνδεσμος μπορεί να αποθηκεύσει κάποιες σελίδες απευθείας στο λογαριασμό σας $3, αλλά για καλύτερα αποτελέσματα θα πρέπει να βεβαιωθείτε ότι η εφαρμογή $2 είναι ανοιχτή πριν επιχειρήσετε να το αποθηκεύσετε."
|
||||
},
|
||||
"progressWindow_error_translation": {
|
||||
"message": "Παρουσιάστηκε σφάλμα κατά την αποθήκευση αυτού του στοιχείου. Δείτε 1 $1 για περισσότερες πληροφορίες."
|
||||
},
|
||||
"general_tryAgain": {
|
||||
"message": "Ξαναπροσπάθηστε"
|
||||
},
|
||||
"error_connection_isAppRunning": {
|
||||
"message": "Εκτελείτε η $1;"
|
||||
},
|
||||
"progressWindow_tagPlaceholder": {
|
||||
"message": "Ετικέτες (διαχωρισμένες με κόμμα)"
|
||||
},
|
||||
"general_more": {
|
||||
"message": "Περισσότερα..."
|
||||
},
|
||||
"progressWindow_error_upgradeClient": {
|
||||
"message": "Αυτή η λειτουργία δεν υποστηρίζεται από την έκδοση 1 $1. Παρακαλώ αναβαθμίστε στην 2 $2."
|
||||
},
|
||||
"general_cancel": {
|
||||
"message": "Άκυρο"
|
||||
},
|
||||
"progressWindow_error_upgradeClient_latestVersion": {
|
||||
"message": "τελευταία έκδοση"
|
||||
},
|
||||
"progressWindow_error_troubleshootingTranslatorIssues": {
|
||||
"message": "Αντιμετώπιση Προβλημάτων Μεταφραστή"
|
||||
},
|
||||
"error_connection_downloadOrTroubleshoot": {
|
||||
"message": "Μπορείτε <a href=\"$1\">να κατεβάσετε $2</a> ή <a href=\"$3\"> αντιμετωπίστε τα προβλήματα της σύνδεση</a> αν είναι απαραίτητο."
|
||||
},
|
||||
"general_done": {
|
||||
"message": "Ολοκληρώθηκε"
|
||||
},
|
||||
"progressWindow_error_fallback": {
|
||||
"message": "Παρουσιάστηκε σφάλμα κατά την αποθήκευση με 1 $1. Προσπαθήστε να αποθηκεύσετε χρησιμοποιώντας 2 $2."
|
||||
},
|
||||
"upgradeApp": {
|
||||
"message": "Αναβάθμιση $1"
|
||||
},
|
||||
"integration_error_clientUpgrade": {
|
||||
"message": "Η αναφορά Ιστού απαιτεί $1 ή νεότερη έκδοση."
|
||||
},
|
||||
"general_saveTo": {
|
||||
"message": "Αποθήκευση σε 1 $1"
|
||||
},
|
||||
"appConnector": {
|
||||
"message": "$1 Συνδετήρας"
|
||||
},
|
||||
"progressWindow_savingTo": {
|
||||
"message": "Αποθήκευση σε"
|
||||
},
|
||||
"integration_error_connection": {
|
||||
"message": "Το $1 δεν μπόρεσε να επικοινωνήσει με την $2 εφαρμογή γραφείου. Το $2 πρέπει να είναι ανοικτό για να χρησιμοποιηθεί μια αναφορά ιστού."
|
||||
}
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
<!ENTITY styles.editor "Zotero Style Editor">
|
||||
<!ENTITY styles.editor "Εμφάνιση Συντάκτη Zotero">
|
||||
|
||||
<!ENTITY styles.editor.citePosition "Cite Position:">
|
||||
<!ENTITY styles.editor.citePosition "Θέση Αναφοράς:">
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<!ENTITY styles.preview "Zotero Style Preview">
|
||||
<!ENTITY styles.preview "Προεπισκόπιση Μορφής Zotero">
|
||||
|
||||
<!ENTITY styles.preview.citationFormat "Citation Format:">
|
||||
<!ENTITY styles.preview.citationFormat "Μορφή Αναφοράς:">
|
||||
<!ENTITY styles.preview.citationFormat.all "όλα">
|
||||
<!ENTITY styles.preview.citationFormat.author "συγγραφέας">
|
||||
<!ENTITY styles.preview.citationFormat.authorDate "συγγραφέας-ημερομηνία">
|
||||
<!ENTITY styles.preview.citationFormat.label "label">
|
||||
<!ENTITY styles.preview.citationFormat.note "note">
|
||||
<!ENTITY styles.preview.citationFormat.numeric "numeric">
|
||||
<!ENTITY styles.preview.citationFormat.label "ετικέτα">
|
||||
<!ENTITY styles.preview.citationFormat.note "σημέιωση">
|
||||
<!ENTITY styles.preview.citationFormat.numeric "αριθμητικό">
|
||||
|
|
|
@ -9,23 +9,23 @@
|
|||
<!ENTITY zotero.preferences.prefpane.general "Γενικές">
|
||||
|
||||
<!ENTITY zotero.preferences.userInterface "Περιβάλλον χρήστη">
|
||||
<!ENTITY zotero.preferences.layout "Layout:">
|
||||
<!ENTITY zotero.preferences.layout "Διάταξη:">
|
||||
<!ENTITY zotero.preferences.layout.standard "Standard">
|
||||
<!ENTITY zotero.preferences.layout.stacked "Stacked">
|
||||
<!ENTITY zotero.preferences.fontSize "Μέγεθος γραμματοσειράς:">
|
||||
<!ENTITY zotero.preferences.fontSize.small "Μικρό">
|
||||
<!ENTITY zotero.preferences.fontSize.medium "Μέσο">
|
||||
<!ENTITY zotero.preferences.fontSize.large "Μεγάλο">
|
||||
<!ENTITY zotero.preferences.fontSize.xlarge "X-Large">
|
||||
<!ENTITY zotero.preferences.fontSize.xlarge "Πολύ Μεγάλο">
|
||||
<!ENTITY zotero.preferences.fontSize.notes "Μέγεθος γραμματοσειράς σημειώσεων:">
|
||||
|
||||
<!ENTITY zotero.preferences.fileHandling "File Handling">
|
||||
<!ENTITY zotero.preferences.automaticSnapshots "Αυτόματη λήψη φωτογραφικού στιγμιότυπου όταν δημιουργείτε στοιχεία από ιστοσελίδες">
|
||||
<!ENTITY zotero.preferences.downloadAssociatedFiles "Αυτόματη προσάρτηση των συνοδευτικών αρχείων PDF και άλλων όταν αποθηκεύετε στοιχεία">
|
||||
<!ENTITY zotero.preferences.autoRecognizeFiles "Automatically retrieve metadata for PDFs">
|
||||
<!ENTITY zotero.preferences.autoRenameFiles "Automatically rename attachment files using parent metadata">
|
||||
<!ENTITY zotero.preferences.autoRecognizeFiles "Αυτόματη ανάκτηση μεταδεδομένων για αρχεία PDF">
|
||||
<!ENTITY zotero.preferences.autoRenameFiles "Αυτόματη μετονομασία συνημμένων αρχείων χρησιμοποιώντας μεταγενέστερα μεταδεδομένα">
|
||||
<!ENTITY zotero.preferences.fileHandler.openPDFsUsing "Open PDFs using">
|
||||
<!ENTITY zotero.preferences.fileHandler.systemDefault "System Default">
|
||||
<!ENTITY zotero.preferences.fileHandler.systemDefault "Προεπιλογή Συστήματος">
|
||||
|
||||
<!ENTITY zotero.preferences.miscellaneous "Διάφορα">
|
||||
<!ENTITY zotero.preferences.autoUpdate "Αυτόματος έλεγχος για ενημερωμένους μεταφραστές και στυλ">
|
||||
|
@ -46,9 +46,9 @@
|
|||
<!ENTITY zotero.preferences.groups.tags "ετικέτες">
|
||||
|
||||
<!ENTITY zotero.preferences.feeds "Feeds">
|
||||
<!ENTITY zotero.preferences.feeds.sorting.label "Sorting:">
|
||||
<!ENTITY zotero.preferences.feeds.sorting.newest "Newest items first">
|
||||
<!ENTITY zotero.preferences.feeds.sorting.oldest "Oldest items first">
|
||||
<!ENTITY zotero.preferences.feeds.sorting.label "Ταξινόμηση:">
|
||||
<!ENTITY zotero.preferences.feeds.sorting.newest "Πρώτα τα νεώτερα στοιχεία">
|
||||
<!ENTITY zotero.preferences.feeds.sorting.oldest "Πρώτα τα παλαιότερα στοιχεία">
|
||||
<!ENTITY zotero.preferences.feeds.feedDefaults "Feed Defaults">
|
||||
|
||||
<!ENTITY zotero.preferences.openurl.search "Αναζήτηση διακομιστών resolver">
|
||||
|
@ -56,12 +56,12 @@
|
|||
<!ENTITY zotero.preferences.openurl.server "Διακομιστή resolver:">
|
||||
<!ENTITY zotero.preferences.openurl.version "Έκδοσης:">
|
||||
|
||||
<!ENTITY zotero.preferences.prefpane.sync "Sync">
|
||||
<!ENTITY zotero.preferences.prefpane.sync "Συγχρονισμός">
|
||||
<!ENTITY zotero.preferences.sync.username "Όνομα χρήστη:">
|
||||
<!ENTITY zotero.preferences.sync.password "Κωδικού:">
|
||||
<!ENTITY zotero.preferences.sync.syncServer "Data Syncing">
|
||||
<!ENTITY zotero.preferences.sync.setUpSync "Set Up Syncing">
|
||||
<!ENTITY zotero.preferences.sync.unlinkAccount "Unlink Account…">
|
||||
<!ENTITY zotero.preferences.sync.password "Κωδικός:">
|
||||
<!ENTITY zotero.preferences.sync.syncServer "Συγχρονισμός Δεδομένων">
|
||||
<!ENTITY zotero.preferences.sync.setUpSync "Ρύθμιση Συγχρονισμού">
|
||||
<!ENTITY zotero.preferences.sync.unlinkAccount "Αποσύνδεση Λογαριασμού...">
|
||||
<!ENTITY zotero.preferences.sync.createAccount "Δημιουργία λογαριασμού">
|
||||
<!ENTITY zotero.preferences.sync.lostPassword "Χάσατε τον κωδικό;">
|
||||
<!ENTITY zotero.preferences.sync.syncAutomatically "Αυτόματος συγχρονισμός">
|
||||
|
@ -78,23 +78,23 @@
|
|||
<!ENTITY zotero.preferences.sync.fileSyncing.verifyServer "Verify Server">
|
||||
<!ENTITY zotero.preferences.sync.fileSyncing.tos1 "Χρησιμοποιώντας την αποθήκευση Zotero συμφωνείτε ότι δεσμεύεστε από τους">
|
||||
<!ENTITY zotero.preferences.sync.fileSyncing.tos2 "όρους και τις προϋποθέσεις">
|
||||
<!ENTITY zotero.preferences.sync.librariesToSync "Libraries to Sync">
|
||||
<!ENTITY zotero.preferences.sync.librariesToSync.button "Choose Libraries…">
|
||||
<!ENTITY zotero.preferences.sync.librariesToSync.sync "Sync">
|
||||
<!ENTITY zotero.preferences.sync.librariesToSync.library "Library">
|
||||
<!ENTITY zotero.preferences.sync.librariesToSync "Βιβλιοθήκες προς Συγχρονισμό">
|
||||
<!ENTITY zotero.preferences.sync.librariesToSync.button "Επιλογή Βιβλιοθηκών...">
|
||||
<!ENTITY zotero.preferences.sync.librariesToSync.sync "Συγχρονισμός">
|
||||
<!ENTITY zotero.preferences.sync.librariesToSync.library "Βιβλιοθήκη">
|
||||
<!ENTITY zotero.preferences.sync.reset.warning1 "Οι λειτουργίες που ακολουθούν προορίζονται για χρήση μόνον σε σπάνιες, ειδικές συνθήκες και θα πρέπει να χρησιμοποιούνται για λόγους γενικής επίλυσης προβλημάτων. Σε πολλές περιπτώσεις η επαναφορά των ρυθμίσεων προκαλεί επιπρόσθετα προβλήματα. Δείτε">
|
||||
<!ENTITY zotero.preferences.sync.reset.warning2 "Συγχρονισμός ρυθμίσεων επαναφοράς">
|
||||
<!ENTITY zotero.preferences.sync.reset.warning3 "για περισσότερες πληροφορίες.">
|
||||
<!ENTITY zotero.preferences.sync.reset.resetDataSyncHistory "Reset Data Sync History">
|
||||
<!ENTITY zotero.preferences.sync.reset.resetDataSyncHistory.desc "Merge local data with remote data, ignoring sync history">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreFromServer "Restore from Online Library">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreFromServer.desc "Overwrite local Zotero data with data from the online library.">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreToServer "Restore to Online Library">
|
||||
<!ENTITY zotero.preferences.sync.reset.resetDataSyncHistory "Επαναφορά Ιστορικού Συγχρονισμού Δεδομένων">
|
||||
<!ENTITY zotero.preferences.sync.reset.resetDataSyncHistory.desc "Συγχώνευση τοπικών δεδομένων με απομακρυσμένα δεδομένα, αγνοώντας το ιστορικό συγχρονισμού">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreFromServer "Επαναφορά από την Online Βιβλιοθήκη">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreFromServer.desc "Αντικατάσταση των τοπικά δεδομένων Zotero με δεδομένα από την Online Ββλιοθήκη.">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreToServer "Επαναφορά της Online Βιβλιοθήκη">
|
||||
<!ENTITY zotero.preferences.sync.reset.restoreToServer.desc "Overwrite online library with local Zotero data">
|
||||
<!ENTITY zotero.preferences.sync.reset.resetFileSyncHistory "Μηδενισμός του ιστορικού συγχρονισμού αρχείων">
|
||||
<!ENTITY zotero.preferences.sync.reset.resetFileSyncHistory.desc "Compare all attachment files with the storage service">
|
||||
<!ENTITY zotero.preferences.sync.reset "Μηδενισμός/Επαναφορά">
|
||||
<!ENTITY zotero.preferences.sync.reset.button "Reset…">
|
||||
<!ENTITY zotero.preferences.sync.reset.resetFileSyncHistory.desc "Συγκρίνει όλα τα συνημμένα αρχεία με την υπηρεσία αποθήκευσης">
|
||||
<!ENTITY zotero.preferences.sync.reset "Επαναφορά">
|
||||
<!ENTITY zotero.preferences.sync.reset.button "Επαναφορά...">
|
||||
|
||||
|
||||
<!ENTITY zotero.preferences.prefpane.search "Αναζήτηση">
|
||||
|
@ -116,12 +116,12 @@
|
|||
<!ENTITY zotero.preferences.export.citePaperJournalArticleURL.description "Όταν επιλέγεται αυτή η λειτουργία το Zotero συμπεριλαμβάνει τις διευθύνσεις URL κατά την παράθεση άρθρων περιοδικών και εφημερίδων, μόνο αν στα άρθρα δεν ορίζεται το εύρος των σελίδων.">
|
||||
|
||||
<!ENTITY zotero.preferences.quickCopy.caption "Γρήγορη αντιγραφή">
|
||||
<!ENTITY zotero.preferences.quickCopy.defaultFormat "Default Format:">
|
||||
<!ENTITY zotero.preferences.quickCopy.defaultFormat "Αρχική Μορφή:">
|
||||
<!ENTITY zotero.preferences.quickCopy.copyAsHTML "Αντιγραφή ως HTML">
|
||||
<!ENTITY zotero.preferences.quickCopy.siteEditor.setings "Ρυθμίσεις βάσει ιστοτόπου:">
|
||||
<!ENTITY zotero.preferences.quickCopy.siteEditor.domainPath "Περιοχή/Διαδρομή">
|
||||
<!ENTITY zotero.preferences.quickCopy.siteEditor.domainPath.example "(e.g., wikipedia.org)">
|
||||
<!ENTITY zotero.preferences.quickCopy.siteEditor.format "Format">
|
||||
<!ENTITY zotero.preferences.quickCopy.siteEditor.domainPath.example "(π.χ. wikipedia.org)">
|
||||
<!ENTITY zotero.preferences.quickCopy.siteEditor.format "Μορφή">
|
||||
<!ENTITY zotero.preferences.quickCopy.siteEditor.locale "Language">
|
||||
<!ENTITY zotero.preferences.quickCopy.dragLimit "Απενεργοποίηση της Γρήγορης αντιγραφής όταν σύρονται περισσότερα από">
|
||||
|
||||
|
@ -129,8 +129,8 @@
|
|||
<!ENTITY zotero.preferences.cite.styles "Στυλ">
|
||||
<!ENTITY zotero.preferences.cite.wordProcessors "Επεξεργαστές κειμένου">
|
||||
<!ENTITY zotero.preferences.cite.wordProcessors.useClassicAddCitationDialog "Χρήση του κλασικού διαλόγου Προσθήκη παραπομπής">
|
||||
<!ENTITY zotero.preferences.styleEditor "Style Editor">
|
||||
<!ENTITY zotero.preferences.stylePreview "Style Preview">
|
||||
<!ENTITY zotero.preferences.styleEditor "Επεξεργασία Εμφάνισης">
|
||||
<!ENTITY zotero.preferences.stylePreview "Προεπισκόπιση Εμφάνισης">
|
||||
|
||||
<!ENTITY zotero.preferences.cite.styles.styleManager "Διαχειριστής στυλ">
|
||||
<!ENTITY zotero.preferences.cite.styles.styleManager.title "Τίτλ">
|
||||
|
@ -149,8 +149,8 @@
|
|||
<!ENTITY zotero.preferences.keys.toggleTagSelector "Εναλλαγή Επιλογέα ετικετών">
|
||||
<!ENTITY zotero.preferences.keys.copySelectedItemCitationsToClipboard "Αντιγραφή των επιλεγμένων παραπομπών στοιχείου στην προσωρινή μνήμη">
|
||||
<!ENTITY zotero.preferences.keys.copySelectedItemsToClipboard "Αντιγραφή των επιλεγμένων στοιχείων στην προσωρινή μνήμη">
|
||||
<!ENTITY zotero.preferences.keys.importFromClipboard "Εισαγωγή από την προσωρινή μνήμη">
|
||||
<!ENTITY zotero.preferences.keys.changesTakeEffect "Changes take effect after restart">
|
||||
<!ENTITY zotero.preferences.keys.importFromClipboard "Εισαγωγή από το Πρόχειρο">
|
||||
<!ENTITY zotero.preferences.keys.changesTakeEffect "Ενεργοποίηση μετά από επανεκκίνηση">
|
||||
|
||||
<!ENTITY zotero.preferences.prefpane.proxies "Διαμεσολαβητές">
|
||||
|
||||
|
@ -177,9 +177,9 @@
|
|||
|
||||
<!ENTITY zotero.preferences.prefpane.advanced "Προχωρημένα">
|
||||
<!ENTITY zotero.preferences.advanced.filesAndFolders "Αρχεία και Φάκελοι">
|
||||
<!ENTITY zotero.preferences.advanced.keys "Shortcuts">
|
||||
<!ENTITY zotero.preferences.advanced.keys "Συντομέυσεις">
|
||||
|
||||
<!ENTITY zotero.preferences.advanced.advancedConfiguration "Advanced Configuration">
|
||||
<!ENTITY zotero.preferences.advanced.advancedConfiguration "Προηγμένη διαμόρφωση">
|
||||
|
||||
<!ENTITY zotero.preferences.prefpane.locate "Εντοπισμός">
|
||||
<!ENTITY zotero.preferences.locate.locateEngineManager "Διαχειριστής Μηχανής Αναζήτησης Άρθρων">
|
||||
|
@ -195,13 +195,13 @@
|
|||
|
||||
<!ENTITY zotero.preferences.dataDir "Θέση Καταλόγου δεδομένων">
|
||||
<!ENTITY zotero.preferences.dataDir.useProfile "Χρήση του καταλόγου προφίλ">
|
||||
<!ENTITY zotero.preferences.dataDir.custom "Προσαρμοσμένος:">
|
||||
<!ENTITY zotero.preferences.dataDir.choose "Choose…">
|
||||
<!ENTITY zotero.preferences.dataDir.viaCommandLine "(specified via command line)">
|
||||
<!ENTITY zotero.preferences.dataDir.custom "Προσαρμογή...">
|
||||
<!ENTITY zotero.preferences.dataDir.choose "Επιλέξτε...">
|
||||
<!ENTITY zotero.preferences.dataDir.viaCommandLine "(καθορίζεται μέσω γραμμής εντολών)">
|
||||
<!ENTITY zotero.preferences.dataDir.reveal "Εμφάνιση του Καταλόγου δεδομένων">
|
||||
<!ENTITY zotero.preferences.dataDir.migrate "Migrate To New Default Location…">
|
||||
<!ENTITY zotero.preferences.dataDir.migrate "Μετεγκατάσταση Σε Νέα Προεπιλεγμένη Θέση ...">
|
||||
|
||||
<!ENTITY zotero.preferences.attachmentBaseDir.caption "Linked Attachment Base Directory">
|
||||
<!ENTITY zotero.preferences.attachmentBaseDir.caption "Σύνδεσμος Βασικού Καταλόγου Συνημμένων">
|
||||
<!ENTITY zotero.preferences.attachmentBaseDir.message "Το Zotero θα χρησιμοποιήσει σχετικές διαδρομές για τα συνδεδεμένα προσαρτήματα αρχείων εντός του καταλόγου βάσης του, επιτρέποντας την πρόσβαση προς αρχείων σε διαφορετικούς υπολογιστές, εφόσον η δομή των αρχείων μέσα στον κατάλογο βάσης παραμένει η ίδια.">
|
||||
<!ENTITY zotero.preferences.attachmentBaseDir.basePath "Κατάλογος βάσης:">
|
||||
<!ENTITY zotero.preferences.attachmentBaseDir.selectBasePath "Επιλέξτε...">
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
|
||||
<!ENTITY toolsMenu.label "Εργαλεία">
|
||||
<!ENTITY toolsMenu.accesskey "Τ">
|
||||
<!ENTITY installConnector.label "Install Browser Connector">
|
||||
<!ENTITY installConnector.label "Εγκατάσταση Σύνδεσης με πρόγραμμα πλοήγησης">
|
||||
<!ENTITY installConnector.accesskey "C">
|
||||
<!ENTITY addons.label "Πρόσθετα">
|
||||
|
||||
|
@ -69,18 +69,18 @@
|
|||
<!ENTITY productHelp.accesskey "D">
|
||||
<!ENTITY helpTroubleshootingInfo.label "Πληροφορίες για αντιμετώπιση προβλημάτων">
|
||||
<!ENTITY helpTroubleshootingInfo.accesskey "Τ">
|
||||
<!ENTITY helpFeedbackPage.label "Discussion Forums">
|
||||
<!ENTITY helpFeedbackPage.label "Φόρουμ συζήτησης">
|
||||
<!ENTITY helpFeedbackPage.accesskey "F">
|
||||
|
||||
<!ENTITY helpReportErrors.label "Report Errors…">
|
||||
<!ENTITY helpReportErrors.label "Αναφορά Λαθών...">
|
||||
<!ENTITY helpReportErrors.accesskey "R">
|
||||
|
||||
<!ENTITY debugOutputLogging.label "Debug Output Logging">
|
||||
<!ENTITY debugOutputLogging.label "Αποσφαλμάτωση Καταγραφή Εξόδου">
|
||||
<!ENTITY debugOutputLogging.accesskey "L">
|
||||
<!ENTITY debugOutputLogging.submit "Submit Output">
|
||||
<!ENTITY debugOutputLogging.view "View Output">
|
||||
<!ENTITY debugOutputLogging.clear "Clear Output">
|
||||
<!ENTITY debugOutputLogging.restartWithLoggingEnabled "Restart with Logging Enabled…">
|
||||
<!ENTITY debugOutputLogging.submit "Υποβολή Εξόδου">
|
||||
<!ENTITY debugOutputLogging.view "Επισκόπηση Εξόδου">
|
||||
<!ENTITY debugOutputLogging.clear "Καθαρισμός Εξόδου">
|
||||
<!ENTITY debugOutputLogging.restartWithLoggingEnabled "Επανεκκίνηση με καταγραφή...">
|
||||
|
||||
<!ENTITY helpCheckForUpdates.label "Έλεγχος για ενημερώσεις...">
|
||||
<!ENTITY helpCheckForUpdates.accesskey "U">
|
||||
|
|
|
@ -10,12 +10,14 @@
|
|||
<!ENTITY zotero.general.cancel "Ακύρωση">
|
||||
<!ENTITY zotero.general.refresh "Ανανέωση">
|
||||
<!ENTITY zotero.general.saveAs "Αποθήκευση ως...">
|
||||
<!ENTITY zotero.general.options "Options">
|
||||
<!ENTITY zotero.general.advancedOptions.label "Προχωρημένες επιλογές">
|
||||
<!ENTITY zotero.general.tools "Tools">
|
||||
<!ENTITY zotero.general.more "Περισσότερα">
|
||||
<!ENTITY zotero.general.loading "Loading…">
|
||||
<!ENTITY zotero.general.close "Close">
|
||||
<!ENTITY zotero.general.minimize "Minimize">
|
||||
<!ENTITY zotero.general.other "Other…">
|
||||
|
||||
|
||||
<!ENTITY zotero.errorReport.title "Αναφορά σφάλματος Zotero">
|
||||
|
@ -175,7 +177,7 @@
|
|||
<!ENTITY zotero.tagColorChooser.setColor "Καθορισμός χρώματος">
|
||||
<!ENTITY zotero.tagColorChooser.removeColor "Απομάκρυνση χρώματος">
|
||||
|
||||
<!ENTITY zotero.lookup.description "Εισάγετε στο παρακάτω πεδίο ένα ή περισσότερα ISBN, DOI ή PMID για αναζήτηση.">
|
||||
<!ENTITY zotero.lookup.description "Enter ISBNs, DOIs, PMIDs, or arXiv IDs to add to your library:">
|
||||
<!ENTITY zotero.lookup.button.search "Αναζήτηση">
|
||||
|
||||
<!ENTITY zotero.selectitems.title "Επιλέξτε στοιχεία">
|
||||
|
@ -202,6 +204,15 @@
|
|||
|
||||
<!ENTITY zotero.progress.title "Πρόοδος">
|
||||
|
||||
<!ENTITY zotero.import "Import">
|
||||
<!ENTITY zotero.import.whereToImportFrom "Where do you want to import from?">
|
||||
<!ENTITY zotero.import.source.file "A file (BibTeX, RIS, Zotero RDF, etc.)">
|
||||
<!ENTITY zotero.import.importing "Importing…">
|
||||
<!ENTITY zotero.import.database "Database">
|
||||
<!ENTITY zotero.import.lastModified "Last Modified">
|
||||
<!ENTITY zotero.import.size "Size">
|
||||
<!ENTITY zotero.import.createCollection "Place imported collections and items into new collection">
|
||||
|
||||
<!ENTITY zotero.exportOptions.title "Export...">
|
||||
<!ENTITY zotero.exportOptions.format.label "Μορφή:">
|
||||
<!ENTITY zotero.exportOptions.translatorOptions.label "Επιλογές μεταφραστή">
|
||||
|
|
|
@ -5,14 +5,14 @@ general.error=Σφάλμα
|
|||
general.warning=Προειδοποίηση
|
||||
general.dontShowWarningAgain=Να μην εμφανιστεί ξανά η προειδοποίηση αυτή.
|
||||
general.browserIsOffline=Ο %S αυτή τη στιγμή είναι εκτός σύνδεσης.
|
||||
general.locate=Locate...
|
||||
general.locate=Θέση...
|
||||
general.restartRequired=Απαιτείται επανεκκίνηση
|
||||
general.restartRequiredForChange=%S πρέπει να επανεκκινήσει για να εφαρμοσθούν οι αλλαγές.
|
||||
general.restartRequiredForChanges=%S must be restarted for the changes to take effect.
|
||||
general.restartRequiredForChange=%S πρέπει να γίνει επανεκκινήση για να εφαρμοσθεί η αλλαγή.
|
||||
general.restartRequiredForChanges=%S πρέπει να γίνει επανεκκινήση για να εφαρμοσθούν οι αλλαγές.
|
||||
general.restartNow=Επανεκκίνηση τώρα
|
||||
general.restartLater=Επανεκκίνηση αργότερα
|
||||
general.restartApp=Restart %S
|
||||
general.quitApp=Quit %S
|
||||
general.restartApp=Επανεκκινήση %S
|
||||
general.quitApp=Έξοδος %S
|
||||
general.errorHasOccurred=Παρουσιάστηκε κάποιο σφάλμα.
|
||||
general.unknownErrorOccurred=Παρουσιάστηκε άγνωστο σφάλμα.
|
||||
general.invalidResponseServer=Invalid response from server.
|
||||
|
@ -62,11 +62,13 @@ general.tryLater=Try Later
|
|||
general.showDirectory=Show Directory
|
||||
general.continue=Continue
|
||||
general.copyToClipboard=Copy to Clipboard
|
||||
general.cancel=Cancel
|
||||
general.clear=Clear
|
||||
general.processing=Processing
|
||||
general.submitted=Submitted
|
||||
general.thanksForHelpingImprove=Thanks for helping to improve %S!
|
||||
general.cancel=Άκυρο
|
||||
general.clear=Καθαρισμός
|
||||
general.processing=Επεξεργασία
|
||||
general.submitted=Υποβλήθηκε
|
||||
general.thanksForHelpingImprove=Ευχαριστούμε που βοηθάτε για την βελτίωση %S!
|
||||
general.describeProblem=Περιγράψτε συνοπτικά το πρόβλημα:
|
||||
general.nMegabytes=%S MB
|
||||
|
||||
general.operationInProgress=Αυτή τη στιγμή εκτελείται κάποια λειτουργία Zotero.
|
||||
general.operationInProgress.waitUntilFinished=Παρακαλώ περιμένετε έως ότου ολοκληρωθεί.
|
||||
|
@ -609,7 +611,7 @@ db.integrityCheck.errorsFixed=The errors in your Zotero database have been corre
|
|||
db.integrityCheck.errorsNotFixed=Zotero was unable to correct all the errors in your database.
|
||||
db.integrityCheck.reportInForums=You can report this problem in the Zotero Forums.
|
||||
|
||||
zotero.preferences.chooseFileHandler=Choose File Handler
|
||||
zotero.preferences.chooseApplication=Επιλογή Εφαρμογής
|
||||
|
||||
zotero.preferences.update.updated=Updated
|
||||
zotero.preferences.update.upToDate=Up to date
|
||||
|
@ -686,10 +688,13 @@ fileInterface.importComplete=Import Complete
|
|||
fileInterface.itemsWereImported=%1$S item was imported;%1$S items were imported
|
||||
fileInterface.itemsExported=Exporting items...
|
||||
fileInterface.import=Import
|
||||
fileInterface.chooseAppDatabaseToImport=Επιλέξτε την βάση δεδομένων %S για εισαγωγή
|
||||
fileInterface.export=Export
|
||||
fileInterface.exportedItems=Exported Items
|
||||
fileInterface.imported=Imported
|
||||
fileInterface.unsupportedFormat=The selected file is not in a supported format.
|
||||
fileInterface.appDatabase=%S Βάδη Δεδομένων
|
||||
fileInterface.appImportCollection=%S Import
|
||||
fileInterface.viewSupportedFormats=View Supported Formats…
|
||||
fileInterface.untitledBibliography=Untitled Bibliography
|
||||
fileInterface.bibliographyHTMLTitle=Bibliography
|
||||
|
@ -860,10 +865,14 @@ integration.corruptBibliography.description=All items cited in the text will app
|
|||
integration.citationChanged=You have modified this citation since Zotero generated it. Do you want to keep your modifications and prevent future updates?
|
||||
integration.citationChanged.description=Clicking "Yes" will prevent Zotero from updating this citation if you add additional citations, switch styles, or modify the item to which it refers. Clicking "No" will erase your changes.
|
||||
integration.citationChanged.edit=You have modified this citation since Zotero generated it. Editing will clear your modifications. Do you want to continue?
|
||||
integration.citationChanged.original=Αυθεντικό: %S
|
||||
integration.citationChanged.modified=Τροποποιημένο: %S
|
||||
integration.delayCitationUpdates.alert.text1=Updating citations in this document is taking a long time. Would you like to disable automatic citation updates?
|
||||
integration.delayCitationUpdates.alert.text2=You will need to click Refresh in the Zotero plugin when you are done inserting citations.
|
||||
integration.delayCitationUpdates.alert.text2.toolbar=Θα πρέπει να κάνετε κλικ στην επιλογή Ανανέωση στη γραμμή εργαλείων του Zotero όταν ολοκληρώσετε την εισαγωγή αναφορών.
|
||||
integration.delayCitationUpdates.alert.text2.tab=Θα πρέπει να κάνετε κλικ στην επιλογή Ανανέωση στην καρτέλα Zotero όταν ολοκληρώσετε την εισαγωγή αναφορών.
|
||||
integration.delayCitationUpdates.alert.text3=You can change this setting later in the document preferences.
|
||||
integration.delayCitationUpdates.bibliography=Automatic citation updates are disabled. To see the bibliography, click Refresh in the Zotero plugin.
|
||||
integration.delayCitationUpdates.bibliography.toolbar=Οι ενημερώσεις αυτόματης αναφοράς είναι απενεργοποιημένες. Για να δείτε τη βιβλιογραφία, κάντε κλικ στην επιλογή Ανανέωση στη γραμμή εργαλείων Zotero.
|
||||
integration.delayCitationUpdates.bibliography.tab=Automatic citation updates are disabled. To see the bibliography, click Refresh in the Zotero tab.\nΟι ενημερώσεις αυτόματης αναφοράς είναι απενεργοποιημένες. Για να δείτε τη βιβλιογραφία, κάντε κλικ στην επιλογή Ανανέωση στην καρτέλα Zotero.
|
||||
|
||||
styles.install.title=Install Style
|
||||
styles.install.unexpectedError=An unexpected error occurred while installing "%1$S"
|
||||
|
@ -1058,8 +1067,9 @@ recognizePDF.couldNotRead=Could not read text from 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.reportMetadata=Αναφορά εσφαλμένων Μετα-δεδομένων
|
||||
|
||||
rtfScan.openTitle=Select a file to scan
|
||||
rtfScan.scanning.label=Scanning RTF Document...
|
||||
|
@ -1068,6 +1078,7 @@ rtfScan.rtf=Rich Text Format (.rtf)
|
|||
rtfScan.saveTitle=Select a location in which to save the formatted file
|
||||
rtfScan.scannedFileSuffix=(Scanned)
|
||||
|
||||
extractedAnnotations=Έγινε εξαγωγή Σχολιασμών
|
||||
|
||||
file.accessError.theFileCannotBeCreated=The file '%S' cannot be created.
|
||||
file.accessError.theFileCannotBeUpdated=The file '%S' cannot be updated.
|
||||
|
|
70
chrome/locale/en-US/zotero/connector.json
Normal file
70
chrome/locale/en-US/zotero/connector.json
Normal file
|
@ -0,0 +1,70 @@
|
|||
{
|
||||
"general_saveTo": {
|
||||
"message": "Save to $1"
|
||||
},
|
||||
"general_more": {
|
||||
"message": "More…"
|
||||
},
|
||||
"general_done": {
|
||||
"message": "Done"
|
||||
},
|
||||
"general_tryAgain": {
|
||||
"message": "Try Again"
|
||||
},
|
||||
"general_cancel": {
|
||||
"message": "Cancel"
|
||||
},
|
||||
|
||||
"progressWindow_savingTo": {
|
||||
"message": "Saving to"
|
||||
},
|
||||
"progressWindow_tagPlaceholder": {
|
||||
"message": "Tags (separated by commas)"
|
||||
},
|
||||
"progressWindow_error_translation": {
|
||||
"message": "An error occurred while saving this item. See $1 for more information."
|
||||
},
|
||||
"progressWindow_error_troubleshootingTranslatorIssues": {
|
||||
"message": "Troubleshooting Translator Issues"
|
||||
},
|
||||
"progressWindow_error_fallback": {
|
||||
"message": "An error occurred saving with $1. Attempting to save using $2 instead.",
|
||||
"description": "The placeholders will contain the names of Zotero translators (e.g., JSTOR or Embedded Metadata)."
|
||||
},
|
||||
"progressWindow_error_upgradeClient": {
|
||||
"message": "This feature is not supported by your version of $1. Please upgrade to the $2.",
|
||||
"description": "$2 will be a link with the localized text 'latest version'."
|
||||
},
|
||||
"progressWindow_error_upgradeClient_latestVersion": {
|
||||
"message": "latest version"
|
||||
},
|
||||
|
||||
"appConnector": {
|
||||
"message": "$1 Connector"
|
||||
},
|
||||
"upgradeApp": {
|
||||
"message": "Upgrade $1"
|
||||
},
|
||||
|
||||
"error_connection_isAppRunning": {
|
||||
"message": "Is $1 Running?"
|
||||
},
|
||||
"error_connection_save": {
|
||||
"message": "The $1 was unable to communicate with the $2 desktop application. The Connector can save some pages directly to your $3 account, but for best results you should make sure $2 is open before attempting to save.",
|
||||
"description": "$1 will contain the localized string 'Zotero Connector'. $2 will contain the app name. $3 will contain the domain name."
|
||||
},
|
||||
"error_connection_downloadOrTroubleshoot": {
|
||||
"message": "You can <a href=\"$1\">download $2</a> or <a href=\"$3\">troubleshoot the connection</a> if necessary."
|
||||
},
|
||||
"error_connection_enableSavingToOnlineLibrary": {
|
||||
"message": "Enable Saving to Online Library"
|
||||
},
|
||||
|
||||
"integration_error_clientUpgrade": {
|
||||
"message": "Web-based citing requires $1 or later."
|
||||
},
|
||||
"integration_error_connection": {
|
||||
"message": "The $1 was unable to communicate with the $2 desktop application. $2 must be open to use web-based citing.",
|
||||
"description": "$1 will be the localized string for the extension (e.g., Zotero Connector). $2 will be the app name (e.g., Zotero)."
|
||||
}
|
||||
}
|
|
@ -10,12 +10,14 @@
|
|||
<!ENTITY zotero.general.cancel "Cancel">
|
||||
<!ENTITY zotero.general.refresh "Refresh">
|
||||
<!ENTITY zotero.general.saveAs "Save As…">
|
||||
<!ENTITY zotero.general.options "Options">
|
||||
<!ENTITY zotero.general.advancedOptions.label "Advanced Options">
|
||||
<!ENTITY zotero.general.tools "Tools">
|
||||
<!ENTITY zotero.general.more "More">
|
||||
<!ENTITY zotero.general.loading "Loading…">
|
||||
<!ENTITY zotero.general.close "Close">
|
||||
<!ENTITY zotero.general.minimize "Minimize">
|
||||
<!ENTITY zotero.general.other "Other…">
|
||||
|
||||
|
||||
<!ENTITY zotero.errorReport.title "Zotero Error Report">
|
||||
|
@ -175,7 +177,7 @@
|
|||
<!ENTITY zotero.tagColorChooser.setColor "Set Color">
|
||||
<!ENTITY zotero.tagColorChooser.removeColor "Remove Color">
|
||||
|
||||
<!ENTITY zotero.lookup.description "Enter one or more ISBNs, DOIs, or PMIDs to look up in the box below.">
|
||||
<!ENTITY zotero.lookup.description "Enter ISBNs, DOIs, PMIDs, or arXiv IDs to add to your library:">
|
||||
<!ENTITY zotero.lookup.button.search "Search">
|
||||
|
||||
<!ENTITY zotero.selectitems.title "Select Items">
|
||||
|
@ -202,6 +204,15 @@
|
|||
|
||||
<!ENTITY zotero.progress.title "Progress">
|
||||
|
||||
<!ENTITY zotero.import "Import">
|
||||
<!ENTITY zotero.import.whereToImportFrom "Where do you want to import from?">
|
||||
<!ENTITY zotero.import.source.file "A file (BibTeX, RIS, Zotero RDF, etc.)">
|
||||
<!ENTITY zotero.import.importing "Importing…">
|
||||
<!ENTITY zotero.import.database "Database">
|
||||
<!ENTITY zotero.import.lastModified "Last Modified">
|
||||
<!ENTITY zotero.import.size "Size">
|
||||
<!ENTITY zotero.import.createCollection "Place imported collections and items into new collection">
|
||||
|
||||
<!ENTITY zotero.exportOptions.title "Export…">
|
||||
<!ENTITY zotero.exportOptions.format.label "Format:">
|
||||
<!ENTITY zotero.exportOptions.translatorOptions.label "Translator Options">
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user