mirror of
https://github.com/zotero/zotero.git
synced 2026-10-11 03:38:25 +00:00
Compare commits
436 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cddd989159 | ||
|
|
d6418c443f | ||
|
|
ffb1cc0c94 | ||
|
|
dd98c5b2b1 | ||
|
|
d05fd69fc2 | ||
|
|
0633f0191b | ||
|
|
72850b49dc | ||
|
|
32e50d89be | ||
|
|
9c9936a88a | ||
|
|
16ea4da9b2 | ||
|
|
9e4986abf5 | ||
|
|
a8a691fddb | ||
|
|
e19ab9b30b | ||
|
|
8f3f3c9b86 | ||
|
|
117bc45ed4 | ||
|
|
4db311eb46 | ||
|
|
fb148497ba | ||
|
|
6f506bbc3d | ||
|
|
78865ed2ef | ||
|
|
0ad55a6a70 | ||
|
|
4f7583e1c0 | ||
|
|
48c7c60bf1 | ||
|
|
266060dc48 | ||
|
|
9515912830 | ||
|
|
078626be82 | ||
|
|
4b61954e52 | ||
|
|
0d6678a735 | ||
|
|
2b9455670f | ||
|
|
1b3caadd20 | ||
|
|
c9381f307c | ||
|
|
c9c7805cdf | ||
|
|
3e12f3f20b | ||
|
|
2f0d41c0cb | ||
|
|
00e63f0f8a | ||
|
|
8096e22f40 | ||
|
|
e59e6cdcd8 | ||
|
|
fcc6cc17a9 | ||
|
|
8992efc869 | ||
|
|
01ebeb1834 | ||
|
|
bed256318a | ||
|
|
860e901240 | ||
|
|
9ce8855fa4 | ||
|
|
ecbee6ee26 | ||
|
|
fe8308c9dd | ||
|
|
90589606c2 | ||
|
|
77e0c71c74 | ||
|
|
ac2c001291 | ||
|
|
c641b28a9f | ||
|
|
31fc418a97 | ||
|
|
b213b8f15d | ||
|
|
7e6f7ac4b3 | ||
|
|
5327616376 | ||
|
|
9a7486e53c | ||
|
|
1d6cae08d2 | ||
|
|
ce16a28f4a | ||
|
|
400396be25 | ||
|
|
41c0d9346e | ||
|
|
543bfdabd3 | ||
|
|
dcfce382d9 | ||
|
|
5ad5598089 | ||
|
|
a66db9b52b | ||
|
|
49b08df0dd | ||
|
|
2e4fed4000 | ||
|
|
db99d4cfe1 | ||
|
|
85c35a2270 | ||
|
|
3cb5837bc6 | ||
|
|
7b614962c6 | ||
|
|
c8109fef18 | ||
|
|
1497524bab | ||
|
|
cd88d1f661 | ||
|
|
23ebf277fe | ||
|
|
42a4649420 | ||
|
|
778fba60cb | ||
|
|
bba09658e1 | ||
|
|
b298a48cda | ||
|
|
8a1dd3637c | ||
|
|
e024449732 | ||
|
|
14bb46f434 | ||
|
|
bc07bf803c | ||
|
|
367dfdf969 | ||
|
|
c57c3c470e | ||
|
|
a1d06066b2 | ||
|
|
1f45c44b86 | ||
|
|
8ac77bf9f0 | ||
|
|
59ba212cf8 | ||
|
|
935c67bc01 | ||
|
|
adf6f9f2fb | ||
|
|
bdadd363d3 | ||
|
|
c3cde7784d | ||
|
|
d7a8b2bb87 | ||
|
|
4ae4d98417 | ||
|
|
a9f1526bfc | ||
|
|
f868f4f713 | ||
|
|
f3c0c01fe1 | ||
|
|
0bd39ef42e | ||
|
|
a78920b16f | ||
|
|
d866a10a2b | ||
|
|
0f9024dc98 | ||
|
|
d8f7626dd4 | ||
|
|
c8da33d19c | ||
|
|
0f63b2e654 | ||
|
|
19be7b9b70 | ||
|
|
52a1981747 | ||
|
|
19e1b231de | ||
|
|
b956d9b429 | ||
|
|
962cc23d1a | ||
|
|
bbf72bfbcf | ||
|
|
b924720a2d | ||
|
|
34bb51f58a | ||
|
|
3aae80ca7d | ||
|
|
ce60ce90f6 | ||
|
|
6d0e5959e6 | ||
|
|
a5fb64f295 | ||
|
|
87fbf3c7fb | ||
|
|
c0bc2bafed | ||
|
|
886500812e | ||
|
|
4a06053d72 | ||
|
|
0ffe3122c2 | ||
|
|
1f721feab7 | ||
|
|
9f53b48984 | ||
|
|
9dad14c244 | ||
|
|
6374aea1c8 | ||
|
|
be6eee5412 | ||
|
|
5919f0e77a | ||
|
|
f006995676 | ||
|
|
665473b108 | ||
|
|
21777af980 | ||
|
|
4fbb89a588 | ||
|
|
ff38ff5b9d | ||
|
|
9d2c7d44f8 | ||
|
|
3678639273 | ||
|
|
52a079044d | ||
|
|
7e61be9799 | ||
|
|
0862e1e1bc | ||
|
|
f6dd47dd1f | ||
|
|
86c56951df | ||
|
|
51762e1c41 | ||
|
|
5697f12310 | ||
|
|
4184dc18d2 | ||
|
|
da392518ed | ||
|
|
1a83b472e8 | ||
|
|
7262465109 | ||
|
|
91c385a67a | ||
|
|
48ad7c0ab7 | ||
|
|
2082b68dd3 | ||
|
|
dcd6cdbcb3 | ||
|
|
d27fc31ef0 | ||
|
|
95e46ac8a2 | ||
|
|
41b0e64109 | ||
|
|
77eb325b26 | ||
|
|
a9dc25decc | ||
|
|
df3856f735 | ||
|
|
6a0ecd243d | ||
|
|
1422d84bd6 | ||
|
|
3d8bbb3e55 | ||
|
|
f33cf1cde3 | ||
|
|
deb5d1cd27 | ||
|
|
29006c556d | ||
|
|
2db90511f9 | ||
|
|
8135895313 | ||
|
|
dfdc66a41c | ||
|
|
1106685f2d | ||
|
|
deb17d450e | ||
|
|
89bc094690 | ||
|
|
1a1c08890f | ||
|
|
a48e0f2437 | ||
|
|
ac4308b3e7 | ||
|
|
c3c0b363c1 | ||
|
|
ae14d433f9 | ||
|
|
b91d85f288 | ||
|
|
26847c672f | ||
|
|
a505c94751 | ||
|
|
52176dc30a | ||
|
|
0e12126436 | ||
|
|
d131fe3dc9 | ||
|
|
c2941738e6 | ||
|
|
0aae101196 | ||
|
|
02098a9a34 | ||
|
|
378d8180e8 | ||
|
|
e3d13826c8 | ||
|
|
6fc5b037d9 | ||
|
|
6a4a329ec6 | ||
|
|
656acf3908 | ||
|
|
1920a5efd7 | ||
|
|
13ab3de1a1 | ||
|
|
47610d0faa | ||
|
|
42e042c6c6 | ||
|
|
b9a5fd9f78 | ||
|
|
cbb524d257 | ||
|
|
93ea818bf2 | ||
|
|
d2243cc339 | ||
|
|
f06267e4cd | ||
|
|
624101832e | ||
|
|
6bc1551f7f | ||
|
|
a8a0cabcc4 | ||
|
|
9d80c54d5d | ||
|
|
1c8554d527 | ||
|
|
5cea183ae3 | ||
|
|
92434dbb78 | ||
|
|
2c5a91682e | ||
|
|
3cc54eaf95 | ||
|
|
7f91a0c1a8 | ||
|
|
77fdc8e40c | ||
|
|
c5a769285b | ||
|
|
fe09afa0a6 | ||
|
|
ab2bec936e | ||
|
|
af66712c77 | ||
|
|
7db16843b3 | ||
|
|
a4eafd0307 | ||
|
|
4e1754b960 | ||
|
|
238c7ce410 | ||
|
|
8a15e5abe0 | ||
|
|
159c112257 | ||
|
|
70f9462913 | ||
|
|
4b5e2eae1b | ||
|
|
c4876fd5ae | ||
|
|
7fcc1d35a4 | ||
|
|
d0d3ed731d | ||
|
|
f2bfaeb7a9 | ||
|
|
2fa67bae64 | ||
|
|
a2db01c633 | ||
|
|
2b9e9c174f | ||
|
|
aa0ef7012c | ||
|
|
73306ee5fb | ||
|
|
d8d6054927 | ||
|
|
410ebc288b | ||
|
|
55095ba354 | ||
|
|
3ba6343668 | ||
|
|
d37aa37629 | ||
|
|
3582405f55 | ||
|
|
78eb857e73 | ||
|
|
618e71b5f1 | ||
|
|
7bada8ce0c | ||
|
|
af436d34a8 | ||
|
|
e45becce2a | ||
|
|
fb4f7b7932 | ||
|
|
fd8aa4433c | ||
|
|
29dd0cf5dc | ||
|
|
28329aa785 | ||
|
|
c5d418fc16 | ||
|
|
e9c6c76e79 | ||
|
|
f13966894f | ||
|
|
b7cc1d549b | ||
|
|
9a3f8dbc94 | ||
|
|
b594ad0b0a | ||
|
|
4dd1851140 | ||
|
|
006dd20d5b | ||
|
|
55afe7c08d | ||
|
|
9fadfac7ee | ||
|
|
38ae3a2600 | ||
|
|
473dd2ed64 | ||
|
|
e3b1cbd4eb | ||
|
|
3b93147052 | ||
|
|
b9a1e93518 | ||
|
|
99084084d4 | ||
|
|
64047f181a | ||
|
|
12b1897478 | ||
|
|
8fc9ebe4ab | ||
|
|
5162845179 | ||
|
|
2569ad6506 | ||
|
|
4fdf43ed92 | ||
|
|
9691fcafc2 | ||
|
|
6aad0cbb9c | ||
|
|
a7e6fc330c | ||
|
|
677499865a | ||
|
|
0e60389ee4 | ||
|
|
2271913e49 | ||
|
|
ff5e09201d | ||
|
|
0b89b15f66 | ||
|
|
f6e11981e3 | ||
|
|
230fc7f2df | ||
|
|
f179f0a54f | ||
|
|
ea09aac616 | ||
|
|
4a49ffec0a | ||
|
|
0d09618865 | ||
|
|
b96fcd41b1 | ||
|
|
c13d17b5e6 | ||
|
|
eb255c5868 | ||
|
|
88217492f9 | ||
|
|
64897455f4 | ||
|
|
5015b0e4cc | ||
|
|
a2d378448a | ||
|
|
32b64b3cf1 | ||
|
|
66fccec6b4 | ||
|
|
894309d061 | ||
|
|
b24c7577ec | ||
|
|
ca4ced1e9f | ||
|
|
6646be66d0 | ||
|
|
ef0811bd4c | ||
|
|
f893206dfd | ||
|
|
ae5de2c2f5 | ||
|
|
a3b45a74e9 | ||
|
|
e474ca524b | ||
|
|
bc0f6203e1 | ||
|
|
91ae576074 | ||
|
|
c255104ada | ||
|
|
75f5d6aca1 | ||
|
|
d156bef42a | ||
|
|
79246196ac | ||
|
|
262e1964ae | ||
|
|
f4dfea6849 | ||
|
|
999f78b0f5 | ||
|
|
2e6321a4bf | ||
|
|
35a1bb20cb | ||
|
|
8c80ea431e | ||
|
|
3abd720957 | ||
|
|
8379540719 | ||
|
|
b373291c02 | ||
|
|
3391f3bc99 | ||
|
|
6ca854a018 | ||
|
|
fa32eb6790 | ||
|
|
96c4427d6c | ||
|
|
5a8b5a1829 | ||
|
|
f368a53311 | ||
|
|
49020b02a2 | ||
|
|
20c6fe6737 | ||
|
|
80351a496b | ||
|
|
2d2e787111 | ||
|
|
eaba3ae099 | ||
|
|
4746300d33 | ||
|
|
85b0c9dbad | ||
|
|
318e4852e9 | ||
|
|
3dc3359cff | ||
|
|
b505c630d3 | ||
|
|
21b7baee35 | ||
|
|
df59cee94a | ||
|
|
d1275f9964 | ||
|
|
d4a7de2e8d | ||
|
|
864fd03c87 | ||
|
|
d3cb7c86d9 | ||
|
|
e4e3c72036 | ||
|
|
0857cefb0c | ||
|
|
9b7b4fe134 | ||
|
|
a9c3fc7790 | ||
|
|
20d0f103fd | ||
|
|
20eca3781d | ||
|
|
87455d28c3 | ||
|
|
2048319541 | ||
|
|
d03b0a716d | ||
|
|
bb2b37a73a | ||
|
|
7ec54fdaa9 | ||
|
|
5a659b070d | ||
|
|
ad96323881 | ||
|
|
fc0f6157d0 | ||
|
|
6733f2edca | ||
|
|
e1997620a4 | ||
|
|
a396771cbc | ||
|
|
0e0997078b | ||
|
|
0785b3a086 | ||
|
|
ba5cb7d5c6 | ||
|
|
d822da4c06 | ||
|
|
a297692487 | ||
|
|
6921268dc0 | ||
|
|
ba1906c054 | ||
|
|
b5be7cbd57 | ||
|
|
5c61815f36 | ||
|
|
b5ca5aed3a | ||
|
|
4b6598acc4 | ||
|
|
14a6ce000e | ||
|
|
9d0cd005f8 | ||
|
|
26db75971e | ||
|
|
cc45e9019b | ||
|
|
98d6f40e4a | ||
|
|
2cad5b00d1 | ||
|
|
95239db0fd | ||
|
|
4c063e7b6e | ||
|
|
07f1517712 | ||
|
|
f0e0971c69 | ||
|
|
acba90f272 | ||
|
|
26a5e2900a | ||
|
|
34d66381d1 | ||
|
|
f7dbcdde4b | ||
|
|
c4563af381 | ||
|
|
56e7f0a10f | ||
|
|
3a160b2630 | ||
|
|
124c18d54d | ||
|
|
57c582e171 | ||
|
|
cf8819cc06 | ||
|
|
fe74b02d28 | ||
|
|
1dcc2728dd | ||
|
|
c5dbf0169f | ||
|
|
3f282fc25f | ||
|
|
7163a196f8 | ||
|
|
8c0499468e | ||
|
|
629892b0b8 | ||
|
|
2ff67c987e | ||
|
|
a8ed30ce80 | ||
|
|
43f15e7517 | ||
|
|
df11227e0a | ||
|
|
d418d79e70 | ||
|
|
42aebccbf4 | ||
|
|
7b6e19435b | ||
|
|
f650f95081 | ||
|
|
0357026865 | ||
|
|
92d89becc7 | ||
|
|
49f9642c0d | ||
|
|
94372111f5 | ||
|
|
f0b896d282 | ||
|
|
ec68216310 | ||
|
|
b215d93ac9 | ||
|
|
bf1480f8f0 | ||
|
|
6f089445a5 | ||
|
|
14e62ee34f | ||
|
|
43c86c0e58 | ||
|
|
6f3f5d2ea8 | ||
|
|
4fb0661b50 | ||
|
|
57ab92b5b9 | ||
|
|
7b20972c27 | ||
|
|
dc045250a6 | ||
|
|
256bd157ed | ||
|
|
2b45b7fea3 | ||
|
|
1d5fd1308a | ||
|
|
d8069fac50 | ||
|
|
18516a33d9 | ||
|
|
b658f09dd8 | ||
|
|
5799bdd554 | ||
|
|
62c7b4db2b | ||
|
|
caa0b05e5f | ||
|
|
5095543bef | ||
|
|
6af3b0276a | ||
|
|
67700f8a2c | ||
|
|
d12f3eda61 | ||
|
|
deabf3f042 | ||
|
|
28ed3e34b5 | ||
|
|
354183c25d | ||
|
|
b0f69ef7e3 | ||
|
|
ebc23d73b0 | ||
|
|
419f74379b | ||
|
|
603b843f71 | ||
|
|
1e3959020d | ||
|
|
3fd0abf6b0 | ||
|
|
111a254696 | ||
|
|
69616500d8 | ||
|
|
693f9a7e1b | ||
|
|
0c0afb129b | ||
|
|
a11c5987ad |
420 changed files with 19110 additions and 7711 deletions
17
.github/workflows/ci.yml
vendored
17
.github/workflows/ci.yml
vendored
|
|
@ -5,14 +5,14 @@ jobs:
|
||||||
name: Build, Upload, Test
|
name: Build, Upload, Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- name: Install Node
|
- name: Install Node
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 14
|
node-version: 18
|
||||||
#cache: npm
|
#cache: npm
|
||||||
|
|
||||||
# On GitHub
|
# On GitHub
|
||||||
|
|
@ -27,7 +27,7 @@ jobs:
|
||||||
|
|
||||||
- name: Cache Firefox
|
- name: Cache Firefox
|
||||||
id: firefox-cache
|
id: firefox-cache
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: firefox-x86_64
|
path: firefox-x86_64
|
||||||
key: firefox-60.9.0esr
|
key: firefox-60.9.0esr
|
||||||
|
|
@ -42,7 +42,7 @@ jobs:
|
||||||
|
|
||||||
- name: Cache Node modules
|
- name: Cache Node modules
|
||||||
id: node-cache
|
id: node-cache
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: node_modules
|
path: node_modules
|
||||||
key: node-modules-${{ hashFiles('package-lock.json') }}
|
key: node-modules-${{ hashFiles('package-lock.json') }}
|
||||||
|
|
@ -53,13 +53,16 @@ jobs:
|
||||||
|
|
||||||
- name: Build Zotero
|
- name: Build Zotero
|
||||||
run: npm run build
|
run: npm run build
|
||||||
|
# Currently necessary for pdf-worker Webpack: https://stackoverflow.com/a/69746937
|
||||||
|
env:
|
||||||
|
NODE_OPTIONS: --openssl-legacy-provider
|
||||||
|
|
||||||
- name: Upload deployment ZIP
|
- name: Upload deployment ZIP
|
||||||
if: |
|
if: |
|
||||||
env.ACT != 'true'
|
env.ACT != 'true'
|
||||||
&& github.repository == 'zotero/zotero'
|
&& github.repository == 'zotero/zotero'
|
||||||
&& github.event_name == 'push'
|
&& github.event_name == 'push'
|
||||||
&& (github.ref == 'refs/heads/master' || endsWith(github.ref, '-hotfix') || github.ref == 'refs/heads/gh-actions-ci-test')
|
&& (github.ref == 'refs/heads/6.0' || endsWith(github.ref, '-hotfix') || github.ref == 'refs/heads/gh-actions-ci-test')
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
|
@ -76,7 +79,7 @@ jobs:
|
||||||
|
|
||||||
- name: Cache utilities Node modules
|
- name: Cache utilities Node modules
|
||||||
id: utilities-node-cache
|
id: utilities-node-cache
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: chrome/content/zotero/xpcom/utilities/node_modules
|
path: chrome/content/zotero/xpcom/utilities/node_modules
|
||||||
key: utilities-node-modules-${{ hashFiles('chrome/content/zotero/xpcom/utilities/package-lock.json') }}
|
key: utilities-node-modules-${{ hashFiles('chrome/content/zotero/xpcom/utilities/package-lock.json') }}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
## Bug Reports and Feature Requests
|
## Bug Reports and Feature Requests
|
||||||
|
|
||||||
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. See [How Zotero Support Works](https://www.zotero.org/support/zotero_support) for more information.
|
Zotero does not use GitHub Issues for bug reports, feature requests, or support questions. Please post all such requests to the [Zotero Forums](https://forums.zotero.org), where Zotero developers and many others can help. See [How Zotero Support Works](https://www.zotero.org/support/zotero_support) for more information.
|
||||||
|
|
||||||
For confirmed bugs or agreed-upon changes, Zotero developers will create new issues in the relevant repositories.
|
For confirmed bugs or agreed-upon changes, Zotero developers will create new issues in the relevant repositories.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,9 @@ content zotero-platform chrome/content/zotero-platform/unix/ os=FreeBSD
|
||||||
content zotero-platform chrome/content/zotero-platform/unix/ os=OpenBSD
|
content zotero-platform chrome/content/zotero-platform/unix/ os=OpenBSD
|
||||||
|
|
||||||
content zotero-platform-version chrome/content/zotero-platform/default-version/
|
content zotero-platform-version chrome/content/zotero-platform/default-version/
|
||||||
# Big Sur shows up as 10.16 when linked against 10.11
|
# Big Sur and later show up as 10.16 when linked against 10.11
|
||||||
content zotero-platform-version chrome/content/zotero-platform/mac-big-sur/ os=Darwin osversion=10.16
|
# (Usually: https://forums.zotero.org/discussion/98406/zotero-will-not-open-mac-os-monterey)
|
||||||
|
content zotero-platform-version chrome/content/zotero-platform/mac-big-sur/ os=Darwin osversion>=10.16
|
||||||
#content zotero-platform-version chrome/content/zotero-platform/mac-big-sur/ os=Darwin
|
#content zotero-platform-version chrome/content/zotero-platform/mac-big-sur/ os=Darwin
|
||||||
|
|
||||||
resource zotero resource/
|
resource zotero resource/
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ var Scaffold = new function () {
|
||||||
var _translatorsLoadedPromise;
|
var _translatorsLoadedPromise;
|
||||||
var _translatorProvider = null;
|
var _translatorProvider = null;
|
||||||
var _lastModifiedTime = 0;
|
var _lastModifiedTime = 0;
|
||||||
|
var _needRebuildTranslatorSuggestions = true;
|
||||||
|
|
||||||
var _editors = {};
|
var _editors = {};
|
||||||
|
|
||||||
|
|
@ -99,15 +100,6 @@ var Scaffold = new function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set font size from general pref
|
|
||||||
Zotero.setFontSize(document.getElementById('scaffold-pane'));
|
|
||||||
|
|
||||||
// Set font size of code editor
|
|
||||||
var size = Zotero.Prefs.get("scaffold.fontSize");
|
|
||||||
if (size) {
|
|
||||||
this.setFontSize(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.generateTranslatorID();
|
this.generateTranslatorID();
|
||||||
|
|
||||||
// Add List fields help menu entries for all other item types
|
// Add List fields help menu entries for all other item types
|
||||||
|
|
@ -146,6 +138,15 @@ var Scaffold = new function () {
|
||||||
this.initCodeEditor();
|
this.initCodeEditor();
|
||||||
this.initTestsEditor();
|
this.initTestsEditor();
|
||||||
|
|
||||||
|
// Set font size from general pref
|
||||||
|
Zotero.setFontSize(document.getElementById('scaffold-pane'));
|
||||||
|
|
||||||
|
// Set font size of code editor
|
||||||
|
var size = Zotero.Prefs.get("scaffold.fontSize");
|
||||||
|
if (size) {
|
||||||
|
this.setFontSize(size);
|
||||||
|
}
|
||||||
|
|
||||||
// Listen for Scaffold coming to the foreground and reload translators
|
// Listen for Scaffold coming to the foreground and reload translators
|
||||||
window.addEventListener('activate', () => this.reloadTranslators());
|
window.addEventListener('activate', () => this.reloadTranslators());
|
||||||
|
|
||||||
|
|
@ -156,6 +157,7 @@ var Scaffold = new function () {
|
||||||
|
|
||||||
onLoadComplete: () => {
|
onLoadComplete: () => {
|
||||||
document.getElementById('cmd_load').removeAttribute('disabled');
|
document.getElementById('cmd_load').removeAttribute('disabled');
|
||||||
|
_needRebuildTranslatorSuggestions = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -267,6 +269,7 @@ var Scaffold = new function () {
|
||||||
|
|
||||||
monaco.languages.registerCodeLensProvider('javascript', this.createRunCodeLensProvider(monaco, editor));
|
monaco.languages.registerCodeLensProvider('javascript', this.createRunCodeLensProvider(monaco, editor));
|
||||||
monaco.languages.registerHoverProvider('javascript', this.createHoverProvider(monaco, editor));
|
monaco.languages.registerHoverProvider('javascript', this.createHoverProvider(monaco, editor));
|
||||||
|
monaco.languages.registerCompletionItemProvider('javascript', this.createCompletionProvider(monaco, editor));
|
||||||
|
|
||||||
let tsLib = await Zotero.File.getContentsAsync(
|
let tsLib = await Zotero.File.getContentsAsync(
|
||||||
OS.Path.join(Scaffold_Translators.getDirectory(), 'index.d.ts'));
|
OS.Path.join(Scaffold_Translators.getDirectory(), 'index.d.ts'));
|
||||||
|
|
@ -501,6 +504,43 @@ var Scaffold = new function () {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.createCompletionProvider = function (monaco, editor) {
|
||||||
|
let suggestions = null;
|
||||||
|
return {
|
||||||
|
provideCompletionItems(model, position) {
|
||||||
|
let prefixText = model.getValueInRange({
|
||||||
|
startLineNumber: position.lineNumber,
|
||||||
|
startColumn: 1,
|
||||||
|
endLineNumber: position.lineNumber,
|
||||||
|
endColumn: position.column
|
||||||
|
});
|
||||||
|
if (/setTranslator\([^)]*$/.test(prefixText)) {
|
||||||
|
let word = model.getWordUntilPosition(position);
|
||||||
|
let range = {
|
||||||
|
startLineNumber: position.lineNumber,
|
||||||
|
endLineNumber: position.lineNumber,
|
||||||
|
startColumn: word.startColumn,
|
||||||
|
endColumn: word.endColumn
|
||||||
|
};
|
||||||
|
if (!suggestions || _needRebuildTranslatorSuggestions) {
|
||||||
|
// Cache the suggestions minus the range field
|
||||||
|
suggestions = [...Scaffold_Translators._translators.entries()].map(([id, meta]) => {
|
||||||
|
return {
|
||||||
|
label: `${meta.translator.label}: '${id}'`,
|
||||||
|
kind: monaco.languages.CompletionItemKind.Constant,
|
||||||
|
insertText: `'${id}'`
|
||||||
|
};
|
||||||
|
});
|
||||||
|
_needRebuildTranslatorSuggestions = false;
|
||||||
|
}
|
||||||
|
// Add the range to each suggestion before returning
|
||||||
|
return { suggestions: suggestions.map(s => ({ ...s, range })) };
|
||||||
|
}
|
||||||
|
return { suggestions: [] };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
this.updateModelMarkers = function (translatorPath) {
|
this.updateModelMarkers = function (translatorPath) {
|
||||||
runESLint(translatorPath)
|
runESLint(translatorPath)
|
||||||
.then(eslintOutputToModelMarkers)
|
.then(eslintOutputToModelMarkers)
|
||||||
|
|
@ -802,7 +842,8 @@ var Scaffold = new function () {
|
||||||
var fieldList = Zotero.ItemFields.getItemTypeFields(typeID);
|
var fieldList = Zotero.ItemFields.getItemTypeFields(typeID);
|
||||||
for (let field of fieldList) {
|
for (let field of fieldList) {
|
||||||
var key = Zotero.ItemFields.getName(field);
|
var key = Zotero.ItemFields.getName(field);
|
||||||
outputObject[key] = "";
|
let fieldLocalizedName = Zotero.ItemFields.getLocalizedString(field);
|
||||||
|
outputObject[key] = fieldLocalizedName;
|
||||||
}
|
}
|
||||||
var creatorList = Zotero.CreatorTypes.getTypesForItemType(typeID);
|
var creatorList = Zotero.CreatorTypes.getTypesForItemType(typeID);
|
||||||
var creators = [];
|
var creators = [];
|
||||||
|
|
@ -1097,13 +1138,19 @@ var Scaffold = new function () {
|
||||||
string = fix2028(Zotero.Utilities.varDump(string));
|
string = fix2028(Zotero.Utilities.varDump(string));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output.value) output.value += "\n";
|
// Put off actually building the log message and appending it to the console until the next animation frame
|
||||||
output.value += Zotero.Utilities.lpad(date.getHours(), '0', 2)
|
// so as not to slow down translation with repeated layout recalculations triggered by appending text
|
||||||
|
// and accessing scrollHeight
|
||||||
|
// requestAnimationFrame() callbacks are guaranteed to be called in the order they were set
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
if (output.value) output.value += "\n";
|
||||||
|
output.value += Zotero.Utilities.lpad(date.getHours(), '0', 2)
|
||||||
+ ":" + Zotero.Utilities.lpad(date.getMinutes(), '0', 2)
|
+ ":" + Zotero.Utilities.lpad(date.getMinutes(), '0', 2)
|
||||||
+ ":" + Zotero.Utilities.lpad(date.getSeconds(), '0', 2)
|
+ ":" + Zotero.Utilities.lpad(date.getSeconds(), '0', 2)
|
||||||
+ " " + string.replace(/\n/g, "\n ");
|
+ " " + string.replace(/\n/g, "\n ");
|
||||||
// move to end
|
// move to end
|
||||||
output.inputField.scrollTop = output.inputField.scrollHeight;
|
output.inputField.scrollTop = output.inputField.scrollHeight;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -1259,6 +1306,12 @@ var Scaffold = new function () {
|
||||||
_writeToEditor(_editors.tests, _stringifyTests(tests));
|
_writeToEditor(_editors.tests, _stringifyTests(tests));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _confirmCreateExpectedFailTest() {
|
||||||
|
return Services.prompt.confirm(null,
|
||||||
|
'Detection Failed',
|
||||||
|
'Add test ensuring that detection always fails on this page?');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mimics most of the behavior of Zotero.Item#fromJSON. Most importantly,
|
* Mimics most of the behavior of Zotero.Item#fromJSON. Most importantly,
|
||||||
* extracts valid fields from item.extra and inserts invalid fields into
|
* extracts valid fields from item.extra and inserts invalid fields into
|
||||||
|
|
@ -1475,7 +1528,7 @@ var Scaffold = new function () {
|
||||||
|
|
||||||
if (level < 2 && value.items) {
|
if (level < 2 && value.items) {
|
||||||
// Test object. Arrange properties in set order
|
// Test object. Arrange properties in set order
|
||||||
let order = ['type', 'url', 'input', 'defer', 'items'];
|
let order = ['type', 'url', 'input', 'defer', 'detectedItemType', 'items'];
|
||||||
for (let i = 0; i < order.length; i++) {
|
for (let i = 0; i < order.length; i++) {
|
||||||
let val = processRow(order[i], value[order[i]]);
|
let val = processRow(order[i], value[order[i]]);
|
||||||
if (val === undefined) continue;
|
if (val === undefined) continue;
|
||||||
|
|
@ -1582,14 +1635,16 @@ var Scaffold = new function () {
|
||||||
_translatorProvider
|
_translatorProvider
|
||||||
);
|
);
|
||||||
return new Promise(
|
return new Promise(
|
||||||
(resolve, reject) => tester.newTest(input, function (obj, newTest) { // "done" handler for do
|
(resolve, reject) => tester.newTest(input,
|
||||||
if (newTest) {
|
(obj, newTest) => { // "done" handler for do
|
||||||
resolve(_sanitizeItemsInTest(newTest));
|
if (newTest) {
|
||||||
}
|
resolve(_sanitizeItemsInTest(newTest));
|
||||||
else {
|
}
|
||||||
reject(new Error('Creation failed'));
|
else {
|
||||||
}
|
reject(new Error('Creation failed'));
|
||||||
})
|
}
|
||||||
|
},
|
||||||
|
_confirmCreateExpectedFailTest)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (type == "import" || type == "search") {
|
else if (type == "import" || type == "search") {
|
||||||
|
|
@ -1729,6 +1784,9 @@ var Scaffold = new function () {
|
||||||
var url = item.getElementsByTagName("listcell")[0].getAttribute("label");
|
var url = item.getElementsByTagName("listcell")[0].getAttribute("label");
|
||||||
var test = JSON.parse(item.getUserData("test-string"));
|
var test = JSON.parse(item.getUserData("test-string"));
|
||||||
var urlOrData = (test.input !== undefined) ? test.input : url;
|
var urlOrData = (test.input !== undefined) ? test.input : url;
|
||||||
|
if (typeof urlOrData !== 'string') {
|
||||||
|
urlOrData = JSON.stringify(urlOrData, null, '\t');
|
||||||
|
}
|
||||||
Zotero.Utilities.Internal.copyTextToClipboard(urlOrData);
|
Zotero.Utilities.Internal.copyTextToClipboard(urlOrData);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1903,13 +1961,11 @@ var Scaffold = new function () {
|
||||||
var test = this.testsToUpdate.shift();
|
var test = this.testsToUpdate.shift();
|
||||||
_logOutput("Updating test " + (this.numTestsTotal - this.testsToUpdate.length));
|
_logOutput("Updating test " + (this.numTestsTotal - this.testsToUpdate.length));
|
||||||
|
|
||||||
var me = this;
|
|
||||||
|
|
||||||
if (test.type == 'web') {
|
if (test.type == 'web') {
|
||||||
_logOutput("Loading web page from " + test.url);
|
_logOutput("Loading web page from " + test.url);
|
||||||
var hiddenBrowser = Zotero.HTTP.loadDocuments(
|
var hiddenBrowser = Zotero.HTTP.loadDocuments(
|
||||||
test.url,
|
test.url,
|
||||||
function (doc) {
|
(doc) => {
|
||||||
_logOutput("Page loaded");
|
_logOutput("Page loaded");
|
||||||
if (test.defer) {
|
if (test.defer) {
|
||||||
_logOutput("Waiting " + (Zotero_TranslatorTester.DEFER_DELAY / 1000)
|
_logOutput("Waiting " + (Zotero_TranslatorTester.DEFER_DELAY / 1000)
|
||||||
|
|
@ -1917,32 +1973,34 @@ var Scaffold = new function () {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Zotero.setTimeout(
|
Zotero.setTimeout(
|
||||||
function () {
|
() => {
|
||||||
doc = hiddenBrowser.contentDocument;
|
doc = hiddenBrowser.contentDocument;
|
||||||
if (doc.location.href != test.url) {
|
if (doc.location.href != test.url) {
|
||||||
_logOutput("Page URL differs from test. Will be updated. " + doc.location.href);
|
_logOutput("Page URL differs from test. Will be updated. " + doc.location.href);
|
||||||
}
|
}
|
||||||
me.tester.newTest(doc, function (obj, newTest) {
|
this.tester.newTest(doc,
|
||||||
Zotero.Browser.deleteHiddenBrowser(hiddenBrowser);
|
(obj, newTest) => {
|
||||||
if (test.defer) {
|
Zotero.Browser.deleteHiddenBrowser(hiddenBrowser);
|
||||||
newTest.defer = true;
|
if (test.defer) {
|
||||||
}
|
newTest.defer = true;
|
||||||
newTest = _sanitizeItemsInTest(newTest);
|
}
|
||||||
me.newTests.push(newTest);
|
newTest = _sanitizeItemsInTest(newTest);
|
||||||
me.testDoneCallback(newTest);
|
this.newTests.push(newTest);
|
||||||
me._updateTests();
|
this.testDoneCallback(newTest);
|
||||||
});
|
this._updateTests();
|
||||||
|
},
|
||||||
|
_confirmCreateExpectedFailTest);
|
||||||
},
|
},
|
||||||
test.defer ? Zotero_TranslatorTester.DEFER_DELAY : 0,
|
test.defer ? Zotero_TranslatorTester.DEFER_DELAY : 0,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
function (e) {
|
(e) => {
|
||||||
Zotero.logError(e);
|
Zotero.logError(e);
|
||||||
me.newTests.push(false);
|
this.newTests.push(false);
|
||||||
me.testDoneCallback(false);
|
this.testDoneCallback(false);
|
||||||
me._updateTests();
|
this._updateTests();
|
||||||
},
|
},
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
@ -1960,15 +2018,15 @@ var Scaffold = new function () {
|
||||||
|
|
||||||
// Re-runs the test.
|
// Re-runs the test.
|
||||||
// TranslatorTester doesn't handle these correctly, so we do it manually
|
// TranslatorTester doesn't handle these correctly, so we do it manually
|
||||||
_run(methods[test.type], test.input, null, function (obj, item) {
|
_run(methods[test.type], test.input, null, (obj, item) => {
|
||||||
if (item) {
|
if (item) {
|
||||||
test.items.push(Zotero_TranslatorTester._sanitizeItem(item));
|
test.items.push(Zotero_TranslatorTester._sanitizeItem(item));
|
||||||
}
|
}
|
||||||
}, null, function () {
|
}, null, () => {
|
||||||
if (!test.items.length) test = false;
|
if (!test.items.length) test = false;
|
||||||
me.newTests.push(test);
|
this.newTests.push(test);
|
||||||
me.testDoneCallback(test);
|
this.testDoneCallback(test);
|
||||||
me._updateTests();
|
this._updateTests();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
async function detectWeb(doc, url) {
|
function detectWeb(doc, url) {
|
||||||
// TODO: adjust the logic here
|
// TODO: adjust the logic here
|
||||||
if ($$CURSOR$$url.includes('/article/')) {
|
if ($$CURSOR$$url.includes('/article/')) {
|
||||||
return 'newspaperArticle';
|
return 'newspaperArticle';
|
||||||
|
|
@ -52,16 +52,19 @@ function getSearchResults(doc, checkOnly) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function doWeb(doc, url) {
|
async function doWeb(doc, url) {
|
||||||
if (await detectWeb(doc, url) == 'multiple') {
|
if (detectWeb(doc, url) == 'multiple') {
|
||||||
let items = await Zotero.selectItems(getSearchResults(doc, false));
|
let items = await Zotero.selectItems(getSearchResults(doc, false));
|
||||||
if (items) {
|
if (!items) return;
|
||||||
await Promise.all(
|
for (let url of Object.keys(items)) {
|
||||||
Object.keys(items)
|
await scrape(await requestDocument(url));
|
||||||
.map(url => requestDocument(url).then(doc => scrape(doc, url)))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
await scrape(doc, url);
|
await scrape(doc, url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function scrape(doc, url = doc.location.href) {
|
||||||
|
// TODO: implement or add a scrape function template
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
async function scrape(doc, url) {
|
async function scrape(doc, url = doc.location.href) {
|
||||||
// TODO adjust the url building
|
// TODO adjust the url building
|
||||||
let m = url.match(/FId=([\w\d]+)&/);
|
let m = url.match(/FId=([\w\d]+)&/);
|
||||||
if (m) {
|
if (m) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
async function scrape(doc, url) {
|
async function scrape(doc, url = doc.location.href) {
|
||||||
let translator = Zotero.loadTranslator('web');
|
let translator = Zotero.loadTranslator('web');
|
||||||
// Embedded Metadata
|
// Embedded Metadata
|
||||||
translator.setTranslator('951c027d-74ac-47d4-a107-9c3069ab7b48');
|
translator.setTranslator('951c027d-74ac-47d4-a107-9c3069ab7b48');
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
async function scrape(doc, url) {
|
async function scrape(doc, url = doc.location.href) {
|
||||||
// TODO adjust the selector for the lines here
|
// TODO adjust the selector for the lines here
|
||||||
let lines = doc.querySelectorAll('table#marcData tr');
|
let lines = doc.querySelectorAll('table#marcData tr');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
async function scrape(doc, url) {
|
async function scrape(doc, url = doc.location.href) {
|
||||||
let DOI = url.match(/\/(10\.[^#?]+)/)[1];
|
let DOI = url.match(/\/(10\.[^#?]+)/)[1];
|
||||||
// TODO adjust the URL here
|
// TODO adjust the URL here
|
||||||
let risURL = `http://citation-needed.services.springer.com/v2/references/${DOI}?format=refman&flavour=citation`;
|
let risURL = `http://citation-needed.services.springer.com/v2/references/${DOI}?format=refman&flavour=citation`;
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,12 @@ row > label:first-child, .creator-type-label, #more-creators-label
|
||||||
max-height: 7px;
|
max-height: 7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.zotero-focusable:focus {
|
||||||
|
/* At least in Fx60, the color doesn't quite match Monterey's native textbox outline,
|
||||||
|
but it will have to do for now */
|
||||||
|
outline: 2px solid -moz-mac-focusring !important;
|
||||||
|
}
|
||||||
|
|
||||||
textbox
|
textbox
|
||||||
{
|
{
|
||||||
margin: -1px 5px -1px 0;
|
margin: -1px 5px -1px 0;
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,8 @@ input {
|
||||||
|
|
||||||
/* This seems to be necessary to center the tabs. Not sure why. */
|
/* This seems to be necessary to center the tabs. Not sure why. */
|
||||||
.zotero-editpane-tabs > tab:last-of-type > hbox .tab-text {
|
.zotero-editpane-tabs > tab:last-of-type > hbox .tab-text {
|
||||||
margin: 2px 9px 2px 9px !important;
|
margin-left: 9px !important;
|
||||||
|
margin-right: 9px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zotero-editpane-tabs > tab[selected=true] > hbox .tab-text {
|
.zotero-editpane-tabs > tab[selected=true] > hbox .tab-text {
|
||||||
|
|
|
||||||
|
|
@ -63,3 +63,7 @@ tab {
|
||||||
background-color: #ececec;
|
background-color: #ececec;
|
||||||
border-top: 1px solid hsla(0, 0%, 0%, 0.2);
|
border-top: 1px solid hsla(0, 0%, 0%, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.zotero-tb-button:focus {
|
||||||
|
border: 1px dotted #999;
|
||||||
|
}
|
||||||
|
|
@ -40,6 +40,14 @@
|
||||||
<overlay id="menuOverlay"
|
<overlay id="menuOverlay"
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||||
|
|
||||||
|
<keyset id="mainKeyset">
|
||||||
|
<key id="key_quitApplication"
|
||||||
|
key="&quitApplicationCmd.accesskey;"
|
||||||
|
command="cmd_quitApplication"
|
||||||
|
modifiers="accel"/>
|
||||||
|
</keyset>
|
||||||
|
|
||||||
<menupopup id="menu_FilePopup">
|
<menupopup id="menu_FilePopup">
|
||||||
<menuseparator/>
|
<menuseparator/>
|
||||||
<menuitem id="menu_FileQuitItem"
|
<menuitem id="menu_FileQuitItem"
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
<menuseparator/>
|
<menuseparator/>
|
||||||
<menuitem id="menu_FileQuitItem"
|
<menuitem id="menu_FileQuitItem"
|
||||||
label="&quitApplicationCmdWin.label;"
|
label="&quitApplicationCmdWin.label;"
|
||||||
key="key_quitApplication"
|
accesskey="&quitApplicationCmdWin.accesskey;"
|
||||||
command="cmd_quitApplication"/>
|
command="cmd_quitApplication"/>
|
||||||
</menupopup>
|
</menupopup>
|
||||||
<menupopup id="menu_EditPopup">
|
<menupopup id="menu_EditPopup">
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,8 @@ var ZoteroAdvancedSearch = new function() {
|
||||||
ref: _searchBox.search,
|
ref: _searchBox.search,
|
||||||
isSearchMode: () => true,
|
isSearchMode: () => true,
|
||||||
getItems: async function () {
|
getItems: async function () {
|
||||||
|
await Zotero.Libraries.get(_libraryID).waitForDataLoad('item');
|
||||||
|
|
||||||
var search = _searchBox.search.clone();
|
var search = _searchBox.search.clone();
|
||||||
search.libraryID = _libraryID;
|
search.libraryID = _libraryID;
|
||||||
var ids = await search.search();
|
var ids = await search.search();
|
||||||
|
|
|
||||||
|
|
@ -245,7 +245,9 @@
|
||||||
urlField.setAttribute('hidden', false);
|
urlField.setAttribute('hidden', false);
|
||||||
if (this.clickableLink) {
|
if (this.clickableLink) {
|
||||||
urlField.onclick = function (event) {
|
urlField.onclick = function (event) {
|
||||||
ZoteroPane_Local.loadURI(this.value, event)
|
if (event.button != 2) {
|
||||||
|
ZoteroPane_Local.loadURI(this.value, event)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
urlField.className = 'zotero-text-link';
|
urlField.className = 'zotero-text-link';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
this._item = val;
|
this._item = val;
|
||||||
|
this._lastTabIndex = null;
|
||||||
this.refresh();
|
this.refresh();
|
||||||
]]></setter>
|
]]></setter>
|
||||||
</property>
|
</property>
|
||||||
|
|
@ -391,7 +392,7 @@
|
||||||
&& Zotero.ItemFields.isDate(fieldName)
|
&& Zotero.ItemFields.isDate(fieldName)
|
||||||
// TEMP - NSF
|
// TEMP - NSF
|
||||||
&& fieldName != 'dateSent') {
|
&& fieldName != 'dateSent') {
|
||||||
this.addDateRow(fieldNames[i], this.item.getField(fieldName, true), tabindex);
|
this.addDateRow(fieldName, this.item.getField(fieldName, true), tabindex);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -422,7 +423,7 @@
|
||||||
&& Zotero.Utilities.isHTTPURL(val, true)) {
|
&& Zotero.Utilities.isHTTPURL(val, true)) {
|
||||||
label.classList.add("pointer");
|
label.classList.add("pointer");
|
||||||
// TODO: make getFieldValue non-private and use below instead
|
// TODO: make getFieldValue non-private and use below instead
|
||||||
label.setAttribute("onclick", "Zotero.launchURL(this.nextSibling.firstChild ? this.nextSibling.firstChild.nodeValue : this.nextSibling.value)");
|
label.addEventListener('click', () => Zotero.launchURL(label.nextSibling.firstChild ? label.nextSibling.firstChild.nodeValue : label.nextSibling.value));
|
||||||
label.setAttribute("tooltiptext", Zotero.getString('pane.item.viewOnline.tooltip'));
|
label.setAttribute("tooltiptext", Zotero.getString('pane.item.viewOnline.tooltip'));
|
||||||
}
|
}
|
||||||
else if (fieldName == 'DOI' && val && typeof val == 'string') {
|
else if (fieldName == 'DOI' && val && typeof val == 'string') {
|
||||||
|
|
@ -437,17 +438,14 @@
|
||||||
.replace(/%/g, '%25')
|
.replace(/%/g, '%25')
|
||||||
.replace(/"/g, '%22');
|
.replace(/"/g, '%22');
|
||||||
label.classList.add("pointer");
|
label.classList.add("pointer");
|
||||||
label.setAttribute("onclick", "ZoteroPane_Local.loadURI('" + doi + "', event)");
|
label.addEventListener('click', event => ZoteroPane_Local.loadURI(doi, event));
|
||||||
label.setAttribute("tooltiptext", Zotero.getString('pane.item.viewOnline.tooltip'));
|
label.setAttribute("tooltiptext", Zotero.getString('pane.item.viewOnline.tooltip'));
|
||||||
valueElement.oncontextmenu = () => {
|
|
||||||
this._id('zotero-doi-menu').openPopup(valueElement);
|
|
||||||
};
|
|
||||||
|
|
||||||
var openURLMenuItem = this._id('zotero-doi-menu-view-online');
|
var openURLMenuItem = this._id('zotero-doi-menu-view-online');
|
||||||
openURLMenuItem.setAttribute("oncommand", "ZoteroPane_Local.loadURI('" + doi + "', event)");
|
openURLMenuItem.addEventListener('command', event => ZoteroPane_Local.loadURI(doi, event));
|
||||||
|
|
||||||
var copyMenuItem = this._id('zotero-doi-menu-copy');
|
var copyMenuItem = this._id('zotero-doi-menu-copy');
|
||||||
copyMenuItem.setAttribute("oncommand", "Zotero.Utilities.Internal.copyTextToClipboard('" + doi + "')");
|
copyMenuItem.addEventListener('command', () => Zotero.Utilities.Internal.copyTextToClipboard(doi));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (fieldName == 'abstractNote') {
|
else if (fieldName == 'abstractNote') {
|
||||||
|
|
@ -616,7 +614,7 @@
|
||||||
this.disableCreatorAddButtons();
|
this.disableCreatorAddButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move to next or previous field if (shift-)tab was pressed
|
// Refocus on last stored tab index. This is required e.g. when remote change triggers refresh.
|
||||||
if (this._lastTabIndex && this._lastTabIndex != -1) {
|
if (this._lastTabIndex && this._lastTabIndex != -1) {
|
||||||
this._focusNextField(this._lastTabIndex);
|
this._focusNextField(this._lastTabIndex);
|
||||||
}
|
}
|
||||||
|
|
@ -719,7 +717,7 @@
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
// getCreatorFields(), switchCreatorMode() and handleCreatorAutoCompleteSelect()
|
// getCreatorFields(), switchCreatorMode() and handleCreatorAutoCompleteSelect()
|
||||||
// may need need to be adjusted if this DOM structure changes
|
// may need need to be adjusted if this DOM structure changes
|
||||||
|
var tabindex = this._tabIndexMinCreators + (this._creatorCount * 6);
|
||||||
var fieldMode = Zotero.Prefs.get('lastCreatorFieldMode');
|
var fieldMode = Zotero.Prefs.get('lastCreatorFieldMode');
|
||||||
var firstName = '';
|
var firstName = '';
|
||||||
var lastName = '';
|
var lastName = '';
|
||||||
|
|
@ -757,9 +755,15 @@
|
||||||
}
|
}
|
||||||
typeBox.setAttribute("fieldname", 'creator-' + this._creatorCount + '-typeID');
|
typeBox.setAttribute("fieldname", 'creator-' + this._creatorCount + '-typeID');
|
||||||
if (this.editable) {
|
if (this.editable) {
|
||||||
typeBox.className = 'creator-type-label zotero-clicky';
|
typeBox.className = 'creator-type-label zotero-clicky zotero-focusable';
|
||||||
|
typeBox.setAttribute('ztabindex', tabindex - 1);
|
||||||
|
typeBox.setAttribute('role', 'listbox');
|
||||||
|
typeBox.addEventListener('focus', ev => {
|
||||||
|
this._lastTabIndex = parseInt(ev.target.getAttribute('ztabindex'));
|
||||||
|
});
|
||||||
var img = document.createElement('image');
|
var img = document.createElement('image');
|
||||||
typeBox.appendChild(img);
|
typeBox.appendChild(img);
|
||||||
|
typeBox.setAttribute('onkeypress', 'return document.getBindingParent(this).handleKeyPress(event)');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
typeBox.className = 'creator-type-label';
|
typeBox.className = 'creator-type-label';
|
||||||
|
|
@ -773,11 +777,17 @@
|
||||||
var hbox = document.createElement("hbox");
|
var hbox = document.createElement("hbox");
|
||||||
hbox.className = 'creator-type-value';
|
hbox.className = 'creator-type-value';
|
||||||
|
|
||||||
|
hbox.addEventListener('focusin', ev => {
|
||||||
|
if(ev.target.classList.contains('zotero-focusable')) {
|
||||||
|
this._lastTabIndex = parseInt(ev.target.getAttribute('ztabindex'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Name
|
// Name
|
||||||
var firstlast = document.createElement("hbox");
|
var firstlast = document.createElement("hbox");
|
||||||
firstlast.className = 'creator-name-box';
|
firstlast.className = 'creator-name-box';
|
||||||
firstlast.setAttribute("flex","1");
|
firstlast.setAttribute("flex","1");
|
||||||
var tabindex = this._tabIndexMinCreators + (this._creatorCount * 2);
|
|
||||||
var fieldName = 'creator-' + this._creatorCount + '-lastName';
|
var fieldName = 'creator-' + this._creatorCount + '-lastName';
|
||||||
var lastNameLabel = firstlast.appendChild(
|
var lastNameLabel = firstlast.appendChild(
|
||||||
this.createValueElement(
|
this.createValueElement(
|
||||||
|
|
@ -820,29 +830,40 @@
|
||||||
var toggleButton = document.createElement('label');
|
var toggleButton = document.createElement('label');
|
||||||
toggleButton.setAttribute('fieldname',
|
toggleButton.setAttribute('fieldname',
|
||||||
'creator-' + this._creatorCount + '-fieldMode');
|
'creator-' + this._creatorCount + '-fieldMode');
|
||||||
toggleButton.className = 'zotero-field-toggle zotero-clicky';
|
toggleButton.setAttribute('ztabindex', tabindex + 2);
|
||||||
|
toggleButton.setAttribute('role', 'button');
|
||||||
|
toggleButton.className = 'zotero-field-toggle zotero-focusable zotero-clicky';
|
||||||
|
toggleButton.setAttribute('onkeypress', 'return document.getBindingParent(this).handleKeyPress(event)');
|
||||||
hbox.appendChild(toggleButton);
|
hbox.appendChild(toggleButton);
|
||||||
|
|
||||||
// Minus (-) button
|
// Minus (-) button
|
||||||
var removeButton = document.createElement('label');
|
var removeButton = document.createElement('label');
|
||||||
|
removeButton.setAttribute('ztabindex', tabindex + 3);
|
||||||
|
removeButton.setAttribute('role', 'button');
|
||||||
|
removeButton.setAttribute('aria-label', Zotero.getString('general.delete'));
|
||||||
removeButton.setAttribute("value","-");
|
removeButton.setAttribute("value","-");
|
||||||
removeButton.setAttribute("class","zotero-clicky zotero-clicky-minus");
|
removeButton.setAttribute('class','zotero-clicky zotero-focusable zotero-clicky-minus');
|
||||||
|
removeButton.setAttribute('onkeypress', 'return document.getBindingParent(this).handleKeyPress(event)');
|
||||||
// If default first row, don't let user remove it
|
// If default first row, don't let user remove it
|
||||||
if (defaultRow) {
|
if (defaultRow) {
|
||||||
this.disableButton(removeButton);
|
this.disableButton(removeButton);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
removeButton.setAttribute("onclick",
|
let index = this._creatorCount;
|
||||||
"document.getBindingParent(this).removeCreator("
|
removeButton.onclick = (ev) => {
|
||||||
+ this._creatorCount
|
document.getBindingParent(ev.currentTarget)
|
||||||
+ ", this.parentNode.parentNode)");
|
.removeCreator(index, ev.currentTarget.parentNode.parentNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
hbox.appendChild(removeButton);
|
hbox.appendChild(removeButton);
|
||||||
|
|
||||||
// Plus (+) button
|
// Plus (+) button
|
||||||
var addButton = document.createElement('label');
|
var addButton = document.createElement('label');
|
||||||
|
addButton.setAttribute('ztabindex', tabindex + 4);
|
||||||
addButton.setAttribute("value","+");
|
addButton.setAttribute("value","+");
|
||||||
addButton.setAttribute("class", "zotero-clicky zotero-clicky-plus");
|
addButton.setAttribute('role', 'button');
|
||||||
|
addButton.setAttribute('class', 'zotero-clicky zotero-focusable zotero-clicky-plus');
|
||||||
|
addButton.setAttribute('onkeypress', 'return document.getBindingParent(this).handleKeyPress(event)');
|
||||||
// If row isn't saved, don't let user add more
|
// If row isn't saved, don't let user add more
|
||||||
if (unsaved) {
|
if (unsaved) {
|
||||||
this.disableButton(addButton);
|
this.disableButton(addButton);
|
||||||
|
|
@ -958,7 +979,11 @@
|
||||||
button.style.background = `url("chrome://zotero/skin/textfield-dual${Zotero.hiDPISuffix}.png") center/21px auto no-repeat`;
|
button.style.background = `url("chrome://zotero/skin/textfield-dual${Zotero.hiDPISuffix}.png") center/21px auto no-repeat`;
|
||||||
button.setAttribute('tooltiptext', Zotero.getString('pane.item.switchFieldMode.two'));
|
button.setAttribute('tooltiptext', Zotero.getString('pane.item.switchFieldMode.two'));
|
||||||
lastName.setAttribute('fieldMode', '1');
|
lastName.setAttribute('fieldMode', '1');
|
||||||
button.setAttribute('onclick', "document.getBindingParent(this).switchCreatorMode(Zotero.getAncestorByTagName(this, 'row'), 0, false, true)");
|
button.onclick = (ev) => {
|
||||||
|
document
|
||||||
|
.getBindingParent(ev.currentTarget)
|
||||||
|
.switchCreatorMode(Zotero.getAncestorByTagName(ev.currentTarget, 'row'), 0, false, true);
|
||||||
|
};
|
||||||
lastName.setAttribute('flex', '1');
|
lastName.setAttribute('flex', '1');
|
||||||
delete lastName.style.width;
|
delete lastName.style.width;
|
||||||
delete lastName.style.maxWidth;
|
delete lastName.style.maxWidth;
|
||||||
|
|
@ -998,7 +1023,11 @@
|
||||||
button.style.background = `url("chrome://zotero/skin/textfield-single${Zotero.hiDPISuffix}.png") center/21px auto no-repeat`;
|
button.style.background = `url("chrome://zotero/skin/textfield-single${Zotero.hiDPISuffix}.png") center/21px auto no-repeat`;
|
||||||
button.setAttribute('tooltiptext', Zotero.getString('pane.item.switchFieldMode.one'));
|
button.setAttribute('tooltiptext', Zotero.getString('pane.item.switchFieldMode.one'));
|
||||||
lastName.setAttribute('fieldMode', '0');
|
lastName.setAttribute('fieldMode', '0');
|
||||||
button.setAttribute('onclick', "document.getBindingParent(this).switchCreatorMode(Zotero.getAncestorByTagName(this, 'row'), 1, false, true)");
|
button.onclick = (ev) => {
|
||||||
|
document
|
||||||
|
.getBindingParent(ev.currentTarget)
|
||||||
|
.switchCreatorMode(Zotero.getAncestorByTagName(ev.currentTarget, 'row'), 1, false, true)
|
||||||
|
};
|
||||||
lastName.setAttribute('flex', '0');
|
lastName.setAttribute('flex', '0');
|
||||||
|
|
||||||
// appropriately truncate lastName
|
// appropriately truncate lastName
|
||||||
|
|
@ -1391,6 +1420,13 @@
|
||||||
valueElement.setAttribute('flex', '1');
|
valueElement.setAttribute('flex', '1');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add popup menu on DOI field with value
|
||||||
|
if (fieldName == 'DOI' && valueText) {
|
||||||
|
valueElement.oncontextmenu = () => {
|
||||||
|
this._id('zotero-doi-menu').openPopup(valueElement);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
var firstSpace = valueText.indexOf(" ");
|
var firstSpace = valueText.indexOf(" ");
|
||||||
|
|
||||||
// To support newlines in Abstract and Extra fields, use multiple
|
// To support newlines in Abstract and Extra fields, use multiple
|
||||||
|
|
@ -1552,8 +1588,7 @@
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Add auto-complete for certain fields
|
// Add auto-complete for certain fields
|
||||||
if (Zotero.ItemFields.isAutocompleteField(fieldName)
|
if (field == 'creator' || Zotero.ItemFields.isAutocompleteField(fieldName)) {
|
||||||
|| fieldName == 'creator') {
|
|
||||||
t.setAttribute('type', 'autocomplete');
|
t.setAttribute('type', 'autocomplete');
|
||||||
t.setAttribute('autocompletesearch', 'zotero');
|
t.setAttribute('autocompletesearch', 'zotero');
|
||||||
|
|
||||||
|
|
@ -1721,6 +1756,19 @@
|
||||||
var target = event.target;
|
var target = event.target;
|
||||||
var focused = document.commandDispatcher.focusedElement;
|
var focused = document.commandDispatcher.focusedElement;
|
||||||
|
|
||||||
|
if ((event.key == 'ArrowUp' || event.key === 'ArrowDown')
|
||||||
|
&& target.classList.contains('creator-type-label')) {
|
||||||
|
event.preventDefault();
|
||||||
|
target.click();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === ' ' && target.classList.contains('zotero-focusable')) {
|
||||||
|
event.preventDefault();
|
||||||
|
target.click();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
switch (event.keyCode)
|
switch (event.keyCode)
|
||||||
{
|
{
|
||||||
case event.DOM_VK_RETURN:
|
case event.DOM_VK_RETURN:
|
||||||
|
|
@ -1932,8 +1980,6 @@
|
||||||
|
|
||||||
newVal = val;
|
newVal = val;
|
||||||
|
|
||||||
// Reset creator mode settings here so that flex attribute gets reset
|
|
||||||
this.switchCreatorMode(row, (otherFields.fieldMode ? 1 : 0), true);
|
|
||||||
if (Zotero.ItemTypes.getName(this.item.itemTypeID) === "bookSection") {
|
if (Zotero.ItemTypes.getName(this.item.itemTypeID) === "bookSection") {
|
||||||
var creatorTypeLabels = document.getAnonymousNodes(this)[0].getElementsByClassName("creator-type-label");
|
var creatorTypeLabels = document.getAnonymousNodes(this)[0].getElementsByClassName("creator-type-label");
|
||||||
Zotero.debug(creatorTypeLabels[creatorTypeLabels.length-1] + "");
|
Zotero.debug(creatorTypeLabels[creatorTypeLabels.length-1] + "");
|
||||||
|
|
@ -2018,6 +2064,11 @@
|
||||||
// Disassociate textbox from label
|
// Disassociate textbox from label
|
||||||
label.setAttribute('control', elem.getAttribute('id'));
|
label.setAttribute('control', elem.getAttribute('id'));
|
||||||
|
|
||||||
|
if (field == 'creator') {
|
||||||
|
// Set correct flex settings and fieldMode attribute
|
||||||
|
this.switchCreatorMode(row, (otherFields.fieldMode ? 1 : 0), true);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.saveOnEdit) {
|
if (this.saveOnEdit) {
|
||||||
await this.item.saveTx();
|
await this.item.saveTx();
|
||||||
}
|
}
|
||||||
|
|
@ -2294,6 +2345,32 @@
|
||||||
</body>
|
</body>
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
|
<method name="focusLastField">
|
||||||
|
<body><![CDATA[
|
||||||
|
const tabbableFields = this._dynamicFields.querySelectorAll('*[ztabindex]:not([disabled=true])');
|
||||||
|
const last = tabbableFields[tabbableFields.length - 1];
|
||||||
|
|
||||||
|
if (last.classList.contains('zotero-focusable')) {
|
||||||
|
last.focus();
|
||||||
|
}
|
||||||
|
// Fields need to be clicked
|
||||||
|
else {
|
||||||
|
last.click();
|
||||||
|
}
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="focusField">
|
||||||
|
<parameter name="fieldName"/>
|
||||||
|
<body>
|
||||||
|
<![CDATA[
|
||||||
|
let field = document.getAnonymousNodes(this)[0].querySelector(`[fieldname="${fieldName}"][ztabindex]`);
|
||||||
|
if (!field) return false;
|
||||||
|
return this._focusNextField(field.getAttribute('ztabindex'));
|
||||||
|
]]>
|
||||||
|
</body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Advance the field focus forward or backward
|
Advance the field focus forward or backward
|
||||||
|
|
@ -2312,7 +2389,7 @@
|
||||||
tabindex = parseInt(tabindex);
|
tabindex = parseInt(tabindex);
|
||||||
|
|
||||||
// Get all fields with ztabindex attributes
|
// Get all fields with ztabindex attributes
|
||||||
var tabbableFields = box.querySelectorAll('*[ztabindex]');
|
var tabbableFields = box.querySelectorAll('*[ztabindex]:not([disabled=true])');
|
||||||
|
|
||||||
if (!tabbableFields.length) {
|
if (!tabbableFields.length) {
|
||||||
Zotero.debug("No tabbable fields found");
|
Zotero.debug("No tabbable fields found");
|
||||||
|
|
@ -2323,7 +2400,9 @@
|
||||||
if (back) {
|
if (back) {
|
||||||
Zotero.debug('Looking for previous tabindex before ' + tabindex, 4);
|
Zotero.debug('Looking for previous tabindex before ' + tabindex, 4);
|
||||||
for (let i = tabbableFields.length - 1; i >= 0; i--) {
|
for (let i = tabbableFields.length - 1; i >= 0; i--) {
|
||||||
if (parseInt(tabbableFields[i].getAttribute('ztabindex')) < tabindex) {
|
let field = tabbableFields[i];
|
||||||
|
let tabIndexHere = parseInt(field.getAttribute('ztabindex'));
|
||||||
|
if (tabIndexHere !== -1 && tabIndexHere < tabindex) {
|
||||||
next = tabbableFields[i];
|
next = tabbableFields[i];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -2332,7 +2411,9 @@
|
||||||
else {
|
else {
|
||||||
Zotero.debug('Looking for tabindex ' + tabindex, 4);
|
Zotero.debug('Looking for tabindex ' + tabindex, 4);
|
||||||
for (var pos = 0; pos < tabbableFields.length; pos++) {
|
for (var pos = 0; pos < tabbableFields.length; pos++) {
|
||||||
if (parseInt(tabbableFields[pos].getAttribute('ztabindex')) >= tabindex) {
|
let field = tabbableFields[pos];
|
||||||
|
let tabIndexHere = parseInt(field.getAttribute('ztabindex'));
|
||||||
|
if (tabIndexHere !== -1 && tabIndexHere >= tabindex) {
|
||||||
next = tabbableFields[pos];
|
next = tabbableFields[pos];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -2345,7 +2426,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drop-down needs to be focused
|
// Drop-down needs to be focused
|
||||||
if (next.id == 'item-type-menu') {
|
if (next.id == 'item-type-menu' || next.classList.contains('zotero-focusable')) {
|
||||||
next.focus();
|
next.focus();
|
||||||
}
|
}
|
||||||
// Fields need to be clicked
|
// Fields need to be clicked
|
||||||
|
|
|
||||||
|
|
@ -353,15 +353,16 @@
|
||||||
<body>
|
<body>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
(async () => {
|
(async () => {
|
||||||
let n = 0;
|
|
||||||
while (!this._editorInstance && n++ < 100) {
|
|
||||||
await Zotero.Promise.delay(10);
|
|
||||||
}
|
|
||||||
await this._editorInstance._initPromise;
|
|
||||||
this._iframe.focus();
|
|
||||||
try {
|
try {
|
||||||
|
let n = 0;
|
||||||
|
while (!this._editorInstance && n++ < 100) {
|
||||||
|
await Zotero.Promise.delay(10);
|
||||||
|
}
|
||||||
|
await this._editorInstance._initPromise;
|
||||||
|
this._iframe.focus();
|
||||||
this._editorInstance._iframeWindow.document.querySelector('.toolbar-button-return').focus();
|
this._editorInstance._iframeWindow.document.querySelector('.toolbar-button-return').focus();
|
||||||
} catch(e) {
|
}
|
||||||
|
catch (e) {
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
]]>
|
]]>
|
||||||
|
|
|
||||||
|
|
@ -330,6 +330,15 @@
|
||||||
]]>
|
]]>
|
||||||
</body>
|
</body>
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
|
<method name="receiveKeyboardFocus">
|
||||||
|
<parameter name="direction" />
|
||||||
|
<body><![CDATA[
|
||||||
|
this.id("addButton").focus();
|
||||||
|
// TODO: the relatedbox is not currently keyboard accessible
|
||||||
|
// so we are ignoring the direction
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
</implementation>
|
</implementation>
|
||||||
<content>
|
<content>
|
||||||
<xul:vbox xbl:inherits="flex" class="zotero-box">
|
<xul:vbox xbl:inherits="flex" class="zotero-box">
|
||||||
|
|
|
||||||
|
|
@ -407,6 +407,7 @@
|
||||||
<parameter name="condition"/>
|
<parameter name="condition"/>
|
||||||
<body><![CDATA[
|
<body><![CDATA[
|
||||||
switch (condition) {
|
switch (condition) {
|
||||||
|
case 'anyField':
|
||||||
case 'collection':
|
case 'collection':
|
||||||
case 'creator':
|
case 'creator':
|
||||||
case 'title':
|
case 'title':
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const ReactDOM = require('react-dom');
|
const ReactDOM = require('react-dom');
|
||||||
const { IntlProvider } = require('react-intl');
|
|
||||||
const LibraryTree = require('./libraryTree');
|
const LibraryTree = require('./libraryTree');
|
||||||
const VirtualizedTable = require('components/virtualized-table');
|
const VirtualizedTable = require('components/virtualized-table');
|
||||||
const { TreeSelectionStub } = VirtualizedTable;
|
const { TreeSelectionStub } = VirtualizedTable;
|
||||||
|
|
@ -42,9 +41,7 @@ var CollectionTree = class CollectionTree extends LibraryTree {
|
||||||
var ref;
|
var ref;
|
||||||
opts.domEl = domEl;
|
opts.domEl = domEl;
|
||||||
let elem = (
|
let elem = (
|
||||||
<IntlProvider locale={Zotero.locale} messages={Zotero.Intl.strings}>
|
<CollectionTree ref={c => ref = c } {...opts} />
|
||||||
<CollectionTree ref={c => ref = c } {...opts} />
|
|
||||||
</IntlProvider>
|
|
||||||
);
|
);
|
||||||
await new Promise(resolve => ReactDOM.render(elem, domEl, resolve));
|
await new Promise(resolve => ReactDOM.render(elem, domEl, resolve));
|
||||||
|
|
||||||
|
|
@ -640,15 +637,15 @@ var CollectionTree = class CollectionTree extends LibraryTree {
|
||||||
this.tree.invalidate();
|
this.tree.invalidate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (action == 'refresh') {
|
if (action == 'refresh' && type != 'trash') {
|
||||||
// If trash is refreshed, we probably need to update the icon from full to empty
|
// Trash handled below
|
||||||
if (type == 'trash') {
|
|
||||||
this.tree.invalidate();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (type == 'feed' && (action == 'unreadCountUpdated' || action == 'statusChanged')) {
|
if (type == 'feed' && (action == 'unreadCountUpdated' || action == 'statusChanged')) {
|
||||||
this.tree.invalidate();
|
let feedRow = this.getRowIndexByID("L" + ids[0]);
|
||||||
|
if (feedRow !== false) {
|
||||||
|
this.tree.invalidateRow(feedRow);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -753,7 +750,7 @@ var CollectionTree = class CollectionTree extends LibraryTree {
|
||||||
// Invalidate parent in case it's become non-empty
|
// Invalidate parent in case it's become non-empty
|
||||||
let parentRow = this.getRowIndexByID("C" + collection.parentID);
|
let parentRow = this.getRowIndexByID("C" + collection.parentID);
|
||||||
if (parentRow !== false) {
|
if (parentRow !== false) {
|
||||||
this._treebox.invalidateRow(parentRow);
|
this.tree.invalidateRow(parentRow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -778,6 +775,9 @@ var CollectionTree = class CollectionTree extends LibraryTree {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'feed':
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
await this.reload();
|
await this.reload();
|
||||||
await this.selectByID(currentTreeRow.id);
|
await this.selectByID(currentTreeRow.id);
|
||||||
|
|
@ -826,6 +826,11 @@ var CollectionTree = class CollectionTree extends LibraryTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (action == 'refresh' && type == 'trash') {
|
||||||
|
// We need to update the trash's status (full or empty), and if empty,
|
||||||
|
// the row might be removed
|
||||||
|
await this.reload();
|
||||||
|
}
|
||||||
|
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
var promise = this.waitForSelect();
|
var promise = this.waitForSelect();
|
||||||
|
|
@ -839,6 +844,7 @@ var CollectionTree = class CollectionTree extends LibraryTree {
|
||||||
* @param ids {String[]} list of row ids to be highlighted
|
* @param ids {String[]} list of row ids to be highlighted
|
||||||
*/
|
*/
|
||||||
async setHighlightedRows(ids) {
|
async setHighlightedRows(ids) {
|
||||||
|
if (this._editing) return;
|
||||||
try {
|
try {
|
||||||
this._highlightedRows = new Set();
|
this._highlightedRows = new Set();
|
||||||
|
|
||||||
|
|
@ -1614,7 +1620,7 @@ var CollectionTree = class CollectionTree extends LibraryTree {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let newAttachment = Zotero.Attachments.copyAttachmentToLibrary(item, targetLibraryID);
|
let newAttachment = await Zotero.Attachments.copyAttachmentToLibrary(item, targetLibraryID);
|
||||||
if (options.annotations) {
|
if (options.annotations) {
|
||||||
await Zotero.Items.copyChildItems(item, newAttachment);
|
await Zotero.Items.copyChildItems(item, newAttachment);
|
||||||
}
|
}
|
||||||
|
|
@ -2306,7 +2312,7 @@ var CollectionTree = class CollectionTree extends LibraryTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showTrash) {
|
if (showTrash) {
|
||||||
let deletedItems = await Zotero.Items.getDeleted(libraryID);
|
let deletedItems = await Zotero.Items.getDeleted(libraryID, true);
|
||||||
if (deletedItems.length || Zotero.Prefs.get("showTrashWhenEmpty")) {
|
if (deletedItems.length || Zotero.Prefs.get("showTrashWhenEmpty")) {
|
||||||
var ref = {
|
var ref = {
|
||||||
libraryID: libraryID
|
libraryID: libraryID
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ import React, { memo } from 'react';
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
import { IntlProvider, FormattedMessage } from "react-intl";
|
|
||||||
|
|
||||||
// This is a quick reimplementation of the annotation for use in the conflict resolution window.
|
// This is a quick reimplementation of the annotation for use in the conflict resolution window.
|
||||||
// We'll want to replace this with a single component shared between the PDF reader and the rest
|
// We'll want to replace this with a single component shared between the PDF reader and the rest
|
||||||
|
|
@ -40,31 +39,26 @@ function AnnotationBox({ data }) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IntlProvider
|
<div className="AnnotationBox">
|
||||||
locale={ Zotero.locale }
|
<div className="title">{Zotero.getString('itemTypes.annotation')}</div>
|
||||||
messages={ Zotero.Intl.strings }
|
<div className="container">
|
||||||
>
|
<div className="header">
|
||||||
<div className="AnnotationBox">
|
<div>{Zotero.Cite.getLocatorString('page')} {data.pageLabel}</div>
|
||||||
<div className="title">{Zotero.getString('itemTypes.annotation')}</div>
|
|
||||||
<div className="container">
|
|
||||||
<div className="header">
|
|
||||||
<div>{Zotero.getString('citation.locator.page')} {data.pageLabel}</div>
|
|
||||||
</div>
|
|
||||||
{data.text !== undefined
|
|
||||||
? <div className="text" style={textStyle}>{data.text}</div>
|
|
||||||
: ''}
|
|
||||||
{data.type == 'image'
|
|
||||||
// TODO: Localize
|
|
||||||
// TODO: Render from PDF based on position, if file is the same? Or don't
|
|
||||||
// worry about it?
|
|
||||||
? <div className="image-placeholder">[image not shown]</div>
|
|
||||||
: ''}
|
|
||||||
{data.comment !== undefined
|
|
||||||
? <div className="comment">{data.comment}</div>
|
|
||||||
: ''}
|
|
||||||
</div>
|
</div>
|
||||||
|
{data.text !== undefined
|
||||||
|
? <div className="text" style={textStyle}>{data.text}</div>
|
||||||
|
: ''}
|
||||||
|
{data.type == 'image'
|
||||||
|
// TODO: Localize
|
||||||
|
// TODO: Render from PDF based on position, if file is the same? Or don't
|
||||||
|
// worry about it?
|
||||||
|
? <div className="image-placeholder">[image not shown]</div>
|
||||||
|
: ''}
|
||||||
|
{data.comment !== undefined
|
||||||
|
? <div className="comment">{data.comment}</div>
|
||||||
|
: ''}
|
||||||
</div>
|
</div>
|
||||||
</IntlProvider>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@
|
||||||
|
|
||||||
const React = require('react')
|
const React = require('react')
|
||||||
const { PureComponent, createElement: create } = React
|
const { PureComponent, createElement: create } = React
|
||||||
const { injectIntl } = require('react-intl')
|
|
||||||
const { IconDownChevron } = require('./icons')
|
const { IconDownChevron } = require('./icons')
|
||||||
const cx = require('classnames')
|
const cx = require('classnames')
|
||||||
const {
|
const {
|
||||||
|
|
@ -59,19 +58,19 @@ class Button extends PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
get text() {
|
get text() {
|
||||||
const { intl, text } = this.props
|
const { text } = this.props;
|
||||||
|
|
||||||
return text ?
|
return text ?
|
||||||
intl.formatMessage({ id: text }) :
|
Zotero.getString(text) :
|
||||||
null
|
null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get title() {
|
get title() {
|
||||||
const { intl, title } = this.props
|
const { title } = this.props;
|
||||||
|
|
||||||
return title ?
|
return title ?
|
||||||
intl.formatMessage({ id: title }) :
|
Zotero.getString(title) :
|
||||||
null
|
null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get menuMarker() {
|
get menuMarker() {
|
||||||
|
|
@ -159,5 +158,5 @@ class Button extends PureComponent {
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
ButtonGroup,
|
ButtonGroup,
|
||||||
Button: injectIntl(Button)
|
Button
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ import React, { memo } from 'react';
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
import { IntlProvider } from "react-intl";
|
|
||||||
|
|
||||||
function CreateParent({ loading, item, toggleAccept }) {
|
function CreateParent({ loading, item, toggleAccept }) {
|
||||||
// When the input has/does not have characters toggle the accept button on the dialog
|
// When the input has/does not have characters toggle the accept button on the dialog
|
||||||
|
|
@ -43,31 +42,26 @@ function CreateParent({ loading, item, toggleAccept }) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IntlProvider
|
<div className="create-parent-container">
|
||||||
locale={ Zotero.locale }
|
<span className="title">
|
||||||
messages={ Zotero.Intl.strings }
|
{ item.attachmentFilename }
|
||||||
>
|
</span>
|
||||||
<div className="create-parent-container">
|
<div className="body">
|
||||||
<span className="title">
|
<input
|
||||||
{ item.attachmentFilename }
|
id="parent-item-identifier"
|
||||||
</span>
|
placeholder={ Zotero.getString('createParent.prompt') }
|
||||||
<div className="body">
|
size="50"
|
||||||
<input
|
disabled={ loading }
|
||||||
id="parent-item-identifier"
|
onChange={ handleInput }
|
||||||
placeholder={ Zotero.getString('createParent.prompt') }
|
/>
|
||||||
size="50"
|
<div
|
||||||
disabled={ loading }
|
mode="undetermined"
|
||||||
onChange={ handleInput }
|
className={ cx('downloadProgress', { hidden: !loading }) }
|
||||||
/>
|
>
|
||||||
<div
|
<div className="progress-bar"></div>
|
||||||
mode="undetermined"
|
|
||||||
className={ cx('downloadProgress', { hidden: !loading }) }
|
|
||||||
>
|
|
||||||
<div className="progress-bar"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</IntlProvider>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@ i('TreeitemBookSection', 'chrome://zotero/skin/treeitem-bookSection.png');
|
||||||
i('TreeitemCase', 'chrome://zotero/skin/treeitem-case.png');
|
i('TreeitemCase', 'chrome://zotero/skin/treeitem-case.png');
|
||||||
i('TreeitemComputerProgram', 'chrome://zotero/skin/treeitem-computerProgram.png');
|
i('TreeitemComputerProgram', 'chrome://zotero/skin/treeitem-computerProgram.png');
|
||||||
i('TreeitemConferencePaper', 'chrome://zotero/skin/treeitem-conferencePaper.png');
|
i('TreeitemConferencePaper', 'chrome://zotero/skin/treeitem-conferencePaper.png');
|
||||||
|
i('TreeitemDataset', 'chrome://zotero/skin/treeitem-dataset.png');
|
||||||
i('TreeitemDictionaryEntry', 'chrome://zotero/skin/treeitem-dictionaryEntry.png');
|
i('TreeitemDictionaryEntry', 'chrome://zotero/skin/treeitem-dictionaryEntry.png');
|
||||||
i('TreeitemEmail', 'chrome://zotero/skin/treeitem-email.png');
|
i('TreeitemEmail', 'chrome://zotero/skin/treeitem-email.png');
|
||||||
i('TreeitemEncyclopediaArticle', 'chrome://zotero/skin/treeitem-encyclopediaArticle.png');
|
i('TreeitemEncyclopediaArticle', 'chrome://zotero/skin/treeitem-encyclopediaArticle.png');
|
||||||
|
|
@ -147,12 +148,13 @@ i('TreeitemPreprint', 'chrome://zotero/skin/treeitem-preprint.png');
|
||||||
i('TreeitemPresentation', 'chrome://zotero/skin/treeitem-presentation.png');
|
i('TreeitemPresentation', 'chrome://zotero/skin/treeitem-presentation.png');
|
||||||
i('TreeitemRadioBroadcast', 'chrome://zotero/skin/treeitem-radioBroadcast.png', false);
|
i('TreeitemRadioBroadcast', 'chrome://zotero/skin/treeitem-radioBroadcast.png', false);
|
||||||
i('TreeitemReport', 'chrome://zotero/skin/treeitem-report.png');
|
i('TreeitemReport', 'chrome://zotero/skin/treeitem-report.png');
|
||||||
|
i('TreeitemStandard', 'chrome://zotero/skin/treeitem-standard.png');
|
||||||
i('TreeitemStatute', 'chrome://zotero/skin/treeitem-statute.png');
|
i('TreeitemStatute', 'chrome://zotero/skin/treeitem-statute.png');
|
||||||
i('TreeitemThesis', 'chrome://zotero/skin/treeitem-thesis.png');
|
i('TreeitemThesis', 'chrome://zotero/skin/treeitem-thesis.png');
|
||||||
i('TreeitemTvBroadcast', 'chrome://zotero/skin/treeitem-tvBroadcast.png', false);
|
i('TreeitemTvBroadcast', 'chrome://zotero/skin/treeitem-tvBroadcast.png', false);
|
||||||
i('TreeitemVideoRecording', 'chrome://zotero/skin/treeitem-videoRecording.png', false);
|
i('TreeitemVideoRecording', 'chrome://zotero/skin/treeitem-videoRecording.png', false);
|
||||||
i('TreeitemWebpageGray', 'chrome://zotero/skin/treeitem-webpage-gray.png');
|
i('TreeitemWebpageGray', 'chrome://zotero/skin/treeitem-webpage-gray.png');
|
||||||
i('TreeitemWebpage', 'chrome://zotero/skin/treeitem-webpage.png', false);
|
i('TreeitemWebpage', 'chrome://zotero/skin/treeitem-webpage.png');
|
||||||
|
|
||||||
// Treesource
|
// Treesource
|
||||||
i('TreesourceBucket', 'chrome://zotero/skin/treesource-bucket.png', false);
|
i('TreesourceBucket', 'chrome://zotero/skin/treesource-bucket.png', false);
|
||||||
|
|
|
||||||
|
|
@ -415,7 +415,7 @@ const TagsBox = React.forwardRef((props, ref) => {
|
||||||
<div className="tags-box" ref={rootRef} onClick={blurOpenField}>
|
<div className="tags-box" ref={rootRef} onClick={blurOpenField}>
|
||||||
<div className="tags-box-header">
|
<div className="tags-box-header">
|
||||||
<div className="tags-box-count">{renderCount()}</div>
|
<div className="tags-box-count">{renderCount()}</div>
|
||||||
{ props.editable && <div><button onClick={handleAddTag}>Add</button></div> }
|
{ props.editable && <div><button onClick={handleAddTag} id="tags-box-add-button">Add</button></div> }
|
||||||
</div>
|
</div>
|
||||||
<div className="tags-box-list-container">
|
<div className="tags-box-list-container">
|
||||||
<ul className="tags-box-list">
|
<ul className="tags-box-list">
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@
|
||||||
import React, { memo, useCallback, useEffect, useRef } from 'react';
|
import React, { memo, useCallback, useEffect, useRef } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { getDOMElement } from 'components/icons';
|
import { getDOMElement } from 'components/icons';
|
||||||
import { IntlProvider } from 'react-intl';
|
|
||||||
|
|
||||||
import VirtualizedTable, { renderCell } from 'components/virtualized-table';
|
import VirtualizedTable, { renderCell } from 'components/virtualized-table';
|
||||||
import { noop } from './utils';
|
import { noop } from './utils';
|
||||||
|
|
@ -101,17 +100,15 @@ const ProgressQueueTable = ({ onActivate = noop, progressQueue }) => {
|
||||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IntlProvider locale={ Zotero.locale } messages={ Zotero.Intl.strings }>
|
<VirtualizedTable
|
||||||
<VirtualizedTable
|
getRowCount={ getRowCount }
|
||||||
getRowCount={ getRowCount }
|
ref={ treeRef }
|
||||||
ref={ treeRef }
|
id="progress-queue-table"
|
||||||
id="progress-queue-table"
|
renderItem={ rowToTreeItem }
|
||||||
renderItem={ rowToTreeItem }
|
showHeader={ true }
|
||||||
showHeader={ true }
|
columns={ tableColumns }
|
||||||
columns={ tableColumns }
|
onActivate={ onActivate }
|
||||||
onActivate={ onActivate }
|
/>
|
||||||
/>
|
|
||||||
</IntlProvider>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -235,7 +235,7 @@ const TabBar = forwardRef(function (props, ref) {
|
||||||
|
|
||||||
function handleWheel(event) {
|
function handleWheel(event) {
|
||||||
// Normalize wheel speed
|
// Normalize wheel speed
|
||||||
let x = event.deltaX;
|
let x = event.deltaX || event.deltaY;
|
||||||
if (x && event.deltaMode) {
|
if (x && event.deltaMode) {
|
||||||
if (event.deltaMode === 1) {
|
if (event.deltaMode === 1) {
|
||||||
x *= 20;
|
x *= 20;
|
||||||
|
|
@ -302,7 +302,10 @@ const TabBar = forwardRef(function (props, ref) {
|
||||||
onWheel={handleWheel}
|
onWheel={handleWheel}
|
||||||
>
|
>
|
||||||
<div className="pinned-tabs">
|
<div className="pinned-tabs">
|
||||||
<div className="tabs">
|
<div
|
||||||
|
className="tabs"
|
||||||
|
onMouseOut={handleTabBarMouseOut}
|
||||||
|
>
|
||||||
{tabs.length ? renderTab(tabs[0], 0) : null}
|
{tabs.length ? renderTab(tabs[0], 0) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const PropTypes = require('prop-types');
|
const PropTypes = require('prop-types');
|
||||||
const { FormattedMessage } = require('react-intl');
|
|
||||||
var { Collection } = require('react-virtualized');
|
var { Collection } = require('react-virtualized');
|
||||||
|
|
||||||
// See also .tag-selector-item in _tag-selector.scss
|
// See also .tag-selector-item in _tag-selector.scss
|
||||||
|
|
@ -191,14 +190,14 @@ class TagList extends React.PureComponent {
|
||||||
if (!this.props.loaded) {
|
if (!this.props.loaded) {
|
||||||
tagList = (
|
tagList = (
|
||||||
<div className="tag-selector-message">
|
<div className="tag-selector-message">
|
||||||
<FormattedMessage id="zotero.tagSelector.loadingTags" />
|
{Zotero.getString('zotero.tagSelector.loadingTags')}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (tagCount == 0) {
|
else if (tagCount == 0) {
|
||||||
tagList = (
|
tagList = (
|
||||||
<div className="tag-selector-message">
|
<div className="tag-selector-message">
|
||||||
<FormattedMessage id="zotero.tagSelector.noTagsToDisplay" />
|
{Zotero.getString('zotero.tagSelector.noTagsToDisplay')}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ const PropTypes = require('prop-types');
|
||||||
const cx = require('classnames');
|
const cx = require('classnames');
|
||||||
const WindowedList = require('./windowed-list');
|
const WindowedList = require('./windowed-list');
|
||||||
const Draggable = require('./draggable');
|
const Draggable = require('./draggable');
|
||||||
const { injectIntl } = require('react-intl');
|
|
||||||
const { IconDownChevron, getDOMElement } = require('components/icons');
|
const { IconDownChevron, getDOMElement } = require('components/icons');
|
||||||
|
|
||||||
const TYPING_TIMEOUT = 1000;
|
const TYPING_TIMEOUT = 1000;
|
||||||
|
|
@ -639,7 +638,10 @@ class VirtualizedTable extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (let i = (this.selection.focused) % rowCount; i != this.selection.focused - 1; i = (i + 1) % rowCount) {
|
// Stop at the row before the selection's focus
|
||||||
|
// but don't set a negative stop point, or we'll loop infinitely
|
||||||
|
let stopRow = Math.max(this.selection.focused - 1, 0);
|
||||||
|
for (let i = (this.selection.focused) % rowCount; i != stopRow; i = (i + 1) % rowCount) {
|
||||||
let rowString = this.props.getRowString(i);
|
let rowString = this.props.getRowString(i);
|
||||||
if (rowString.toLowerCase().indexOf(this._typingString) == 0) {
|
if (rowString.toLowerCase().indexOf(this._typingString) == 0) {
|
||||||
if (i != this.selection.focused) {
|
if (i != this.selection.focused) {
|
||||||
|
|
@ -660,7 +662,14 @@ class VirtualizedTable extends React.Component {
|
||||||
this._isMouseDrag = true;
|
this._isMouseDrag = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDragEnd = () => {
|
_onDragEnd = async () => {
|
||||||
|
// macOS force-click sometimes causes a second mouseup event to be fired some time later
|
||||||
|
// causing a collection change on dragend, so we add a delay here. It shouldn't cause any issues
|
||||||
|
// because isMouseDrag is only used in mouseup handler to exactly prevent from accidentally switching
|
||||||
|
// selection after dragend.
|
||||||
|
if (Zotero.isMac) {
|
||||||
|
await Zotero.Promise.delay(500);
|
||||||
|
}
|
||||||
this._isMouseDrag = false;
|
this._isMouseDrag = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -740,6 +749,7 @@ class VirtualizedTable extends React.Component {
|
||||||
this.selection.pivot = index;
|
this.selection.pivot = index;
|
||||||
this.invalidateRow(previousFocused);
|
this.invalidateRow(previousFocused);
|
||||||
this.invalidateRow(index);
|
this.invalidateRow(index);
|
||||||
|
this.selection._updateTree(shouldDebounce);
|
||||||
}
|
}
|
||||||
// Normal selection
|
// Normal selection
|
||||||
else if (!toggleSelection) {
|
else if (!toggleSelection) {
|
||||||
|
|
@ -1625,7 +1635,7 @@ function makeRowRenderer(getRowData) {
|
||||||
|
|
||||||
function formatColumnName(column) {
|
function formatColumnName(column) {
|
||||||
if (column.label in Zotero.Intl.strings) {
|
if (column.label in Zotero.Intl.strings) {
|
||||||
return Zotero.Intl.strings[column.label];
|
return Zotero.getString(column.label);
|
||||||
}
|
}
|
||||||
else if (/^[^\s]+\w\.\w[^\s]+$/.test(column.label)) {
|
else if (/^[^\s]+\w\.\w[^\s]+$/.test(column.label)) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -1641,7 +1651,7 @@ function formatColumnName(column) {
|
||||||
return column.label;
|
return column.label;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = injectIntl(VirtualizedTable, { forwardRef: true });
|
module.exports = VirtualizedTable;
|
||||||
module.exports.TreeSelection = TreeSelection;
|
module.exports.TreeSelection = TreeSelection;
|
||||||
module.exports.TreeSelectionStub = TreeSelectionStub;
|
module.exports.TreeSelectionStub = TreeSelectionStub;
|
||||||
module.exports.renderCell = renderCell;
|
module.exports.renderCell = renderCell;
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const ReactDOM = require('react-dom');
|
const ReactDOM = require('react-dom');
|
||||||
const PropTypes = require('prop-types');
|
const PropTypes = require('prop-types');
|
||||||
const { IntlProvider } = require('react-intl');
|
|
||||||
const TagSelector = require('components/tagSelector.js');
|
const TagSelector = require('components/tagSelector.js');
|
||||||
const defaults = {
|
const defaults = {
|
||||||
tagColors: new Map(),
|
tagColors: new Map(),
|
||||||
|
|
@ -762,9 +761,7 @@ Zotero.TagSelector = class TagSelectorContainer extends React.PureComponent {
|
||||||
static init(domEl, opts) {
|
static init(domEl, opts) {
|
||||||
var ref;
|
var ref;
|
||||||
let elem = (
|
let elem = (
|
||||||
<IntlProvider locale={Zotero.locale} messages={Zotero.Intl.strings}>
|
<TagSelectorContainer ref={c => ref = c } {...opts} />
|
||||||
<TagSelectorContainer ref={c => ref = c } {...opts} />
|
|
||||||
</IntlProvider>
|
|
||||||
);
|
);
|
||||||
ReactDOM.render(elem, domEl);
|
ReactDOM.render(elem, domEl);
|
||||||
ref.domEl = domEl;
|
ref.domEl = domEl;
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,9 @@ var ZoteroContextPane = new function () {
|
||||||
var _itemContexts = [];
|
var _itemContexts = [];
|
||||||
var _notesContexts = [];
|
var _notesContexts = [];
|
||||||
|
|
||||||
|
var _globalDeckIndex = [];
|
||||||
|
var _preventGlobalDeckChange = false;
|
||||||
|
|
||||||
// Using attribute instead of property to set 'selectedIndex'
|
// Using attribute instead of property to set 'selectedIndex'
|
||||||
// is more reliable
|
// is more reliable
|
||||||
|
|
||||||
|
|
@ -176,6 +179,20 @@ var ZoteroContextPane = new function () {
|
||||||
if (Zotero_Tabs.deck.children.length == 1) {
|
if (Zotero_Tabs.deck.children.length == 1) {
|
||||||
_notesContexts.forEach(x => x.notesListRef.current.setExpanded(false));
|
_notesContexts.forEach(x => x.notesListRef.current.setExpanded(false));
|
||||||
}
|
}
|
||||||
|
// Close tab specific notes if tab id no longer exists, but
|
||||||
|
// do that only when unloaded tab is reloaded
|
||||||
|
setTimeout(() => {
|
||||||
|
var contextNodes = Array.from(_notesPaneDeck.children);
|
||||||
|
for (let contextNode of contextNodes) {
|
||||||
|
var nodes = Array.from(contextNode.querySelector('.zotero-context-pane-tab-notes-deck').children);
|
||||||
|
for (let node of nodes) {
|
||||||
|
var tabID = node.getAttribute('data-tab-id');
|
||||||
|
if (!document.getElementById(tabID)) {
|
||||||
|
node.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else if (action == 'select') {
|
else if (action == 'select') {
|
||||||
// It seems that changing `hidden` or `collapsed` values might
|
// It seems that changing `hidden` or `collapsed` values might
|
||||||
|
|
@ -188,6 +205,14 @@ var ZoteroContextPane = new function () {
|
||||||
_tabCover.hidden = true;
|
_tabCover.hidden = true;
|
||||||
}
|
}
|
||||||
else if (Zotero_Tabs.selectedType == 'reader') {
|
else if (Zotero_Tabs.selectedType == 'reader') {
|
||||||
|
if (_panesDeck.selectedIndex == 1
|
||||||
|
&& _notesPaneDeck.selectedPanel.selectedIndex != 2
|
||||||
|
&& !_preventGlobalDeckChange) {
|
||||||
|
let libraryID = _notesPaneDeck.selectedPanel.getAttribute('data-library-id');
|
||||||
|
_globalDeckIndex[libraryID] = _notesPaneDeck.selectedPanel.selectedIndex;
|
||||||
|
}
|
||||||
|
_preventGlobalDeckChange = false;
|
||||||
|
|
||||||
var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
|
var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
|
||||||
if (reader) {
|
if (reader) {
|
||||||
_tabCover.hidden = false;
|
_tabCover.hidden = false;
|
||||||
|
|
@ -207,11 +232,25 @@ var ZoteroContextPane = new function () {
|
||||||
var notesContext = _getNotesContext(attachment.libraryID);
|
var notesContext = _getNotesContext(attachment.libraryID);
|
||||||
notesContext.updateFromCache();
|
notesContext.updateFromCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let tabNotesDeck = _notesPaneDeck.selectedPanel.querySelector('.zotero-context-pane-tab-notes-deck');
|
||||||
|
let selectedIndex = Array.from(tabNotesDeck.children).findIndex(x => x.getAttribute('data-tab-id') == ids[0]);
|
||||||
|
if (selectedIndex != -1) {
|
||||||
|
tabNotesDeck.setAttribute('selectedIndex', selectedIndex);
|
||||||
|
_notesPaneDeck.selectedPanel.setAttribute('selectedIndex', 2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let libraryID = _notesPaneDeck.selectedPanel.getAttribute('data-library-id');
|
||||||
|
_notesPaneDeck.selectedPanel.setAttribute('selectedIndex', _globalDeckIndex[libraryID] || 0);
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
_contextPaneSplitter.setAttribute('hidden', false);
|
_contextPaneSplitter.setAttribute('hidden', false);
|
||||||
// It seems that on heavy load (i.e. syncing) the line below doesn't set the correct value
|
|
||||||
|
_contextPane.setAttribute('collapsed', !(_contextPaneSplitter.getAttribute('state') != 'collapsed'));
|
||||||
|
// It seems that on heavy load (i.e. syncing) the line below doesn't set the correct value,
|
||||||
|
// therefore we repeat the same operation at the end of JS message queue
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
_contextPane.setAttribute('collapsed', !(_contextPaneSplitter.getAttribute('state') != 'collapsed'));
|
_contextPane.setAttribute('collapsed', !(_contextPaneSplitter.getAttribute('state') != 'collapsed'));
|
||||||
});
|
});
|
||||||
|
|
@ -243,9 +282,14 @@ var ZoteroContextPane = new function () {
|
||||||
|
|
||||||
if (splitter.getAttribute('state') != 'collapsed') {
|
if (splitter.getAttribute('state') != 'collapsed') {
|
||||||
if (_panesDeck.selectedIndex == 1) {
|
if (_panesDeck.selectedIndex == 1) {
|
||||||
var node = _notesPaneDeck.selectedPanel;
|
var libraryContext = _notesPaneDeck.selectedPanel;
|
||||||
if (node.selectedIndex == 1) {
|
// Global note
|
||||||
return node.querySelector('zoteronoteeditor');
|
if (libraryContext.selectedIndex == 1) {
|
||||||
|
return libraryContext.querySelector('zoteronoteeditor');
|
||||||
|
}
|
||||||
|
// Tab specific child note
|
||||||
|
else if (libraryContext.selectedIndex == 2) {
|
||||||
|
return libraryContext.querySelector('.zotero-context-pane-tab-notes-deck').selectedPanel.querySelector('zoteronoteeditor');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -420,11 +464,20 @@ var ZoteroContextPane = new function () {
|
||||||
editor.setAttribute('flex', 1);
|
editor.setAttribute('flex', 1);
|
||||||
noteContainer.append(title, editor);
|
noteContainer.append(title, editor);
|
||||||
|
|
||||||
|
var tabNotesContainer = document.createElement('vbox');
|
||||||
|
var title = document.createElement('vbox');
|
||||||
|
title.className = 'zotero-context-pane-editor-parent-line';
|
||||||
|
let tabNotesDeck = document.createElement('deck');
|
||||||
|
tabNotesDeck.className = 'zotero-context-pane-tab-notes-deck';
|
||||||
|
tabNotesDeck.setAttribute('flex', 1);
|
||||||
|
tabNotesContainer.append(title, tabNotesDeck);
|
||||||
|
|
||||||
let contextNode = document.createElement('deck');
|
let contextNode = document.createElement('deck');
|
||||||
contextNode.append(list, noteContainer);
|
contextNode.append(list, noteContainer, tabNotesContainer);
|
||||||
_notesPaneDeck.append(contextNode);
|
_notesPaneDeck.append(contextNode);
|
||||||
|
|
||||||
contextNode.className = 'context-node';
|
contextNode.className = 'context-node';
|
||||||
|
contextNode.setAttribute('data-library-id', libraryID);
|
||||||
contextNode.setAttribute('selectedIndex', 0);
|
contextNode.setAttribute('selectedIndex', 0);
|
||||||
|
|
||||||
editor.returnHandler = () => {
|
editor.returnHandler = () => {
|
||||||
|
|
@ -440,8 +493,15 @@ var ZoteroContextPane = new function () {
|
||||||
if (!attachment) {
|
if (!attachment) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
var annotations = attachment.getAnnotations().filter(x => x.annotationType != 'ink');
|
||||||
|
if (!annotations.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
var note = await Zotero.EditorInstance.createNoteFromAnnotations(
|
var note = await Zotero.EditorInstance.createNoteFromAnnotations(
|
||||||
attachment.getAnnotations().filter(x => x.annotationType != 'ink'), child && attachment.parentID
|
annotations,
|
||||||
|
{
|
||||||
|
parentID: child && attachment.parentID
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
_updateAddToNote();
|
_updateAddToNote();
|
||||||
|
|
@ -449,7 +509,7 @@ var ZoteroContextPane = new function () {
|
||||||
input.value = '';
|
input.value = '';
|
||||||
_updateNotesList();
|
_updateNotesList();
|
||||||
|
|
||||||
_setPinnedNote(note.id);
|
_setPinnedNote(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _createNote(child) {
|
function _createNote(child) {
|
||||||
|
|
@ -463,10 +523,7 @@ var ZoteroContextPane = new function () {
|
||||||
}
|
}
|
||||||
item.parentID = attachment.parentID;
|
item.parentID = attachment.parentID;
|
||||||
}
|
}
|
||||||
editor.mode = 'edit';
|
_setPinnedNote(item);
|
||||||
editor.item = item;
|
|
||||||
editor.parentItem = null;
|
|
||||||
editor.focus();
|
|
||||||
_updateAddToNote();
|
_updateAddToNote();
|
||||||
|
|
||||||
input.value = '';
|
input.value = '';
|
||||||
|
|
@ -672,7 +729,10 @@ var ZoteroContextPane = new function () {
|
||||||
<NotesList
|
<NotesList
|
||||||
ref={notesListRef}
|
ref={notesListRef}
|
||||||
onClick={(id) => {
|
onClick={(id) => {
|
||||||
_setPinnedNote(id);
|
let item = Zotero.Items.get(id);
|
||||||
|
if (item) {
|
||||||
|
_setPinnedNote(item);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
onContextMenu={(id, event) => {
|
onContextMenu={(id, event) => {
|
||||||
document.getElementById('context-pane-list-move-to-trash').setAttribute('disabled', readOnly);
|
document.getElementById('context-pane-list-move-to-trash').setAttribute('disabled', readOnly);
|
||||||
|
|
@ -727,21 +787,63 @@ var ZoteroContextPane = new function () {
|
||||||
return !Zotero.Libraries.get(libraryID).editable;
|
return !Zotero.Libraries.get(libraryID).editable;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _setPinnedNote(itemID) {
|
function _setPinnedNote(item) {
|
||||||
var item = Zotero.Items.get(itemID);
|
|
||||||
if (!item) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var readOnly = _isLibraryReadOnly(item.libraryID);
|
var readOnly = _isLibraryReadOnly(item.libraryID);
|
||||||
var context = _getNotesContext(item.libraryID);
|
var context = _getNotesContext(item.libraryID);
|
||||||
if (context) {
|
if (context) {
|
||||||
var { editor, node } = context;
|
var { editor, node } = context;
|
||||||
node.setAttribute('selectedIndex', 1);
|
|
||||||
editor.mode = readOnly ? 'view' : 'edit';
|
|
||||||
editor.item = item;
|
|
||||||
editor.parentItem = null;
|
|
||||||
|
|
||||||
node.querySelector('.zotero-context-pane-editor-parent-line').innerHTML = '';
|
let isChild = false;
|
||||||
|
var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
|
||||||
|
if (reader) {
|
||||||
|
let attachment = Zotero.Items.get(reader.itemID);
|
||||||
|
if (attachment.parentItemID == item.parentItemID) {
|
||||||
|
isChild = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tabNotesDeck = _notesPaneDeck.selectedPanel.querySelector('.zotero-context-pane-tab-notes-deck');
|
||||||
|
var parentTitleContainer;
|
||||||
|
if (isChild) {
|
||||||
|
var vbox = document.createElement('vbox');
|
||||||
|
vbox.setAttribute('data-tab-id', Zotero_Tabs.selectedID);
|
||||||
|
vbox.style.display = 'flex';
|
||||||
|
|
||||||
|
editor = document.createElement('zoteronoteeditor');
|
||||||
|
editor.style.display = 'flex';
|
||||||
|
editor.style.width = '100%';
|
||||||
|
vbox.append(editor);
|
||||||
|
|
||||||
|
tabNotesDeck.append(vbox);
|
||||||
|
|
||||||
|
editor.mode = readOnly ? 'view' : 'edit';
|
||||||
|
editor.item = item;
|
||||||
|
editor.parentItem = null;
|
||||||
|
editor.returnHandler = () => {
|
||||||
|
_panesDeck.setAttribute('selectedIndex', 1);
|
||||||
|
_notesPaneDeck.selectedPanel.setAttribute('selectedIndex', 0);
|
||||||
|
vbox.remove();
|
||||||
|
_updateAddToNote();
|
||||||
|
_preventGlobalDeckChange = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
_notesPaneDeck.selectedPanel.setAttribute('selectedIndex', 2);
|
||||||
|
tabNotesDeck.setAttribute('selectedIndex', tabNotesDeck.children.length - 1);
|
||||||
|
|
||||||
|
parentTitleContainer = _notesPaneDeck.selectedPanel.children[2].querySelector('.zotero-context-pane-editor-parent-line');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
node.setAttribute('selectedIndex', 1);
|
||||||
|
editor.mode = readOnly ? 'view' : 'edit';
|
||||||
|
editor.item = item;
|
||||||
|
editor.parentItem = null;
|
||||||
|
|
||||||
|
parentTitleContainer = node.querySelector('.zotero-context-pane-editor-parent-line');
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.focus();
|
||||||
|
|
||||||
|
parentTitleContainer.innerHTML = '';
|
||||||
var parentItem = item.parentItem;
|
var parentItem = item.parentItem;
|
||||||
if (parentItem) {
|
if (parentItem) {
|
||||||
var container = document.createElementNS(HTML_NS, 'div');
|
var container = document.createElementNS(HTML_NS, 'div');
|
||||||
|
|
@ -752,7 +854,7 @@ var ZoteroContextPane = new function () {
|
||||||
title.append(parentItem.getDisplayTitle());
|
title.append(parentItem.getDisplayTitle());
|
||||||
title.className = 'parent-title';
|
title.className = 'parent-title';
|
||||||
container.append(img, title);
|
container.append(img, title);
|
||||||
node.querySelector('.zotero-context-pane-editor-parent-line').append(container);
|
parentTitleContainer.append(container);
|
||||||
}
|
}
|
||||||
_updateAddToNote();
|
_updateAddToNote();
|
||||||
}
|
}
|
||||||
|
|
@ -835,13 +937,13 @@ var ZoteroContextPane = new function () {
|
||||||
|
|
||||||
// Info tab
|
// Info tab
|
||||||
var tabInfo = document.createElement('tab');
|
var tabInfo = document.createElement('tab');
|
||||||
tabInfo.setAttribute('label', Zotero.Intl.strings['zotero.tabs.info.label']);
|
tabInfo.setAttribute('label', Zotero.getString('zotero.tabs.info.label'));
|
||||||
// Tags tab
|
// Tags tab
|
||||||
var tabTags = document.createElement('tab');
|
var tabTags = document.createElement('tab');
|
||||||
tabTags.setAttribute('label', Zotero.Intl.strings['zotero.tabs.tags.label']);
|
tabTags.setAttribute('label', Zotero.getString('zotero.tabs.tags.label'));
|
||||||
// Related tab
|
// Related tab
|
||||||
var tabRelated = document.createElement('tab');
|
var tabRelated = document.createElement('tab');
|
||||||
tabRelated.setAttribute('label', Zotero.Intl.strings['zotero.tabs.related.label']);
|
tabRelated.setAttribute('label', Zotero.getString('zotero.tabs.related.label'));
|
||||||
|
|
||||||
tabs.append(tabInfo, tabTags, tabRelated);
|
tabs.append(tabInfo, tabTags, tabRelated);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ async function doAccept() {
|
||||||
);
|
);
|
||||||
|
|
||||||
// If we successfully created a parent, return it
|
// If we successfully created a parent, return it
|
||||||
if (newItems) {
|
if (newItems.length) {
|
||||||
io.dataOut = { parent: newItems[0] };
|
io.dataOut = { parent: newItems[0] };
|
||||||
window.close();
|
window.close();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,10 +93,11 @@ Zotero_File_Exporter.prototype.save = async function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Otherwise exclude note export translators
|
}
|
||||||
else {
|
|
||||||
translators = translators.filter(t => !t.configOptions || !t.configOptions.noteTranslator);
|
// Exclude note translators if not exporting notes
|
||||||
}
|
if (!exportingNotes) {
|
||||||
|
translators = translators.filter(t => !t.configOptions || !t.configOptions.noteTranslator);
|
||||||
}
|
}
|
||||||
|
|
||||||
// present options dialog
|
// present options dialog
|
||||||
|
|
@ -238,26 +239,29 @@ var Zotero_File_Interface = new function() {
|
||||||
/*
|
/*
|
||||||
* exports items to clipboard
|
* exports items to clipboard
|
||||||
*/
|
*/
|
||||||
function exportItemsToClipboard(items, translatorID) {
|
function exportItemsToClipboard(items, format) {
|
||||||
function _translate(items, translatorID, callback) {
|
function _translate(items, format, callback) {
|
||||||
let translation = new Zotero.Translate.Export();
|
let translation = new Zotero.Translate.Export();
|
||||||
translation.setItems(items.slice());
|
translation.setItems(items.slice());
|
||||||
translation.setTranslator(translatorID);
|
translation.setTranslator(format.id);
|
||||||
|
if (format.options) {
|
||||||
|
translation.setDisplayOptions(format.options);
|
||||||
|
}
|
||||||
translation.setHandler("done", callback);
|
translation.setHandler("done", callback);
|
||||||
translation.translate();
|
translation.translate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If translating with virtual "Markdown + Rich Text" translator, use Note Markdown and
|
// If translating with virtual "Markdown + Rich Text" translator, use Note Markdown and
|
||||||
// Note HTML instead
|
// Note HTML instead
|
||||||
if (translatorID == Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) {
|
if (format.id == Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) {
|
||||||
translatorID = Zotero.Translators.TRANSLATOR_ID_NOTE_MARKDOWN;
|
let markdownFormat = { mode: 'export', id: Zotero.Translators.TRANSLATOR_ID_NOTE_MARKDOWN, options: format.markdownOptions };
|
||||||
_translate(items, translatorID, (obj, worked) => {
|
let htmlFormat = { mode: 'export', id: Zotero.Translators.TRANSLATOR_ID_NOTE_HTML, options: format.htmlOptions };
|
||||||
|
_translate(items, markdownFormat, (obj, worked) => {
|
||||||
if (!worked) {
|
if (!worked) {
|
||||||
Zotero.log(Zotero.getString('fileInterface.exportError'), 'warning');
|
Zotero.log(Zotero.getString('fileInterface.exportError'), 'warning');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
translatorID = Zotero.Translators.TRANSLATOR_ID_NOTE_HTML;
|
_translate(items, htmlFormat, (obj2, worked) => {
|
||||||
_translate(items, translatorID, (obj2, worked) => {
|
|
||||||
if (!worked) {
|
if (!worked) {
|
||||||
Zotero.log(Zotero.getString('fileInterface.exportError'), 'warning');
|
Zotero.log(Zotero.getString('fileInterface.exportError'), 'warning');
|
||||||
return;
|
return;
|
||||||
|
|
@ -293,14 +297,14 @@ var Zotero_File_Interface = new function() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
_translate(items, translatorID, (obj, worked) => {
|
_translate(items, format, (obj, worked) => {
|
||||||
if (!worked) {
|
if (!worked) {
|
||||||
Zotero.log(Zotero.getString('fileInterface.exportError'), 'warning');
|
Zotero.log(Zotero.getString('fileInterface.exportError'), 'warning');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let text = obj.string;
|
let text = obj.string;
|
||||||
// For Note HTML translator use body content only
|
// For Note HTML translator use body content only
|
||||||
if (translatorID == Zotero.Translators.TRANSLATOR_ID_NOTE_HTML) {
|
if (format.id == Zotero.Translators.TRANSLATOR_ID_NOTE_HTML) {
|
||||||
let parser = Components.classes['@mozilla.org/xmlextras/domparser;1']
|
let parser = Components.classes['@mozilla.org/xmlextras/domparser;1']
|
||||||
.createInstance(Components.interfaces.nsIDOMParser);
|
.createInstance(Components.interfaces.nsIDOMParser);
|
||||||
let doc = parser.parseFromString(text, 'text/html');
|
let doc = parser.parseFromString(text, 'text/html');
|
||||||
|
|
@ -441,10 +445,13 @@ var Zotero_File_Interface = new function() {
|
||||||
|
|
||||||
var translation;
|
var translation;
|
||||||
|
|
||||||
if (options.mendeleyCode) {
|
if (options.mendeleyAuth || options.mendeleyCode) {
|
||||||
translation = yield _getMendeleyTranslation();
|
translation = yield _getMendeleyTranslation();
|
||||||
translation.createNewCollection = createNewCollection;
|
translation.createNewCollection = createNewCollection;
|
||||||
|
translation.mendeleyAuth = options.mendeleyAuth;
|
||||||
translation.mendeleyCode = options.mendeleyCode;
|
translation.mendeleyCode = options.mendeleyCode;
|
||||||
|
translation.newItemsOnly = options.newItemsOnly;
|
||||||
|
translation.relinkOnly = options.relinkOnly;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Check if the file is an SQLite database
|
// Check if the file is an SQLite database
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,9 @@ const parseCitavi5Quads = (quadsRaw) => {
|
||||||
const ImportCitaviAnnotatons = async (translation) => {
|
const ImportCitaviAnnotatons = async (translation) => {
|
||||||
const IDMap = translation._itemSaver._IDMap;
|
const IDMap = translation._itemSaver._IDMap;
|
||||||
const ZU = translation._sandboxZotero.Utilities;
|
const ZU = translation._sandboxZotero.Utilities;
|
||||||
|
|
||||||
|
// stream might be closed by now, re-init to make sure getXML() works
|
||||||
|
translation._io.init('xml/dom');
|
||||||
const doc = translation._sandboxZotero.getXML();
|
const doc = translation._sandboxZotero.getXML();
|
||||||
const isCitavi5 = ZU.xpathText(doc, '//CitaviExchangeData/@Version').startsWith('5');
|
const isCitavi5 = ZU.xpathText(doc, '//CitaviExchangeData/@Version').startsWith('5');
|
||||||
var annotationNodes = ZU.xpath(doc, '//Annotations/Annotation');
|
var annotationNodes = ZU.xpath(doc, '//Annotations/Annotation');
|
||||||
|
|
@ -35,11 +38,23 @@ const ImportCitaviAnnotatons = async (translation) => {
|
||||||
const quadsRaw = ZU.xpathText(annotationNodes[i], './Quads');
|
const quadsRaw = ZU.xpathText(annotationNodes[i], './Quads');
|
||||||
const locationID = ZU.xpathText(annotationNodes[i], './LocationID');
|
const locationID = ZU.xpathText(annotationNodes[i], './LocationID');
|
||||||
|
|
||||||
const location = ZU.xpath(doc, `//Locations/Location[@${isCitavi5 ? 'ID' : 'id'}='${locationID}']`)[0];
|
const location = ZU.xpath(doc, `//Locations/Location[@id='${locationID}']|//Locations/Location[@ID='${locationID}']`)[0];
|
||||||
|
|
||||||
|
if (!location) {
|
||||||
|
Zotero.debug(`Missing <Location> entry for annotation ${id}, skipping...`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const referenceID = ZU.xpathText(location, './ReferenceID');
|
const referenceID = ZU.xpathText(location, './ReferenceID');
|
||||||
const entityLink = ZU.xpath(doc, `//EntityLinks/EntityLink[TargetID='${id}']`)[0];
|
const entityLink = ZU.xpath(doc, `//EntityLinks/EntityLink[TargetID='${id}']`)[0];
|
||||||
|
|
||||||
|
if (!entityLink) {
|
||||||
|
Zotero.debug(`Missing <EntityLink> entry for annotation ${id}, skipping...`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const entitySourceID = ZU.xpathText(entityLink, './SourceID');
|
const entitySourceID = ZU.xpathText(entityLink, './SourceID');
|
||||||
const knowledgeItem = ZU.xpath(doc, `//KnowledgeItems/KnowledgeItem[@${isCitavi5 ? 'ID' : 'id'}='${entitySourceID}']`)[0];
|
const knowledgeItem = ZU.xpath(doc, `//KnowledgeItems/KnowledgeItem[@id='${entitySourceID}']|//KnowledgeItems/KnowledgeItem[@ID='${entitySourceID}']`)[0];
|
||||||
const keywordsIDsText = ZU.xpathText(doc, `//KnowledgeItemKeywords/OnetoN[starts-with(text(), "${entitySourceID}")]`);
|
const keywordsIDsText = ZU.xpathText(doc, `//KnowledgeItemKeywords/OnetoN[starts-with(text(), "${entitySourceID}")]`);
|
||||||
const keywords = keywordsIDsText
|
const keywords = keywordsIDsText
|
||||||
? keywordsIDsText
|
? keywordsIDsText
|
||||||
|
|
@ -135,15 +150,21 @@ const ImportCitaviAnnotatons = async (translation) => {
|
||||||
annotations.push(annotation);
|
annotations.push(annotation);
|
||||||
});
|
});
|
||||||
|
|
||||||
annotations = await Zotero.PDFWorker.processCitaviAnnotations(
|
|
||||||
itemAttachment.getFilePath(), annotations
|
|
||||||
);
|
|
||||||
|
|
||||||
annotations.forEach((annotation) => {
|
try {
|
||||||
promises.push(Zotero.Annotations.saveFromJSON(
|
// eslint-disable-next-line no-await-in-loop
|
||||||
itemAttachment, annotation, { skipSelect: true }
|
annotations = await Zotero.PDFWorker.processCitaviAnnotations(
|
||||||
));
|
itemAttachment.getFilePath(), annotations
|
||||||
});
|
);
|
||||||
|
annotations.forEach((annotation) => {
|
||||||
|
promises.push(Zotero.Annotations.saveFromJSON(
|
||||||
|
itemAttachment, annotation, { skipSelect: true }
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
Zotero.debug(`Could not process annotations for attachment item ${itemAttachment.key} (file path: ${itemAttachment.getFilePath()})`);
|
||||||
|
}
|
||||||
|
|
||||||
progress = baseProgress + Math.ceil((i / annotationNodes.length) * stageProgress);
|
progress = baseProgress + Math.ceil((i / annotationNodes.length) * stageProgress);
|
||||||
translation._runHandler('itemDone', []);
|
translation._runHandler('itemDone', []);
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,19 @@
|
||||||
|
/* global mendeleyAPIUtils:false */
|
||||||
import FilePicker from 'zotero/modules/filePicker';
|
import FilePicker from 'zotero/modules/filePicker';
|
||||||
|
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||||
|
Services.scriptloader.loadSubScript("chrome://zotero/content/import/mendeley/mendeleyAPIUtils.js");
|
||||||
|
const { directAuth } = mendeleyAPIUtils;
|
||||||
|
|
||||||
var Zotero_Import_Wizard = {
|
var Zotero_Import_Wizard = {
|
||||||
_wizard: null,
|
_wizard: null,
|
||||||
_dbs: null,
|
_dbs: null,
|
||||||
_file: null,
|
_file: null,
|
||||||
_translation: null,
|
_translation: null,
|
||||||
_mendeleyOnlineRedirectURLWithCode: null,
|
|
||||||
_mendeleyCode: null,
|
_mendeleyCode: null,
|
||||||
|
_mendeleyAuth: null,
|
||||||
|
_mendeleyHasPreviouslyImported: false,
|
||||||
|
_mendeleyImporterVersion: 0,
|
||||||
|
_isZotfileInstalled: false,
|
||||||
|
|
||||||
init: async function () {
|
init: async function () {
|
||||||
this._wizard = document.getElementById('import-wizard');
|
this._wizard = document.getElementById('import-wizard');
|
||||||
|
|
@ -17,6 +23,18 @@ var Zotero_Import_Wizard = {
|
||||||
//document.getElementById('radio-import-source-mendeley').hidden = false;
|
//document.getElementById('radio-import-source-mendeley').hidden = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const extensions = await Zotero.getInstalledExtensions();
|
||||||
|
this._isZotfileInstalled = !!extensions.find(extName => extName.match(/^ZotFile((?!disabled).)*$/));
|
||||||
|
|
||||||
|
const predicateID = Zotero.RelationPredicates.getID('mendeleyDB:documentUUID');
|
||||||
|
|
||||||
|
if (predicateID) {
|
||||||
|
const relSQL = 'SELECT ROWID FROM itemRelations WHERE predicateID = ? LIMIT 1';
|
||||||
|
this._mendeleyHasPreviouslyImported = !!(await Zotero.DB.valueQueryAsync(relSQL, predicateID));
|
||||||
|
}
|
||||||
|
|
||||||
|
this._mendeleyImporterVersion = parseInt((await Zotero.DB.valueQueryAsync("SELECT value FROM settings WHERE setting='mendeleyImport' AND key='version'")) || 0);
|
||||||
|
|
||||||
// If no existing collections or non-trash items in the library, don't create a new
|
// If no existing collections or non-trash items in the library, don't create a new
|
||||||
// collection by default
|
// collection by default
|
||||||
var args = window.arguments[0].wrappedJSObject;
|
var args = window.arguments[0].wrappedJSObject;
|
||||||
|
|
@ -35,7 +53,11 @@ var Zotero_Import_Wizard = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args && args.mendeleyCode) {
|
if (args && args.pageID) {
|
||||||
|
this._wizard.goTo(args.pageID);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args && args.mendeleyCode && Zotero.Prefs.get("import.mendeleyUseOAuth")) {
|
||||||
this._mendeleyCode = args.mendeleyCode;
|
this._mendeleyCode = args.mendeleyCode;
|
||||||
this._wizard.goTo('page-options');
|
this._wizard.goTo('page-options');
|
||||||
}
|
}
|
||||||
|
|
@ -55,9 +77,29 @@ var Zotero_Import_Wizard = {
|
||||||
Zotero.appName
|
Zotero.appName
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Set up Mendeley username/password fields
|
||||||
|
document.querySelector('label[for="mendeley-username"]').textContent
|
||||||
|
= Zotero.Utilities.Internal.stringWithColon(Zotero.getString('general.username'));
|
||||||
|
document.querySelector('label[for="mendeley-password"]').textContent
|
||||||
|
= Zotero.Utilities.Internal.stringWithColon(Zotero.getString('general.password'));
|
||||||
|
document.getElementById('mendeley-username').addEventListener('keyup', this.onMendeleyAuthKeyUp.bind(this));
|
||||||
|
document.getElementById('mendeley-password').addEventListener('keyup', this.onMendeleyAuthKeyUp.bind(this));
|
||||||
|
|
||||||
|
document.getElementById('relink-only-checkbox').addEventListener('command', this.onRelinkOnlyChange.bind(this));
|
||||||
|
// Set from integration.js prompt
|
||||||
|
if (args && args.relinkOnly) {
|
||||||
|
document.getElementById('relink-only-checkbox').checked = true;
|
||||||
|
this.onRelinkOnlyChange();
|
||||||
|
}
|
||||||
|
|
||||||
Zotero.Translators.init(); // async
|
Zotero.Translators.init(); // async
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onCancel: function () {
|
||||||
|
if (this._translation && this._translation.interrupt) {
|
||||||
|
this._translation.interrupt();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
onModeChosen: async function () {
|
onModeChosen: async function () {
|
||||||
var wizard = this._wizard;
|
var wizard = this._wizard;
|
||||||
|
|
@ -70,6 +112,14 @@ var Zotero_Import_Wizard = {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'radio-import-source-mendeley-online':
|
case 'radio-import-source-mendeley-online':
|
||||||
|
if (this._isZotfileInstalled) {
|
||||||
|
this._onDone(
|
||||||
|
Zotero.getString('general.error'),
|
||||||
|
Zotero.getString('import.online.blockedByPlugin', 'ZotFile'),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
wizard.goTo('mendeley-online-explanation');
|
wizard.goTo('mendeley-online-explanation');
|
||||||
wizard.canRewind = true;
|
wizard.canRewind = true;
|
||||||
break;
|
break;
|
||||||
|
|
@ -103,19 +153,58 @@ var Zotero_Import_Wizard = {
|
||||||
},
|
},
|
||||||
|
|
||||||
onMendeleyOnlineShow: async function () {
|
onMendeleyOnlineShow: async function () {
|
||||||
document.getElementById('mendeley-online-description').textContent = Zotero.getString(
|
document.getElementById('mendeley-online-description').textContent = Zotero.Prefs.get("import.mendeleyUseOAuth")
|
||||||
'import.online.intro', [Zotero.appName, 'Mendeley Reference Manager', 'Mendeley']
|
? Zotero.getString('import.online.intro', [Zotero.appName, 'Mendeley Reference Manager', 'Mendeley'])
|
||||||
);
|
: Zotero.getString('import.online.formIntro', [Zotero.appName, 'Mendeley Reference Manager', 'Mendeley']);
|
||||||
document.getElementById('mendeley-online-description2').textContent = Zotero.getString(
|
document.getElementById('mendeley-online-description2').textContent = Zotero.getString(
|
||||||
'import.online.intro2', [Zotero.appName, 'Mendeley']
|
'import.online.intro2', [Zotero.appName, 'Mendeley']
|
||||||
);
|
);
|
||||||
|
document.getElementById('mendeley-login').style.display = Zotero.Prefs.get("import.mendeleyUseOAuth") ? 'none' : '';
|
||||||
|
document.getElementById('mendeley-online-login-feedback').style.display = 'none';
|
||||||
|
this._wizard.canAdvance = Zotero.Prefs.get("import.mendeleyUseOAuth");
|
||||||
},
|
},
|
||||||
|
|
||||||
onMendeleyOnlineAdvance: function () {
|
onMendeleyOnlineAdvance: async function () {
|
||||||
if (!this._mendeleyOnlineRedirectURLWithCode) {
|
if (Zotero.Prefs.get("import.mendeleyUseOAuth")) {
|
||||||
Zotero_File_Interface.authenticateMendeleyOnline();
|
Zotero_File_Interface.authenticateMendeleyOnline();
|
||||||
window.close();
|
window.close();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
const userNameEl = document.getElementById('mendeley-username');
|
||||||
|
const passwordEl = document.getElementById('mendeley-password');
|
||||||
|
userNameEl.disabled = true;
|
||||||
|
passwordEl.disabled = true;
|
||||||
|
try {
|
||||||
|
this._mendeleyAuth = await directAuth(userNameEl.value, passwordEl.value);
|
||||||
|
this._wizard.goTo('page-options');
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
const feedbackEl = document.getElementById('mendeley-online-login-feedback');
|
||||||
|
feedbackEl.textContent = Zotero.getString('import.online.wrongCredentials', ['Mendeley']);
|
||||||
|
feedbackEl.style.display = '';
|
||||||
|
this._wizard.canAdvance = false; // change to either of the inputs will reset thi
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
userNameEl.disabled = false;
|
||||||
|
passwordEl.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onMendeleyAuthKeyUp: function () {
|
||||||
|
document.getElementById('mendeley-online-login-feedback').style.display = 'none';
|
||||||
|
this._wizard.canAdvance = document.getElementById('mendeley-username').value.length > 0
|
||||||
|
&& document.getElementById('mendeley-password').value.length > 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
onRelinkOnlyChange: function () {
|
||||||
|
if (document.getElementById('relink-only-checkbox').checked) {
|
||||||
|
document.getElementById('new-items-only-checkbox').checked = true;
|
||||||
|
document.getElementById('create-collection-checkbox').checked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('new-items-only-checkbox').disabled = document.getElementById('relink-only-checkbox').checked;
|
||||||
|
document.getElementById('create-collection-checkbox').disabled = document.getElementById('relink-only-checkbox').checked;
|
||||||
},
|
},
|
||||||
|
|
||||||
goToStart: function () {
|
goToStart: function () {
|
||||||
|
|
@ -196,7 +285,17 @@ var Zotero_Import_Wizard = {
|
||||||
|
|
||||||
|
|
||||||
onOptionsShown: function () {
|
onOptionsShown: function () {
|
||||||
document.getElementById('file-handling-options').hidden = !!this._mendeleyCode;
|
document.getElementById('file-handling-options').hidden = !!(this._mendeleyAuth || this._mendeleyCode);
|
||||||
|
const hideExtraMendeleyOptions = !this._mendeleyHasPreviouslyImported || !(this._mendeleyAuth || this._mendeleyCode);
|
||||||
|
document.getElementById('mendeley-options').hidden = hideExtraMendeleyOptions;
|
||||||
|
document.getElementById('relink-only-wrapper').hidden = hideExtraMendeleyOptions || this._mendeleyImporterVersion > 0;
|
||||||
|
document.getElementById('relink-only-more-info').addEventListener('click', function () {
|
||||||
|
Zotero.launchURL('https://www.zotero.org/support/kb/mendeley_import#using_mendeley_citations');
|
||||||
|
window.close();
|
||||||
|
});
|
||||||
|
if (hideExtraMendeleyOptions) {
|
||||||
|
document.getElementById('new-items-only-checkbox').removeAttribute('checked');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -223,7 +322,7 @@ var Zotero_Import_Wizard = {
|
||||||
|
|
||||||
|
|
||||||
onImportStart: async function () {
|
onImportStart: async function () {
|
||||||
if (!this._file && !this._mendeleyCode) {
|
if (!this._file && !(this._mendeleyAuth || this._mendeleyCode)) {
|
||||||
let index = document.getElementById('file-list').selectedIndex;
|
let index = document.getElementById('file-list').selectedIndex;
|
||||||
this._file = this._dbs[index].path;
|
this._file = this._dbs[index].path;
|
||||||
}
|
}
|
||||||
|
|
@ -235,10 +334,12 @@ var Zotero_Import_Wizard = {
|
||||||
let result = await Zotero_File_Interface.importFile({
|
let result = await Zotero_File_Interface.importFile({
|
||||||
file: this._file,
|
file: this._file,
|
||||||
onBeforeImport: this.onBeforeImport.bind(this),
|
onBeforeImport: this.onBeforeImport.bind(this),
|
||||||
addToLibraryRoot: !document.getElementById('create-collection-checkbox')
|
addToLibraryRoot: !document.getElementById('create-collection-checkbox').checked,
|
||||||
.hasAttribute('checked'),
|
|
||||||
linkFiles: document.getElementById('file-handling-radio').selectedIndex == 1,
|
linkFiles: document.getElementById('file-handling-radio').selectedIndex == 1,
|
||||||
mendeleyCode: this._mendeleyCode
|
mendeleyAuth: this._mendeleyAuth,
|
||||||
|
mendeleyCode: this._mendeleyCode,
|
||||||
|
newItemsOnly: document.getElementById('new-items-only-checkbox').checked,
|
||||||
|
relinkOnly: document.getElementById('relink-only-checkbox').checked
|
||||||
});
|
});
|
||||||
|
|
||||||
// Cancelled by user or due to error
|
// Cancelled by user or due to error
|
||||||
|
|
@ -248,9 +349,12 @@ var Zotero_Import_Wizard = {
|
||||||
}
|
}
|
||||||
|
|
||||||
let numItems = this._translation.newItems.length;
|
let numItems = this._translation.newItems.length;
|
||||||
|
let numRelinked = this._translation.numRelinked;
|
||||||
this._onDone(
|
this._onDone(
|
||||||
Zotero.getString('fileInterface.importComplete'),
|
Zotero.getString('fileInterface.importComplete'),
|
||||||
Zotero.getString(`fileInterface.itemsWereImported`, numItems, numItems)
|
document.getElementById('relink-only-checkbox').checked
|
||||||
|
? Zotero.getString(`fileInterface.itemsWereRelinked`, numRelinked, numRelinked)
|
||||||
|
: Zotero.getString(`fileInterface.itemsWereImported`, numItems, numItems)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||||
title="&zotero.import;"
|
title="&zotero.import;"
|
||||||
|
onwizardcancel="Zotero_Import_Wizard.onCancel()"
|
||||||
onload="Zotero_Import_Wizard.init()">
|
onload="Zotero_Import_Wizard.init()">
|
||||||
|
|
||||||
<script src="../include.js"/>
|
<script src="../include.js"/>
|
||||||
|
|
@ -36,6 +37,17 @@
|
||||||
>
|
>
|
||||||
<description id="mendeley-online-description" />
|
<description id="mendeley-online-description" />
|
||||||
<description id="mendeley-online-description2" />
|
<description id="mendeley-online-description2" />
|
||||||
|
<html:fieldset id="mendeley-login">
|
||||||
|
<html:div class="field">
|
||||||
|
<html:label for="mendeley-username"/>
|
||||||
|
<html:input type="text" id="mendeley-username" />
|
||||||
|
</html:div>
|
||||||
|
<html:div class="field">
|
||||||
|
<html:label for="mendeley-password"/>
|
||||||
|
<html:input type="password" id="mendeley-password" />
|
||||||
|
</html:div>
|
||||||
|
</html:fieldset>
|
||||||
|
<description id="mendeley-online-login-feedback" />
|
||||||
</wizardpage>
|
</wizardpage>
|
||||||
|
|
||||||
<wizardpage pageid="page-file-list"
|
<wizardpage pageid="page-file-list"
|
||||||
|
|
@ -66,6 +78,14 @@
|
||||||
onpageshow="Zotero_Import_Wizard.onOptionsShown()"
|
onpageshow="Zotero_Import_Wizard.onOptionsShown()"
|
||||||
onpagerewound="return Zotero_Import_Wizard.goToStart()"
|
onpagerewound="return Zotero_Import_Wizard.goToStart()"
|
||||||
onpageadvanced="Zotero_Import_Wizard.onImportStart()">
|
onpageadvanced="Zotero_Import_Wizard.onImportStart()">
|
||||||
|
|
||||||
|
<vbox id="relink-only-wrapper">
|
||||||
|
<hbox align="center">
|
||||||
|
<checkbox id="relink-only-checkbox" label="&zotero.import.online.relinkOnly;" />
|
||||||
|
<label id="relink-only-more-info" class="zotero-text-link" value="&zotero.general.moreInformation;"/>
|
||||||
|
</hbox>
|
||||||
|
</vbox>
|
||||||
|
|
||||||
<checkbox id="create-collection-checkbox" label="&zotero.import.createCollection;" checked="true" />
|
<checkbox id="create-collection-checkbox" label="&zotero.import.createCollection;" checked="true" />
|
||||||
|
|
||||||
<vbox id="file-handling-options">
|
<vbox id="file-handling-options">
|
||||||
|
|
@ -76,6 +96,9 @@
|
||||||
</radiogroup>
|
</radiogroup>
|
||||||
<description id="file-handling-description"/>
|
<description id="file-handling-description"/>
|
||||||
</vbox>
|
</vbox>
|
||||||
|
<vbox id="mendeley-options">
|
||||||
|
<checkbox id="new-items-only-checkbox" label="&zotero.import.online.newItemsOnly;" checked="true" />
|
||||||
|
</vbox>
|
||||||
</wizardpage>
|
</wizardpage>
|
||||||
|
|
||||||
<wizardpage pageid="page-progress"
|
<wizardpage pageid="page-progress"
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,67 @@
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
var mendeleyAPIUtils = (function () {
|
var mendeleyAPIUtils = (function () {
|
||||||
const OAUTH_URL = 'https://www.zotero.org/utils/mendeley/oauth';
|
const ZOTERO_OAUTH_URL = 'https://www.zotero.org/utils/mendeley/oauth';
|
||||||
|
const OAUTH_URL = 'https://api.mendeley.com/oauth/token';
|
||||||
const MENDELEY_API_URL = 'https://api.mendeley.com';
|
const MENDELEY_API_URL = 'https://api.mendeley.com';
|
||||||
|
const CLIENT_ID = '6';
|
||||||
|
const CLIENT_NOT_VERY_SECRET = 'JtSAMzFdwC6RAED3RMZU';
|
||||||
|
const USER_AGENT = 'Mendeley Desktop/1.18';
|
||||||
|
|
||||||
const getTokens = async (codeOrRefreshToken, isRefresh = false) => {
|
const getTokens = async (url, bodyProps, headers = {}, options = {}) => {
|
||||||
const options = {
|
const body = Object.entries(bodyProps)
|
||||||
body: isRefresh
|
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
|
||||||
? `grant_type=refresh_token&refresh_token=${codeOrRefreshToken}`
|
.join('&');
|
||||||
: `grant_type=authorization_code&code=${codeOrRefreshToken}`,
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const response = await Zotero.HTTP.request('POST', OAUTH_URL, options);
|
|
||||||
|
|
||||||
|
headers = { ...headers, 'Content-Type': 'application/x-www-form-urlencoded' };
|
||||||
|
|
||||||
|
if (!Zotero.Prefs.get('import.mendeleyUseOAuth')) {
|
||||||
|
headers['User-Agent'] = USER_AGENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
options = { ...options, body, headers, timeout: 30000 };
|
||||||
|
const response = await Zotero.HTTP.request('POST', url, options);
|
||||||
return JSON.parse(response.responseText);
|
return JSON.parse(response.responseText);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const directAuth = async (username, password, headers = {}, options = {}) => {
|
||||||
|
const bodyProps = {
|
||||||
|
client_id: CLIENT_ID, // eslint-disable-line camelcase
|
||||||
|
client_secret: CLIENT_NOT_VERY_SECRET, // eslint-disable-line camelcase
|
||||||
|
grant_type: 'password', // eslint-disable-line camelcase
|
||||||
|
password,
|
||||||
|
scope: 'all',
|
||||||
|
username
|
||||||
|
};
|
||||||
|
|
||||||
|
return getTokens(OAUTH_URL, bodyProps, headers, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
const codeAuth = async (code, headers = {}, options = {}) => {
|
||||||
|
const bodyProps = {
|
||||||
|
grant_type: 'authorization_code', // eslint-disable-line camelcase
|
||||||
|
code,
|
||||||
|
};
|
||||||
|
|
||||||
|
return getTokens(ZOTERO_OAUTH_URL, bodyProps, headers, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
const refreshAuth = async (refreshToken, headers = {}, options = {}) => {
|
||||||
|
const bodyProps = {
|
||||||
|
grant_type: 'refresh_token', // eslint-disable-line camelcase
|
||||||
|
refresh_token: refreshToken, // eslint-disable-line camelcase
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!Zotero.Prefs.get('import.mendeleyUseOAuth')) {
|
||||||
|
bodyProps.client_id = CLIENT_ID; // eslint-disable-line camelcase
|
||||||
|
bodyProps.client_secret = CLIENT_NOT_VERY_SECRET; // eslint-disable-line camelcase
|
||||||
|
}
|
||||||
|
|
||||||
|
return getTokens(
|
||||||
|
Zotero.Prefs.get('import.mendeleyUseOAuth') ? ZOTERO_OAUTH_URL : OAUTH_URL,
|
||||||
|
bodyProps, headers, options
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const getNextLinkFromResponse = (response) => {
|
const getNextLinkFromResponse = (response) => {
|
||||||
let next = null;
|
let next = null;
|
||||||
let links = response.getResponseHeader('link');
|
let links = response.getResponseHeader('link');
|
||||||
|
|
@ -33,7 +78,7 @@ const getNextLinkFromResponse = (response) => {
|
||||||
|
|
||||||
const apiFetchUrl = async (tokens, url, headers = {}, options = {}) => {
|
const apiFetchUrl = async (tokens, url, headers = {}, options = {}) => {
|
||||||
headers = { ...headers, Authorization: `Bearer ${tokens.access_token}` };
|
headers = { ...headers, Authorization: `Bearer ${tokens.access_token}` };
|
||||||
options = { ...options, headers };
|
options = { ...options, headers, timeout: 60000 };
|
||||||
const method = 'GET';
|
const method = 'GET';
|
||||||
|
|
||||||
// Run the request. If we see 401 or 403, try to refresh tokens and run the request again
|
// Run the request. If we see 401 or 403, try to refresh tokens and run the request again
|
||||||
|
|
@ -42,7 +87,7 @@ const apiFetchUrl = async (tokens, url, headers = {}, options = {}) => {
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
if (e.status === 401 || e.status === 403) {
|
if (e.status === 401 || e.status === 403) {
|
||||||
const newTokens = await getTokens(tokens.refresh_token, true);
|
const newTokens = await refreshAuth(tokens.refresh_token);
|
||||||
// update tokens in the tokens object and in the header for next request
|
// update tokens in the tokens object and in the header for next request
|
||||||
tokens.access_token = newTokens.access_token; // eslint-disable-line camelcase
|
tokens.access_token = newTokens.access_token; // eslint-disable-line camelcase
|
||||||
tokens.refresh_token = newTokens.refresh_token; // eslint-disable-line camelcase
|
tokens.refresh_token = newTokens.refresh_token; // eslint-disable-line camelcase
|
||||||
|
|
@ -64,21 +109,22 @@ const get = async (tokens, endPoint, params = {}, headers = {}, options = {}) =>
|
||||||
return JSON.parse(response.responseText);
|
return JSON.parse(response.responseText);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAll = async (tokens, endPoint, params = {}, headers = {}, options = {}) => {
|
const getAll = async (tokens, endPoint, params = {}, headers = {}, options = {}, interruptChecker = () => {}) => {
|
||||||
const PER_PAGE = endPoint === 'annotations' ? 200 : 500;
|
const PER_PAGE = endPoint === 'annotations' ? 200 : 500;
|
||||||
const response = await apiFetch(tokens, endPoint, { ...params, limit: PER_PAGE }, headers, options);
|
const response = await apiFetch(tokens, endPoint, { ...params, limit: PER_PAGE }, headers, options);
|
||||||
var next = getNextLinkFromResponse(response);
|
var next = getNextLinkFromResponse(response);
|
||||||
var data = JSON.parse(response.responseText);
|
var data = JSON.parse(response.responseText);
|
||||||
|
interruptChecker();
|
||||||
|
|
||||||
while (next) {
|
while (next) {
|
||||||
const response = await apiFetchUrl(tokens, next, headers, options); //eslint-disable-line no-await-in-loop
|
const response = await apiFetchUrl(tokens, next, headers, options); //eslint-disable-line no-await-in-loop
|
||||||
data = [...data, ...JSON.parse(response.responseText)];
|
data = [...data, ...JSON.parse(response.responseText)];
|
||||||
next = getNextLinkFromResponse(response);
|
next = getNextLinkFromResponse(response);
|
||||||
|
interruptChecker();
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return { codeAuth, directAuth, getNextLinkFromResponse, apiFetch, apiFetchUrl, get, getAll };
|
||||||
return { getNextLinkFromResponse, getTokens, apiFetch, apiFetchUrl, get, getAll };
|
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,67 @@
|
||||||
/* global map:false, mendeleyOnlineMappings:false, mendeleyAPIUtils:false */
|
/* eslint-disable no-await-in-loop, camelcase */
|
||||||
var EXPORTED_SYMBOLS = ["Zotero_Import_Mendeley"];
|
/* global mendeleyDBMaps:false, mendeleyOnlineMappings:false, mendeleyAPIUtils:false */
|
||||||
|
var EXPORTED_SYMBOLS = ["Zotero_Import_Mendeley"]; //eslint-disable-line no-unused-vars
|
||||||
|
|
||||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||||
Components.utils.import("resource://gre/modules/osfile.jsm");
|
Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||||
Services.scriptloader.loadSubScript("chrome://zotero/content/include.js");
|
Services.scriptloader.loadSubScript("chrome://zotero/content/include.js");
|
||||||
Services.scriptloader.loadSubScript("chrome://zotero/content/import/mendeley/mendeleyOnlineMappings.js");
|
Services.scriptloader.loadSubScript("chrome://zotero/content/import/mendeley/mendeleyOnlineMappings.js");
|
||||||
Services.scriptloader.loadSubScript("chrome://zotero/content/import/mendeley/mendeleyAPIUtils.js");
|
Services.scriptloader.loadSubScript("chrome://zotero/content/import/mendeley/mendeleyAPIUtils.js");
|
||||||
|
Services.scriptloader.loadSubScript("chrome://zotero/content/import/mendeley/mendeleySchemaMap.js");
|
||||||
|
|
||||||
|
const importerVersion = 1;
|
||||||
const { apiTypeToDBType, apiFieldToDBField } = mendeleyOnlineMappings;
|
const { apiTypeToDBType, apiFieldToDBField } = mendeleyOnlineMappings;
|
||||||
const { apiFetch, get, getAll, getTokens } = mendeleyAPIUtils;
|
const { apiFetch, codeAuth, directAuth, get, getAll } = mendeleyAPIUtils;
|
||||||
|
|
||||||
|
const colorMap = new Map();
|
||||||
|
colorMap.set('rgb(255, 245, 173)', '#ffd400');
|
||||||
|
colorMap.set('#fff5ad', '#ffd400');
|
||||||
|
colorMap.set('rgb(255, 181, 182)', '#ff6666');
|
||||||
|
colorMap.set('#ffb5b6', '#ff6666');
|
||||||
|
colorMap.set('rgb(186, 226, 255)', '#2ea8e5');
|
||||||
|
colorMap.set('#bae2ff', '#2ea8e5');
|
||||||
|
colorMap.set('rgb(211, 194, 255)', '#a28ae5');
|
||||||
|
colorMap.set('#d3c2ff', '#a28ae5');
|
||||||
|
colorMap.set('rgb(220, 255, 176)', '#5fb236');
|
||||||
|
colorMap.set('#dcffb0', '#5fb236');
|
||||||
|
|
||||||
var Zotero_Import_Mendeley = function () {
|
var Zotero_Import_Mendeley = function () {
|
||||||
this.createNewCollection = null;
|
this.createNewCollection = null;
|
||||||
this.linkFiles = null;
|
this.linkFiles = null;
|
||||||
this.newItems = [];
|
this.newItems = [];
|
||||||
this.mendeleyCode = null;
|
this.newCollections = [];
|
||||||
|
this.mendeleyAuth = null;
|
||||||
|
this.newItemsOnly = false;
|
||||||
|
this.relinkOnly = false;
|
||||||
|
this.numRelinked = 0;
|
||||||
|
|
||||||
this._tokens = null;
|
this._tokens = null;
|
||||||
this._db;
|
this._db = null;
|
||||||
this._file;
|
this._file = null;
|
||||||
this._saveOptions = null;
|
this._saveOptions = null;
|
||||||
this._itemDone;
|
this._itemDone = () => {};
|
||||||
this._progress = 0;
|
this._progress = 0;
|
||||||
this._progressMax;
|
this._progressMax = 0;
|
||||||
this._tmpFilesToDelete = [];
|
this._tmpFilesToDelete = [];
|
||||||
|
this._caller = null;
|
||||||
|
this._interrupted = false;
|
||||||
|
this._totalSize = 0;
|
||||||
|
this._started = Date.now();
|
||||||
|
this._interruptChecker = (tickProgress = false) => {
|
||||||
|
if (this._interrupted) {
|
||||||
|
throw new Error(`Mendeley import interrupted!
|
||||||
|
Started: ${this._started} (${Math.round((Date.now() - this._started) / 1000)}s ago)
|
||||||
|
Progress: ${this._progress} / ${this._progressMax}
|
||||||
|
New items created: ${this.newItems.length}
|
||||||
|
Total size of files to download: ${Math.round(this._totalSize / 1024)}KB
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tickProgress) {
|
||||||
|
this._progress++;
|
||||||
|
this._itemDone();
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
Zotero_Import_Mendeley.prototype.setLocation = function (file) {
|
Zotero_Import_Mendeley.prototype.setLocation = function (file) {
|
||||||
|
|
@ -32,9 +70,9 @@ Zotero_Import_Mendeley.prototype.setLocation = function (file) {
|
||||||
|
|
||||||
Zotero_Import_Mendeley.prototype.setHandler = function (name, handler) {
|
Zotero_Import_Mendeley.prototype.setHandler = function (name, handler) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case 'itemDone':
|
case 'itemDone':
|
||||||
this._itemDone = handler;
|
this._itemDone = handler;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -57,34 +95,18 @@ Zotero_Import_Mendeley.prototype.translate = async function (options = {}) {
|
||||||
...(options.saveOptions || {})
|
...(options.saveOptions || {})
|
||||||
};
|
};
|
||||||
|
|
||||||
if (true) {
|
this.newItemsOnly = this.newItemsOnly || this.relinkOnly;
|
||||||
Services.scriptloader.loadSubScript("chrome://zotero/content/import/mendeley/mendeleySchemaMap.js");
|
|
||||||
}
|
|
||||||
// TEMP: Load uncached from ~/zotero-client for development
|
|
||||||
else {
|
|
||||||
Components.utils.import("resource://gre/modules/FileUtils.jsm");
|
|
||||||
let file = FileUtils.getDir("Home", []);
|
|
||||||
file = OS.Path.join(file.path, 'zotero-client', 'chrome', 'content', 'zotero', 'import', 'mendeley', 'mendeleySchemaMap.js');
|
|
||||||
let fileURI = OS.Path.toFileURI(file);
|
|
||||||
let xmlhttp = await Zotero.HTTP.request(
|
|
||||||
'GET',
|
|
||||||
fileURI,
|
|
||||||
{
|
|
||||||
noCache: true,
|
|
||||||
responseType: 'text'
|
|
||||||
}
|
|
||||||
);
|
|
||||||
eval(xmlhttp.response);
|
|
||||||
}
|
|
||||||
|
|
||||||
const libraryID = options.libraryID || Zotero.Libraries.userLibraryID;
|
const libraryID = options.libraryID || Zotero.Libraries.userLibraryID;
|
||||||
const { key: rootCollectionKey } = options.collections
|
const { key: rootCollectionKey } = options.collections
|
||||||
? Zotero.Collections.getLibraryAndKeyFromID(options.collections[0])
|
? Zotero.Collections.getLibraryAndKeyFromID(options.collections[0])
|
||||||
: {};
|
: {};
|
||||||
|
|
||||||
|
Zotero.debug(`Begining Mendeley import at ${this._started}. libraryID: ${libraryID}, linkFiles: ${this.linkFiles}, rootCollectionKey: ${rootCollectionKey}, newItemsOnly: ${this.newItemsOnly}, relinkOnly: ${this.relinkOnly}`);
|
||||||
|
|
||||||
// TODO: Get appropriate version based on schema version
|
// TODO: Get appropriate version based on schema version
|
||||||
const mapVersion = 83;
|
const mapVersion = 83;
|
||||||
map = map[mapVersion];
|
const map = mendeleyDBMaps[mapVersion];
|
||||||
|
|
||||||
const mendeleyGroupID = 0;
|
const mendeleyGroupID = 0;
|
||||||
|
|
||||||
|
|
@ -100,22 +122,36 @@ Zotero_Import_Mendeley.prototype.translate = async function (options = {}) {
|
||||||
throw new Error("Not a valid Mendeley database");
|
throw new Error("Not a valid Mendeley database");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.mendeleyCode) {
|
if (this.mendeleyAuth) {
|
||||||
this._tokens = await getTokens(this.mendeleyCode);
|
this._tokens = this.mendeleyAuth;
|
||||||
|
}
|
||||||
|
else if (this.mendeleyCode) {
|
||||||
|
this._tokens = await codeAuth(this.mendeleyCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._file && !this._tokens) {
|
if (!this._file && !this._tokens) {
|
||||||
throw new Error("Missing import token");
|
throw new Error("Missing import token");
|
||||||
}
|
}
|
||||||
|
|
||||||
const folders = this._tokens
|
// we don't know how long the import will be but want to show progress to give
|
||||||
? await this._getFoldersAPI(mendeleyGroupID)
|
// feedback that import has started so we arbitrary set progress at 2%
|
||||||
: await this._getFoldersDB(mendeleyGroupID);
|
this._progress = 1;
|
||||||
|
this._progressMax = 50;
|
||||||
|
this._itemDone();
|
||||||
|
|
||||||
const collectionJSON = this._foldersToAPIJSON(folders, rootCollectionKey);
|
let folderKeys = new Map();
|
||||||
const folderKeys = this._getFolderKeys(collectionJSON);
|
if(!this.relinkOnly) {
|
||||||
await this._saveCollections(libraryID, collectionJSON, folderKeys);
|
const folders = this._tokens
|
||||||
|
? await this._getFoldersAPI(mendeleyGroupID)
|
||||||
|
: await this._getFoldersDB(mendeleyGroupID);
|
||||||
|
|
||||||
|
const collectionJSON = this._foldersToAPIJSON(folders, rootCollectionKey);
|
||||||
|
folderKeys = this._getFolderKeys(collectionJSON);
|
||||||
|
|
||||||
|
await this._saveCollections(libraryID, collectionJSON, folderKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._interruptChecker(true);
|
||||||
//
|
//
|
||||||
// Items
|
// Items
|
||||||
//
|
//
|
||||||
|
|
@ -123,40 +159,59 @@ Zotero_Import_Mendeley.prototype.translate = async function (options = {}) {
|
||||||
? await this._getDocumentsAPI(mendeleyGroupID)
|
? await this._getDocumentsAPI(mendeleyGroupID)
|
||||||
: await this._getDocumentsDB(mendeleyGroupID);
|
: await this._getDocumentsDB(mendeleyGroupID);
|
||||||
|
|
||||||
this._progressMax = documents.length;
|
// Update progress to reflect items to import and remaining meta data stages
|
||||||
|
// We arbitrary set progress at approx 4%. We then add 8, one "tick" for each remaining meta data download.
|
||||||
|
this._progress = Math.max(Math.floor(0.04 * documents.length), 2);
|
||||||
|
this._progressMax = documents.length + this._progress + 8;
|
||||||
// Get various attributes mapped to document ids
|
// Get various attributes mapped to document ids
|
||||||
let urls = this._tokens
|
let urls = this._tokens
|
||||||
? await this._getDocumentURLsAPI(documents)
|
? await this._getDocumentURLsAPI(documents)
|
||||||
: await this._getDocumentURLsDB(mendeleyGroupID);
|
: await this._getDocumentURLsDB(mendeleyGroupID);
|
||||||
|
|
||||||
|
this._interruptChecker(true);
|
||||||
|
|
||||||
let creators = this._tokens
|
let creators = this._tokens
|
||||||
? await this._getDocumentCreatorsAPI(documents)
|
? await this._getDocumentCreatorsAPI(documents)
|
||||||
: await this._getDocumentCreatorsDB(mendeleyGroupID, map.creatorTypes);
|
: await this._getDocumentCreatorsDB(mendeleyGroupID, map.creatorTypes);
|
||||||
|
|
||||||
|
this._interruptChecker(true);
|
||||||
|
|
||||||
let tags = this._tokens
|
let tags = this._tokens
|
||||||
? await this._getDocumentTagsAPI(documents)
|
? await this._getDocumentTagsAPI(documents)
|
||||||
: await this._getDocumentTagsDB(mendeleyGroupID);
|
: await this._getDocumentTagsDB(mendeleyGroupID);
|
||||||
|
|
||||||
let collections = this._tokens
|
this._interruptChecker(true);
|
||||||
|
|
||||||
|
let collections = this.relinkOnly ? new Map() : this._tokens
|
||||||
? await this._getDocumentCollectionsAPI(documents, rootCollectionKey, folderKeys)
|
? await this._getDocumentCollectionsAPI(documents, rootCollectionKey, folderKeys)
|
||||||
: await this._getDocumentCollectionsDB(mendeleyGroupID, documents, rootCollectionKey, folderKeys);
|
: await this._getDocumentCollectionsDB(mendeleyGroupID, documents, rootCollectionKey, folderKeys);
|
||||||
|
|
||||||
let files = this._tokens
|
this._interruptChecker(true);
|
||||||
|
|
||||||
|
let files = this.relinkOnly ? new Map() : this._tokens
|
||||||
? await this._getDocumentFilesAPI(documents)
|
? await this._getDocumentFilesAPI(documents)
|
||||||
: await this._getDocumentFilesDB(mendeleyGroupID);
|
: await this._getDocumentFilesDB(mendeleyGroupID);
|
||||||
|
|
||||||
|
this._interruptChecker(true);
|
||||||
|
|
||||||
let annotations = this._tokens
|
let annotations = this._tokens
|
||||||
? await this._getDocumentAnnotationsAPI(mendeleyGroupID)
|
? await this._getDocumentAnnotationsAPI(mendeleyGroupID)
|
||||||
: await this._getDocumentAnnotationsDB(mendeleyGroupID);
|
: await this._getDocumentAnnotationsDB(mendeleyGroupID);
|
||||||
|
|
||||||
|
this._interruptChecker(true);
|
||||||
|
|
||||||
let profile = this._tokens
|
let profile = this._tokens
|
||||||
? await this._getProfileAPI()
|
? await this._getProfileAPI()
|
||||||
: await this._getProfileDB();
|
: await this._getProfileDB();
|
||||||
|
|
||||||
|
this._interruptChecker(true);
|
||||||
|
|
||||||
let groups = this._tokens
|
let groups = this._tokens
|
||||||
? await this._getGroupsAPI()
|
? await this._getGroupsAPI()
|
||||||
: await this._getGroupsDB();
|
: await this._getGroupsDB();
|
||||||
|
|
||||||
|
this._interruptChecker(true);
|
||||||
|
|
||||||
const fileHashLookup = new Map();
|
const fileHashLookup = new Map();
|
||||||
|
|
||||||
for (let [documentID, fileEntries] of files) {
|
for (let [documentID, fileEntries] of files) {
|
||||||
|
|
@ -165,7 +220,6 @@ Zotero_Import_Mendeley.prototype.translate = async function (options = {}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (let group of groups) {
|
for (let group of groups) {
|
||||||
let groupAnnotations = this._tokens
|
let groupAnnotations = this._tokens
|
||||||
? await this._getDocumentAnnotationsAPI(group.id, profile.id)
|
? await this._getDocumentAnnotationsAPI(group.id, profile.id)
|
||||||
|
|
@ -262,12 +316,20 @@ Zotero_Import_Mendeley.prototype.translate = async function (options = {}) {
|
||||||
annotations.get(document.id)
|
annotations.get(document.id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.newItems.push(Zotero.Items.get(documentIDMap.get(document.id)));
|
this._interruptChecker(true);
|
||||||
this._progress++;
|
|
||||||
if (this._itemDone) {
|
|
||||||
this._itemDone();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (this.newItemsOnly && rootCollectionKey && this.newItems.length === 0) {
|
||||||
|
Zotero.debug(`Mendeley Import detected no new items, removing import collection containing ${this.newCollections.length} collections created during the import`);
|
||||||
|
const rootCollection = await Zotero.Collections.getAsync(options.collections[0]);
|
||||||
|
await rootCollection.eraseTx(this._saveOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Zotero.DB.queryAsync("REPLACE INTO settings VALUES ('mendeleyImport', 'version', ?)", importerVersion);
|
||||||
|
|
||||||
|
Zotero.debug(`Completed Mendeley import in ${Math.round((Date.now() - this._started) / 1000)}s. (Started: ${this._started})`);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
Zotero.logError(e);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
try {
|
try {
|
||||||
|
|
@ -275,6 +337,7 @@ Zotero_Import_Mendeley.prototype.translate = async function (options = {}) {
|
||||||
await this._db.closeDatabase();
|
await this._db.closeDatabase();
|
||||||
}
|
}
|
||||||
if (this._tokens) {
|
if (this._tokens) {
|
||||||
|
Zotero.debug(`Clearing ${this._tmpFilesToDelete.length} temporary files after Mendeley import`);
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
this._tmpFilesToDelete.map(f => this._removeTemporaryFile(f))
|
this._tmpFilesToDelete.map(f => this._removeTemporaryFile(f))
|
||||||
);
|
);
|
||||||
|
|
@ -288,14 +351,20 @@ Zotero_Import_Mendeley.prototype.translate = async function (options = {}) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Zotero_Import_Mendeley.prototype.interrupt = function () {
|
||||||
|
this._interrupted = true;
|
||||||
|
|
||||||
|
if (this._caller) {
|
||||||
|
this._caller.stop();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Zotero_Import_Mendeley.prototype._removeTemporaryFile = async function (file) {
|
Zotero_Import_Mendeley.prototype._removeTemporaryFile = async function (file) {
|
||||||
const containingDir = OS.Path.dirname(file);
|
|
||||||
try {
|
try {
|
||||||
await Zotero.File.removeIfExists(file);
|
await Zotero.File.removeIfExists(file);
|
||||||
await OS.File.removeEmptyDir(containingDir);
|
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
Zotero.logError(e);
|
Zotero.logError("Error while removing temporary file " + file + ": " + e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -345,7 +414,7 @@ Zotero_Import_Mendeley.prototype._getFoldersAPI = async function (groupID) {
|
||||||
if (groupID && groupID !== 0) {
|
if (groupID && groupID !== 0) {
|
||||||
params.group_id = groupID; //eslint-disable-line camelcase
|
params.group_id = groupID; //eslint-disable-line camelcase
|
||||||
}
|
}
|
||||||
return (await getAll(this._tokens, 'folders', params, headers)).map(f => ({
|
return (await getAll(this._tokens, 'folders', params, headers, {}, this._interruptChecker)).map(f => ({
|
||||||
id: f.id,
|
id: f.id,
|
||||||
uuid: f.id,
|
uuid: f.id,
|
||||||
name: f.name,
|
name: f.name,
|
||||||
|
|
@ -365,11 +434,11 @@ Zotero_Import_Mendeley.prototype._foldersToAPIJSON = function (folderRows, paren
|
||||||
};
|
};
|
||||||
|
|
||||||
Zotero_Import_Mendeley.prototype._getFolderDescendents = function (folderID, folderKey, folderRows, maxDepth) {
|
Zotero_Import_Mendeley.prototype._getFolderDescendents = function (folderID, folderKey, folderRows, maxDepth) {
|
||||||
if (maxDepth == 0) return []
|
if (maxDepth == 0) return [];
|
||||||
var descendents = [];
|
var descendents = [];
|
||||||
var children = folderRows
|
var children = folderRows
|
||||||
.filter(f => f.parentId == folderID)
|
.filter(f => f.parentId == folderID)
|
||||||
.map(f => {
|
.map((f) => {
|
||||||
let c = {
|
let c = {
|
||||||
folderID: f.id,
|
folderID: f.id,
|
||||||
remoteUUID: f.remoteUuid,
|
remoteUUID: f.remoteUuid,
|
||||||
|
|
@ -431,6 +500,7 @@ Zotero_Import_Mendeley.prototype._saveCollections = async function (libraryID, j
|
||||||
collection.key = collectionJSON.key;
|
collection.key = collectionJSON.key;
|
||||||
await collection.loadPrimaryData();
|
await collection.loadPrimaryData();
|
||||||
}
|
}
|
||||||
|
this.newCollections.push(collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove external ids before saving
|
// Remove external ids before saving
|
||||||
|
|
@ -492,11 +562,19 @@ Zotero_Import_Mendeley.prototype._getDocumentsAPI = async function (groupID) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (await getAll(this._tokens, 'documents', params, headers)).map(d => ({
|
return (await getAll(this._tokens, 'documents', params, headers, {}, this._interruptChecker)).map(d => {
|
||||||
...d,
|
const processedDocument = { ...d, remoteUuid: d.id };
|
||||||
uuid: d.id,
|
|
||||||
remoteUuid: d.id
|
try {
|
||||||
}));
|
const clientData = JSON.parse(d.client_data);
|
||||||
|
processedDocument.uuid = clientData.desktop_id ? clientData.desktop_id : d.id;
|
||||||
|
}
|
||||||
|
catch (_) {
|
||||||
|
processedDocument.uuid = d.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return processedDocument;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -558,7 +636,12 @@ Zotero_Import_Mendeley.prototype._getDocumentCreatorsAPI = async function (docum
|
||||||
const authors = (doc.authors || []).map(c => this._makeCreator('author', c.first_name, c.last_name));
|
const authors = (doc.authors || []).map(c => this._makeCreator('author', c.first_name, c.last_name));
|
||||||
const editors = (doc.editors || []).map(c => this._makeCreator('editor', c.first_name, c.last_name));
|
const editors = (doc.editors || []).map(c => this._makeCreator('editor', c.first_name, c.last_name));
|
||||||
const translators = (doc.translators || []).map(c => this._makeCreator('translator', c.first_name, c.last_name));
|
const translators = (doc.translators || []).map(c => this._makeCreator('translator', c.first_name, c.last_name));
|
||||||
map.set(doc.id, [...authors, ...editors, ...translators]);
|
const creators = [...authors, ...editors, ...translators];
|
||||||
|
const validCreators = creators.filter(c => c.name || c.firstName || c.lastName);
|
||||||
|
if (creators.length !== validCreators.length) {
|
||||||
|
Zotero.debug(`Discarding ${creators.length - validCreators.length} invalid creators for document ${doc.id}`);
|
||||||
|
}
|
||||||
|
map.set(doc.id, validCreators);
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
};
|
};
|
||||||
|
|
@ -597,7 +680,10 @@ Zotero_Import_Mendeley.prototype._getDocumentTagsDB = async function (groupID) {
|
||||||
Zotero_Import_Mendeley.prototype._getDocumentTagsAPI = async function (documents) {
|
Zotero_Import_Mendeley.prototype._getDocumentTagsAPI = async function (documents) {
|
||||||
var map = new Map();
|
var map = new Map();
|
||||||
for (let doc of documents) {
|
for (let doc of documents) {
|
||||||
const tags = [...(doc.tags || []).map(tag => ({ tag, type: 0 })), ...(doc.keywords || []).map(tag => ({ tag, type: 1 }))];
|
const tags = [
|
||||||
|
...(doc.tags || []).map(tag => ({ tag, type: 0 })),
|
||||||
|
...(doc.keywords || []).map(tag => ({ tag, type: 1 }))
|
||||||
|
].filter(t => t.tag && t.tag.trim());
|
||||||
map.set(doc.id, tags);
|
map.set(doc.id, tags);
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
|
|
@ -693,12 +779,13 @@ Zotero_Import_Mendeley.prototype._fetchFile = async function (fileID, filePath)
|
||||||
Zotero_Import_Mendeley.prototype._getDocumentFilesAPI = async function (documents) {
|
Zotero_Import_Mendeley.prototype._getDocumentFilesAPI = async function (documents) {
|
||||||
const map = new Map();
|
const map = new Map();
|
||||||
|
|
||||||
let totalSize = 0;
|
this._totalSize = 0;
|
||||||
|
|
||||||
Components.utils.import("resource://zotero/concurrentCaller.js");
|
Components.utils.import("resource://zotero/concurrentCaller.js");
|
||||||
var caller = new ConcurrentCaller({
|
this._caller = new ConcurrentCaller({
|
||||||
numConcurrent: 6,
|
numConcurrent: 6,
|
||||||
onError: e => Zotero.logError(e),
|
onError: e => Zotero.logError(e),
|
||||||
|
logger: Zotero.debug,
|
||||||
Promise: Zotero.Promise
|
Promise: Zotero.Promise
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -712,7 +799,7 @@ Zotero_Import_Mendeley.prototype._getDocumentFilesAPI = async function (document
|
||||||
let tmpFile = OS.Path.join(Zotero.getTempDirectory().path, `m-api-${file.id}.${ext}`);
|
let tmpFile = OS.Path.join(Zotero.getTempDirectory().path, `m-api-${file.id}.${ext}`);
|
||||||
|
|
||||||
this._tmpFilesToDelete.push(tmpFile);
|
this._tmpFilesToDelete.push(tmpFile);
|
||||||
caller.add(this._fetchFile.bind(this, file.id, tmpFile));
|
this._caller.add(this._fetchFile.bind(this, file.id, tmpFile));
|
||||||
files.push({
|
files.push({
|
||||||
fileURL: OS.Path.toFileURI(tmpFile),
|
fileURL: OS.Path.toFileURI(tmpFile),
|
||||||
title: file.file_name || '',
|
title: file.file_name || '',
|
||||||
|
|
@ -720,13 +807,14 @@ Zotero_Import_Mendeley.prototype._getDocumentFilesAPI = async function (document
|
||||||
hash: file.filehash,
|
hash: file.filehash,
|
||||||
fileBaseName
|
fileBaseName
|
||||||
});
|
});
|
||||||
totalSize += file.size;
|
this._totalSize += file.size;
|
||||||
this._progressMax += 1;
|
this._progressMax += 1;
|
||||||
}
|
}
|
||||||
map.set(doc.id, files);
|
map.set(doc.id, files);
|
||||||
}
|
}
|
||||||
// TODO: check if enough space available totalSize
|
// TODO: check if enough space available totalSize
|
||||||
await caller.runAll();
|
await this._caller.runAll();
|
||||||
|
this._caller = null;
|
||||||
return map;
|
return map;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -823,7 +911,7 @@ Zotero_Import_Mendeley.prototype._getDocumentAnnotationsAPI = async function (gr
|
||||||
}
|
}
|
||||||
|
|
||||||
const map = new Map();
|
const map = new Map();
|
||||||
(await getAll(this._tokens, 'annotations', params, { Accept: 'application/vnd.mendeley-annotation.1+json' }))
|
(await getAll(this._tokens, 'annotations', params, { Accept: 'application/vnd.mendeley-annotation.1+json' }, {}, this._interruptChecker))
|
||||||
.forEach((a) => {
|
.forEach((a) => {
|
||||||
if (profileID !== null && a.profile_id !== profileID) {
|
if (profileID !== null && a.profile_id !== profileID) {
|
||||||
// optionally filter annotations by profile id
|
// optionally filter annotations by profile id
|
||||||
|
|
@ -885,7 +973,7 @@ Zotero_Import_Mendeley.prototype._getGroupsAPI = async function () {
|
||||||
const params = { type: 'all' };
|
const params = { type: 'all' };
|
||||||
const headers = { Accept: 'application/vnd.mendeley-group-list+json' };
|
const headers = { Accept: 'application/vnd.mendeley-group-list+json' };
|
||||||
|
|
||||||
return getAll(this._tokens, 'groups/v2', params, headers);
|
return getAll(this._tokens, 'groups/v2', params, headers, {}, this._interruptChecker);
|
||||||
};
|
};
|
||||||
|
|
||||||
Zotero_Import_Mendeley.prototype._getGroupsDB = async function () {
|
Zotero_Import_Mendeley.prototype._getGroupsDB = async function () {
|
||||||
|
|
@ -985,14 +1073,14 @@ Zotero_Import_Mendeley.prototype._documentToAPIJSON = async function (map, docum
|
||||||
|
|
||||||
for (let field in parent) {
|
for (let field in parent) {
|
||||||
switch (field) {
|
switch (field) {
|
||||||
case 'itemType':
|
case 'itemType':
|
||||||
case 'key':
|
case 'key':
|
||||||
case 'parentItem':
|
case 'parentItem':
|
||||||
case 'note':
|
case 'note':
|
||||||
case 'creators':
|
case 'creators':
|
||||||
case 'dateAdded':
|
case 'dateAdded':
|
||||||
case 'dateModified':
|
case 'dateModified':
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move unknown/invalid fields to Extra
|
// Move unknown/invalid fields to Extra
|
||||||
|
|
@ -1035,7 +1123,7 @@ Zotero_Import_Mendeley.prototype._documentToAPIJSON = async function (map, docum
|
||||||
// seriesEditor isn't valid on some item types (e.g., book)
|
// seriesEditor isn't valid on some item types (e.g., book)
|
||||||
if (creator.creatorType == 'seriesEditor'
|
if (creator.creatorType == 'seriesEditor'
|
||||||
&& !Zotero.CreatorTypes.isValidForItemType(
|
&& !Zotero.CreatorTypes.isValidForItemType(
|
||||||
Zotero.CreatorTypes.getID('seriesEditor'), itemTypeID)) {
|
Zotero.CreatorTypes.getID('seriesEditor'), itemTypeID)) {
|
||||||
creator.creatorType = 'editor';
|
creator.creatorType = 'editor';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1160,7 +1248,6 @@ Zotero_Import_Mendeley.prototype._processField = function (parent, children, zFi
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Zotero.warn(`Unknown function subfield: ${subfield}`);
|
Zotero.warn(`Unknown function subfield: ${subfield}`);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -1191,20 +1278,21 @@ Zotero_Import_Mendeley.prototype._makeCreator = function (creatorType, firstName
|
||||||
Zotero_Import_Mendeley.prototype._addExtraField = function (extra, field, val) {
|
Zotero_Import_Mendeley.prototype._addExtraField = function (extra, field, val) {
|
||||||
// Strip the field if it appears at the beginning of the value (to avoid "DOI: DOI: 10...")
|
// Strip the field if it appears at the beginning of the value (to avoid "DOI: DOI: 10...")
|
||||||
if (typeof val == 'string') {
|
if (typeof val == 'string') {
|
||||||
val = val.replace(new RegExp(`^${field}:\s*`, 'i'), "");
|
val = val.replace(new RegExp(`^${field}:\\s*`, 'i'), "");
|
||||||
}
|
}
|
||||||
extra = extra ? extra + '\n' : '';
|
extra = extra ? extra + '\n' : '';
|
||||||
if (field != 'arXiv') {
|
if (field != 'arXiv') {
|
||||||
field = field[0].toUpperCase() + field.substr(1);
|
field = field[0].toUpperCase() + field.substr(1);
|
||||||
field = field.replace(/([a-z])([A-Z][a-z])/, "$1 $2");
|
field = field.replace(/([a-z])([A-Z][a-z])/, "$1 $2");
|
||||||
}
|
}
|
||||||
|
|
||||||
return extra + `${field}: ${val}`;
|
return extra + `${field}: ${val}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
Zotero_Import_Mendeley.prototype._convertNote = function (note) {
|
Zotero_Import_Mendeley.prototype._convertNote = function (note) {
|
||||||
return note
|
return note
|
||||||
// Add newlines after <br>
|
// Add newlines after <br>
|
||||||
.replace(/<br\s*\/>/g, '<br\/>\n')
|
.replace(/<br\s*\/>/g, '<br/>\n')
|
||||||
//
|
//
|
||||||
// Legacy pre-HTML stuff
|
// Legacy pre-HTML stuff
|
||||||
//
|
//
|
||||||
|
|
@ -1228,16 +1316,19 @@ Zotero_Import_Mendeley.prototype._saveItems = async function (libraryID, json) {
|
||||||
var lastExistingParentItem;
|
var lastExistingParentItem;
|
||||||
for (let i = 0; i < json.length; i++) {
|
for (let i = 0; i < json.length; i++) {
|
||||||
let itemJSON = json[i];
|
let itemJSON = json[i];
|
||||||
|
let isMappedToExisting = false;
|
||||||
|
|
||||||
// Check if the item has been previously imported
|
// Check if the item has been previously imported
|
||||||
let item = await this._findExistingItem(libraryID, itemJSON, lastExistingParentItem);
|
let item = await this._findExistingItem(libraryID, itemJSON, lastExistingParentItem);
|
||||||
if (item) {
|
if (item) {
|
||||||
|
isMappedToExisting = true;
|
||||||
if (item.isRegularItem()) {
|
if (item.isRegularItem()) {
|
||||||
lastExistingParentItem = item;
|
lastExistingParentItem = item;
|
||||||
|
|
||||||
// Update any child items to point to the existing item's key instead of the
|
// Update any child items to point to the existing item's key instead of the
|
||||||
// new generated one
|
// new generated one
|
||||||
this._updateParentKeys('item', json, i + 1, itemJSON.key, item.key);
|
this._updateParentKeys('item', json, i + 1, itemJSON.key, item.key);
|
||||||
|
this.numRelinked++;
|
||||||
|
|
||||||
// Leave item in any collections it's in
|
// Leave item in any collections it's in
|
||||||
itemJSON.collections = item.getCollections()
|
itemJSON.collections = item.getCollections()
|
||||||
|
|
@ -1256,11 +1347,32 @@ Zotero_Import_Mendeley.prototype._saveItems = async function (libraryID, json) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(this.relinkOnly && !isMappedToExisting) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Remove external id before save
|
// Remove external id before save
|
||||||
let toSave = Object.assign({}, itemJSON);
|
let toSave = Object.assign({}, itemJSON);
|
||||||
delete toSave.documentID;
|
delete toSave.documentID;
|
||||||
|
|
||||||
item.fromJSON(toSave);
|
if ((this.newItemsOnly && !isMappedToExisting) || !this.newItemsOnly) {
|
||||||
|
if (isMappedToExisting) {
|
||||||
|
// dateAdded shouldn't change on an updated item. See #2881
|
||||||
|
delete toSave.dateAdded;
|
||||||
|
}
|
||||||
|
item.fromJSON(toSave);
|
||||||
|
this.newItems.push(item);
|
||||||
|
}
|
||||||
|
else if (isMappedToExisting && toSave.relations) {
|
||||||
|
const predicate = 'mendeleyDB:documentUUID';
|
||||||
|
const existingRels = item.getRelationsByPredicate(predicate);
|
||||||
|
const newRel = toSave.relations[predicate];
|
||||||
|
if (existingRels.length && newRel && existingRels[0] !== newRel) {
|
||||||
|
Zotero.debug(`Migrating relation ${predicate} for existing item ${item.key} from ${existingRels[0]} to ${newRel}`);
|
||||||
|
item.removeRelation(predicate, existingRels[0]);
|
||||||
|
item.addRelation(predicate, newRel);
|
||||||
|
}
|
||||||
|
}
|
||||||
await item.saveTx({
|
await item.saveTx({
|
||||||
skipDateModifiedUpdate: true,
|
skipDateModifiedUpdate: true,
|
||||||
...this._saveOptions
|
...this._saveOptions
|
||||||
|
|
@ -1360,7 +1472,7 @@ Zotero_Import_Mendeley.prototype._findExistingItem = async function (libraryID,
|
||||||
Zotero.debug(`Found existing item ${existingItem.libraryKey} for `
|
Zotero.debug(`Found existing item ${existingItem.libraryKey} for `
|
||||||
+ `${predicate} ${itemJSON.relations[predicate]}`);
|
+ `${predicate} ${itemJSON.relations[predicate]}`);
|
||||||
return existingItem;
|
return existingItem;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
Zotero_Import_Mendeley.prototype._getItemByRelation = async function (libraryID, predicate, object) {
|
Zotero_Import_Mendeley.prototype._getItemByRelation = async function (libraryID, predicate, object) {
|
||||||
|
|
@ -1443,7 +1555,7 @@ Zotero_Import_Mendeley.prototype._saveFilesAndAnnotations = async function (file
|
||||||
Zotero.logError(e);
|
Zotero.logError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
Zotero_Import_Mendeley.prototype._findExistingFile = function (parentItemID, file) {
|
Zotero_Import_Mendeley.prototype._findExistingFile = function (parentItemID, file) {
|
||||||
|
|
@ -1460,7 +1572,7 @@ Zotero_Import_Mendeley.prototype._findExistingFile = function (parentItemID, fil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
};
|
||||||
|
|
||||||
Zotero_Import_Mendeley.prototype._isDownloadedFile = function (path) {
|
Zotero_Import_Mendeley.prototype._isDownloadedFile = function (path) {
|
||||||
var parentDir = OS.Path.dirname(path);
|
var parentDir = OS.Path.dirname(path);
|
||||||
|
|
@ -1468,7 +1580,7 @@ Zotero_Import_Mendeley.prototype._isDownloadedFile = function (path) {
|
||||||
|| parentDir.endsWith(OS.Path.join('Local', 'Mendeley Ltd', 'Mendeley Desktop', 'Downloaded'))
|
|| parentDir.endsWith(OS.Path.join('Local', 'Mendeley Ltd', 'Mendeley Desktop', 'Downloaded'))
|
||||||
|| parentDir.endsWith(OS.Path.join('Local', 'Mendeley Ltd.', 'Mendeley Desktop', 'Downloaded'))
|
|| parentDir.endsWith(OS.Path.join('Local', 'Mendeley Ltd.', 'Mendeley Desktop', 'Downloaded'))
|
||||||
|| parentDir.endsWith(OS.Path.join('data', 'Mendeley Ltd.', 'Mendeley Desktop', 'Downloaded'));
|
|| parentDir.endsWith(OS.Path.join('data', 'Mendeley Ltd.', 'Mendeley Desktop', 'Downloaded'));
|
||||||
}
|
};
|
||||||
|
|
||||||
Zotero_Import_Mendeley.prototype._isTempDownloadedFile = function (path) {
|
Zotero_Import_Mendeley.prototype._isTempDownloadedFile = function (path) {
|
||||||
return path.startsWith(OS.Path.join(Zotero.getTempDirectory().path, 'm-api'));
|
return path.startsWith(OS.Path.join(Zotero.getTempDirectory().path, 'm-api'));
|
||||||
|
|
@ -1498,7 +1610,7 @@ Zotero_Import_Mendeley.prototype._getRealFilePath = async function (path) {
|
||||||
return altPath;
|
return altPath;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
};
|
||||||
|
|
||||||
Zotero_Import_Mendeley.prototype._saveAnnotations = async function (annotations, parentItemID, attachmentItemID, fileHash) {
|
Zotero_Import_Mendeley.prototype._saveAnnotations = async function (annotations, parentItemID, attachmentItemID, fileHash) {
|
||||||
if (!annotations.length) return;
|
if (!annotations.length) return;
|
||||||
|
|
@ -1507,7 +1619,6 @@ Zotero_Import_Mendeley.prototype._saveAnnotations = async function (annotations,
|
||||||
var libraryID = parentItem.libraryID;
|
var libraryID = parentItem.libraryID;
|
||||||
if (attachmentItemID) {
|
if (attachmentItemID) {
|
||||||
var attachmentItem = Zotero.Items.get(attachmentItemID);
|
var attachmentItem = Zotero.Items.get(attachmentItemID);
|
||||||
var attachmentURIPath = Zotero.API.getLibraryPrefix(libraryID) + '/items/' + attachmentItem.key;
|
|
||||||
|
|
||||||
if (attachmentItem) {
|
if (attachmentItem) {
|
||||||
let file = await attachmentItem.getFilePathAsync();
|
let file = await attachmentItem.getFilePathAsync();
|
||||||
|
|
@ -1530,6 +1641,10 @@ Zotero_Import_Mendeley.prototype._saveAnnotations = async function (annotations,
|
||||||
}
|
}
|
||||||
// PDFWorker needs 'id'
|
// PDFWorker needs 'id'
|
||||||
annotations.forEach(annotation => annotation.id = annotation.uuid);
|
annotations.forEach(annotation => annotation.id = annotation.uuid);
|
||||||
|
annotations.forEach(annotation => (colorMap.has(annotation.color)
|
||||||
|
? annotation.color = colorMap.get(annotation.color)
|
||||||
|
: annotation.color
|
||||||
|
));
|
||||||
// Returns 'id', 'position', 'type', 'pageLabel', 'sortIndex', 'text' (for highlight)
|
// Returns 'id', 'position', 'type', 'pageLabel', 'sortIndex', 'text' (for highlight)
|
||||||
Zotero.debug("Processing annotations in " + file);
|
Zotero.debug("Processing annotations in " + file);
|
||||||
annotations = await Zotero.PDFWorker.processMendeleyAnnotations(file, annotations);
|
annotations = await Zotero.PDFWorker.processMendeleyAnnotations(file, annotations);
|
||||||
|
|
@ -1616,7 +1731,7 @@ Zotero_Import_Mendeley.prototype._saveAnnotations = async function (annotations,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
note.setNote('<h1>' + Zotero.getString('extractedAnnotations') + '</h1>\n' + noteStrings.join('\n'));
|
note.setNote('<h1>' + Zotero.getString('extractedAnnotations') + '</h1>\n' + noteStrings.join('\n'));
|
||||||
return note.saveTx(this._saveOptions);
|
await note.saveTx(this._saveOptions);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1633,17 +1748,7 @@ Zotero_Import_Mendeley.prototype._updateParentKeys = function (objectType, json,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
Zotero_Import_Mendeley.prototype._updateItemCollectionKeys = function (json, oldKey, newKey) {
|
|
||||||
for (; i < json.length; i++) {
|
|
||||||
let x = json[i];
|
|
||||||
if (x[prop] == oldKey) {
|
|
||||||
x[prop] = newKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Clean up extra files created <5.0.51
|
// Clean up extra files created <5.0.51
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ var mendeleyOnlineMappings = {
|
||||||
arxiv: 'arxivId',
|
arxiv: 'arxivId',
|
||||||
accessed: 'dateAccessed',
|
accessed: 'dateAccessed',
|
||||||
authors: false, // all author types handled separately
|
authors: false, // all author types handled separately
|
||||||
|
client_data: false, // desktop_id extraction handled separately
|
||||||
citation_key: 'citationKey',
|
citation_key: 'citationKey',
|
||||||
created: 'added',
|
created: 'added',
|
||||||
edition: 'edition',
|
edition: 'edition',
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
var map = {
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
var mendeleyDBMaps = {
|
||||||
83: {
|
83: {
|
||||||
itemTypes: {
|
itemTypes: {
|
||||||
Bill: "bill",
|
Bill: "bill",
|
||||||
|
|
|
||||||
|
|
@ -113,9 +113,10 @@ var Zotero_Citation_Dialog = new function () {
|
||||||
var menu = document.getElementById("label");
|
var menu = document.getElementById("label");
|
||||||
var label_list = document.getElementById("locator-type-popup");
|
var label_list = document.getElementById("locator-type-popup");
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
var pageLocatorIndex;
|
||||||
for(var value in locators) {
|
for(var value in locators) {
|
||||||
var locator = locators[value];
|
var locator = locators[value];
|
||||||
var locatorLabel = Zotero.getString('citation.locator.'+locator.replace(/\s/g,''));
|
let locatorLabel = Zotero.Cite.getLocatorString(locator);
|
||||||
// add to list of labels
|
// add to list of labels
|
||||||
var child = document.createElement("menuitem");
|
var child = document.createElement("menuitem");
|
||||||
child.setAttribute("value", value);
|
child.setAttribute("value", value);
|
||||||
|
|
@ -124,9 +125,13 @@ var Zotero_Citation_Dialog = new function () {
|
||||||
// add to array
|
// add to array
|
||||||
_locatorIndexArray[locator] = i;
|
_locatorIndexArray[locator] = i;
|
||||||
_locatorNameArray[i] = locator;
|
_locatorNameArray[i] = locator;
|
||||||
|
if (locator == 'page') {
|
||||||
|
pageLocatorIndex = i;
|
||||||
|
_preserveData.label[1] = i;
|
||||||
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
menu.selectedIndex = 0;
|
menu.selectedIndex = pageLocatorIndex;
|
||||||
|
|
||||||
// load (from selectItemsDialog.js)
|
// load (from selectItemsDialog.js)
|
||||||
yield doLoad();
|
yield doLoad();
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ var Zotero_QuickFormat = new function () {
|
||||||
const pixelRe = /^([0-9]+)px$/
|
const pixelRe = /^([0-9]+)px$/
|
||||||
const specifiedLocatorRe = /^(?:,? *(p{1,2})(?:\. *| *)|:)([0-9\-]+) *$/;
|
const specifiedLocatorRe = /^(?:,? *(p{1,2})(?:\. *| *)|:)([0-9\-]+) *$/;
|
||||||
const yearRe = /,? *([0-9]+) *(B[. ]*C[. ]*(?:E[. ]*)?|A[. ]*D[. ]*|C[. ]*E[. ]*)?$/i;
|
const yearRe = /,? *([0-9]+) *(B[. ]*C[. ]*(?:E[. ]*)?|A[. ]*D[. ]*|C[. ]*E[. ]*)?$/i;
|
||||||
const locatorRe = /(?:,? *(p{0,2})\.?|(\:)) *([0-9\-–]+)$/i;
|
const locatorRe = / (?:,? *(p{0,2})\.?|(\:)) *([0-9\-–]+)$/i;
|
||||||
const creatorSplitRe = /(?:,| *(?:and|\&)) +/;
|
const creatorSplitRe = /(?:,| *(?:and|\&)) +/;
|
||||||
const charRe = /[\w\u007F-\uFFFF]/;
|
const charRe = /[\w\u007F-\uFFFF]/;
|
||||||
const numRe = /^[0-9\-–]+$/;
|
const numRe = /^[0-9\-–]+$/;
|
||||||
|
|
@ -37,7 +37,8 @@ var Zotero_QuickFormat = new function () {
|
||||||
keepSorted, showEditor, referencePanel, referenceBox, referenceHeight = 0,
|
keepSorted, showEditor, referencePanel, referenceBox, referenceHeight = 0,
|
||||||
separatorHeight = 0, currentLocator, currentLocatorLabel, currentSearchTime, dragging,
|
separatorHeight = 0, currentLocator, currentLocatorLabel, currentSearchTime, dragging,
|
||||||
panel, panelPrefix, panelSuffix, panelSuppressAuthor, panelLocatorLabel, panelLocator,
|
panel, panelPrefix, panelSuffix, panelSuppressAuthor, panelLocatorLabel, panelLocator,
|
||||||
panelLibraryLink, panelInfo, panelRefersToBubble, panelFrameHeight = 0, accepted = false;
|
panelLibraryLink, panelInfo, panelRefersToBubble, panelFrameHeight = 0, accepted = false,
|
||||||
|
isPaste = false;
|
||||||
var locatorLocked = true;
|
var locatorLocked = true;
|
||||||
var locatorNode = null;
|
var locatorNode = null;
|
||||||
var _searchPromise;
|
var _searchPromise;
|
||||||
|
|
@ -116,10 +117,9 @@ var Zotero_QuickFormat = new function () {
|
||||||
|
|
||||||
// add labels to popup
|
// add labels to popup
|
||||||
var locators = Zotero.Cite.labels;
|
var locators = Zotero.Cite.labels;
|
||||||
var menu = document.getElementById("locator-label");
|
|
||||||
var labelList = document.getElementById("locator-label-popup");
|
var labelList = document.getElementById("locator-label-popup");
|
||||||
for(var locator of locators) {
|
for(var locator of locators) {
|
||||||
var locatorLabel = Zotero.getString('citation.locator.'+locator.replace(/\s/g,''));
|
let locatorLabel = Zotero.Cite.getLocatorString(locator);
|
||||||
|
|
||||||
// add to list of labels
|
// add to list of labels
|
||||||
var child = document.createElement("menuitem");
|
var child = document.createElement("menuitem");
|
||||||
|
|
@ -127,7 +127,6 @@ var Zotero_QuickFormat = new function () {
|
||||||
child.setAttribute("label", locatorLabel);
|
child.setAttribute("label", locatorLabel);
|
||||||
labelList.appendChild(child);
|
labelList.appendChild(child);
|
||||||
}
|
}
|
||||||
menu.selectedIndex = 0;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,10 +216,10 @@ var Zotero_QuickFormat = new function () {
|
||||||
|
|
||||||
// Range could be referenced to the body element
|
// Range could be referenced to the body element
|
||||||
if(node === qfe) {
|
if(node === qfe) {
|
||||||
var offset = range.startOffset;
|
for (let i = qfe.childNodes.length - 1; i >= 0; i--) {
|
||||||
if(offset !== range.endOffset) return false;
|
node = qfe.childNodes[i];
|
||||||
node = qfe.childNodes[Math.min(qfe.childNodes.length-1, offset)];
|
if(node.nodeType === Node.TEXT_NODE) return node;
|
||||||
if(node.nodeType === Node.TEXT_NODE) return node;
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -240,7 +239,7 @@ var Zotero_QuickFormat = new function () {
|
||||||
* @return {String} str without locator
|
* @return {String} str without locator
|
||||||
*/
|
*/
|
||||||
function _updateLocator(str) {
|
function _updateLocator(str) {
|
||||||
m = locatorRe.exec(str);
|
m = !isPaste && locatorRe.exec(str);
|
||||||
if(m && (m[1] || m[2] || m[3].length !== 4) && m.index > 0) {
|
if(m && (m[1] || m[2] || m[3].length !== 4) && m.index > 0) {
|
||||||
currentLocator = m[3];
|
currentLocator = m[3];
|
||||||
str = str.substr(0, m.index)+str.substring(m.index+m[0].length);
|
str = str.substr(0, m.index)+str.substring(m.index+m[0].length);
|
||||||
|
|
@ -500,6 +499,7 @@ var Zotero_QuickFormat = new function () {
|
||||||
await Zotero.Items.loadDataTypes(items);
|
await Zotero.Items.loadDataTypes(items);
|
||||||
|
|
||||||
searchString = searchString.toLowerCase();
|
searchString = searchString.toLowerCase();
|
||||||
|
let searchParts = Zotero.SearchConditions.parseSearchString(searchString);
|
||||||
var collation = Zotero.getLocaleCollation();
|
var collation = Zotero.getLocaleCollation();
|
||||||
|
|
||||||
function _itemSort(a, b) {
|
function _itemSort(a, b) {
|
||||||
|
|
@ -508,13 +508,15 @@ var Zotero_QuickFormat = new function () {
|
||||||
// Favor left-bound name matches (e.g., "Baum" < "Appelbaum"),
|
// Favor left-bound name matches (e.g., "Baum" < "Appelbaum"),
|
||||||
// using last name of first author
|
// using last name of first author
|
||||||
if (firstCreatorA && firstCreatorB) {
|
if (firstCreatorA && firstCreatorB) {
|
||||||
let caStartsWith = firstCreatorA.toLowerCase().indexOf(searchString) == 0;
|
for (let part of searchParts) {
|
||||||
let cbStartsWith = firstCreatorB.toLowerCase().indexOf(searchString) == 0;
|
let caStartsWith = firstCreatorA.toLowerCase().startsWith(part.text);
|
||||||
if (caStartsWith && !cbStartsWith) {
|
let cbStartsWith = firstCreatorB.toLowerCase().startsWith(part.text);
|
||||||
return -1;
|
if (caStartsWith && !cbStartsWith) {
|
||||||
}
|
return -1;
|
||||||
else if (!caStartsWith && cbStartsWith) {
|
}
|
||||||
return 1;
|
else if (!caStartsWith && cbStartsWith) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -729,9 +731,7 @@ var Zotero_QuickFormat = new function () {
|
||||||
|
|
||||||
// Title, if no creator (getDisplayTitle in order to get case, e-mail, statute which don't have a title field)
|
// Title, if no creator (getDisplayTitle in order to get case, e-mail, statute which don't have a title field)
|
||||||
title = item.getDisplayTitle();
|
title = item.getDisplayTitle();
|
||||||
if (item.isNote()) {
|
title = title.substr(0, 32) + (title.length > 32 ? "…" : "");
|
||||||
title = title.substr(0, 24) + '…';
|
|
||||||
}
|
|
||||||
if (!str) {
|
if (!str) {
|
||||||
str = Zotero.getString("punctuation.openingQMark") + title + Zotero.getString("punctuation.closingQMark");
|
str = Zotero.getString("punctuation.openingQMark") + title + Zotero.getString("punctuation.closingQMark");
|
||||||
}
|
}
|
||||||
|
|
@ -758,15 +758,17 @@ var Zotero_QuickFormat = new function () {
|
||||||
|
|
||||||
// Prefix
|
// Prefix
|
||||||
if(citationItem.prefix && Zotero.CiteProc.CSL.ENDSWITH_ROMANESQUE_REGEXP) {
|
if(citationItem.prefix && Zotero.CiteProc.CSL.ENDSWITH_ROMANESQUE_REGEXP) {
|
||||||
str = citationItem.prefix
|
let prefix = citationItem.prefix.substr(0, 10) + (citationItem.prefix.length > 10 ? "…" : "")
|
||||||
|
str = prefix
|
||||||
+(Zotero.CiteProc.CSL.ENDSWITH_ROMANESQUE_REGEXP.test(citationItem.prefix) ? " " : "")
|
+(Zotero.CiteProc.CSL.ENDSWITH_ROMANESQUE_REGEXP.test(citationItem.prefix) ? " " : "")
|
||||||
+str;
|
+str;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Suffix
|
// Suffix
|
||||||
if(citationItem.suffix && Zotero.CiteProc.CSL.STARTSWITH_ROMANESQUE_REGEXP) {
|
if(citationItem.suffix && Zotero.CiteProc.CSL.STARTSWITH_ROMANESQUE_REGEXP) {
|
||||||
|
let suffix = citationItem.suffix.substr(0, 10) + (citationItem.suffix.length > 10 ? "…" : "")
|
||||||
str += (Zotero.CiteProc.CSL.STARTSWITH_ROMANESQUE_REGEXP.test(citationItem.suffix) ? " " : "")
|
str += (Zotero.CiteProc.CSL.STARTSWITH_ROMANESQUE_REGEXP.test(citationItem.suffix) ? " " : "")
|
||||||
+citationItem.suffix;
|
+ suffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
|
|
@ -778,9 +780,6 @@ var Zotero_QuickFormat = new function () {
|
||||||
function _insertBubble(citationItem, nextNode) {
|
function _insertBubble(citationItem, nextNode) {
|
||||||
var str = _buildBubbleString(citationItem);
|
var str = _buildBubbleString(citationItem);
|
||||||
|
|
||||||
// It's entirely unintuitive why, but after trying a bunch of things, it looks like using
|
|
||||||
// a XUL label for these things works best. A regular span causes issues with moving the
|
|
||||||
// cursor.
|
|
||||||
var bubble = qfiDocument.createElement("span");
|
var bubble = qfiDocument.createElement("span");
|
||||||
bubble.setAttribute("class", "citation-dialog bubble");
|
bubble.setAttribute("class", "citation-dialog bubble");
|
||||||
bubble.setAttribute("draggable", "true");
|
bubble.setAttribute("draggable", "true");
|
||||||
|
|
@ -871,6 +870,7 @@ var Zotero_QuickFormat = new function () {
|
||||||
// handling to maintain the correct locator node in
|
// handling to maintain the correct locator node in
|
||||||
// _showCitation()
|
// _showCitation()
|
||||||
var bubble = locatorNode = _insertBubble(citationItem, node);
|
var bubble = locatorNode = _insertBubble(citationItem, node);
|
||||||
|
isPaste = false;
|
||||||
_clearEntryList();
|
_clearEntryList();
|
||||||
yield _previewAndSort();
|
yield _previewAndSort();
|
||||||
_refocusQfe();
|
_refocusQfe();
|
||||||
|
|
@ -1085,15 +1085,16 @@ var Zotero_QuickFormat = new function () {
|
||||||
let citationItem = JSON.parse(target.dataset.citationItem);
|
let citationItem = JSON.parse(target.dataset.citationItem);
|
||||||
panelPrefix.value = citationItem["prefix"] ? citationItem["prefix"] : "";
|
panelPrefix.value = citationItem["prefix"] ? citationItem["prefix"] : "";
|
||||||
panelSuffix.value = citationItem["suffix"] ? citationItem["suffix"] : "";
|
panelSuffix.value = citationItem["suffix"] ? citationItem["suffix"] : "";
|
||||||
|
var pageOption = panelLocatorLabel.getElementsByAttribute("value", "page")[0];
|
||||||
if(citationItem["label"]) {
|
if(citationItem["label"]) {
|
||||||
var option = panelLocatorLabel.getElementsByAttribute("value", citationItem["label"]);
|
var option = panelLocatorLabel.getElementsByAttribute("value", citationItem["label"]);
|
||||||
if(option.length) {
|
if(option.length) {
|
||||||
panelLocatorLabel.selectedItem = option[0];
|
panelLocatorLabel.selectedItem = option[0];
|
||||||
} else {
|
} else {
|
||||||
panelLocatorLabel.selectedIndex = 0;
|
panelLocatorLabel.selectedItem = pageOption;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
panelLocatorLabel.selectedIndex = 0;
|
panelLocatorLabel.selectedItem = pageOption;
|
||||||
}
|
}
|
||||||
panelLocator.value = citationItem["locator"] ? citationItem["locator"] : "";
|
panelLocator.value = citationItem["locator"] ? citationItem["locator"] : "";
|
||||||
panelSuppressAuthor.checked = !!citationItem["suppress-author"];
|
panelSuppressAuthor.checked = !!citationItem["suppress-author"];
|
||||||
|
|
@ -1356,6 +1357,7 @@ var Zotero_QuickFormat = new function () {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
isPaste = false;
|
||||||
_resetSearchTimer();
|
_resetSearchTimer();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ const { noop, getDragTargetOrient } = require("components/utils");
|
||||||
const PropTypes = require("prop-types");
|
const PropTypes = require("prop-types");
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const ReactDOM = require('react-dom');
|
const ReactDOM = require('react-dom');
|
||||||
const { IntlProvider } = require('react-intl');
|
|
||||||
const LibraryTree = require('./libraryTree');
|
const LibraryTree = require('./libraryTree');
|
||||||
const VirtualizedTable = require('components/virtualized-table');
|
const VirtualizedTable = require('components/virtualized-table');
|
||||||
const { renderCell, formatColumnName } = VirtualizedTable;
|
const { renderCell, formatColumnName } = VirtualizedTable;
|
||||||
|
|
@ -38,9 +37,8 @@ const { Cc, Ci, Cu } = require('chrome');
|
||||||
Cu.import("resource://gre/modules/osfile.jsm");
|
Cu.import("resource://gre/modules/osfile.jsm");
|
||||||
|
|
||||||
const CHILD_INDENT = 12;
|
const CHILD_INDENT = 12;
|
||||||
const COLORED_TAGS_RE = new RegExp("^[0-" + Zotero.Tags.MAX_COLORED_TAGS + "]{1}$");
|
const COLORED_TAGS_RE = new RegExp("^(?:Numpad|Digit)([0-" + Zotero.Tags.MAX_COLORED_TAGS + "]{1})$");
|
||||||
const COLUMN_PREFS_FILEPATH = OS.Path.join(Zotero.Profile.dir, "treePrefs.json");
|
const COLUMN_PREFS_FILEPATH = OS.Path.join(Zotero.Profile.dir, "treePrefs.json");
|
||||||
const EMOJI_RE = /\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/gu;
|
|
||||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||||
const ATTACHMENT_STATE_LOAD_DELAY = 150; //ms
|
const ATTACHMENT_STATE_LOAD_DELAY = 150; //ms
|
||||||
|
|
||||||
|
|
@ -50,9 +48,7 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
var ref;
|
var ref;
|
||||||
opts.domEl = domEl;
|
opts.domEl = domEl;
|
||||||
let elem = (
|
let elem = (
|
||||||
<IntlProvider locale={Zotero.locale} messages={Zotero.Intl.strings}>
|
<ItemTree ref={c => ref = c } {...opts} />
|
||||||
<ItemTree ref={c => ref = c } {...opts} />
|
|
||||||
</IntlProvider>
|
|
||||||
);
|
);
|
||||||
await new Promise(resolve => ReactDOM.render(elem, domEl, resolve));
|
await new Promise(resolve => ReactDOM.render(elem, domEl, resolve));
|
||||||
|
|
||||||
|
|
@ -448,11 +444,20 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
let row = this.getRowIndexByID(id);
|
let row = this.getRowIndexByID(id);
|
||||||
if (row === false) continue;
|
if (row === false) continue;
|
||||||
let item = Zotero.Items.get(id);
|
let item = Zotero.Items.get(id);
|
||||||
if (!item.deleted && !item.numChildren()) {
|
// Remove parent row if it isn't deleted and doesn't have any deleted children
|
||||||
|
// (shown by the numChildren including deleted being the same as numChildren not including deleted)
|
||||||
|
if (!item.deleted && (!item.isRegularItem() || item.numChildren(true) == item.numChildren(false))) {
|
||||||
rows.push(row);
|
rows.push(row);
|
||||||
|
// And all its children in the tree
|
||||||
|
for (let child = row + 1; child < this.rowCount && this.getLevel(child) > this.getLevel(row); child++) {
|
||||||
|
rows.push(child);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._removeRows(rows);
|
if (rows.length) {
|
||||||
|
this._removeRows(rows);
|
||||||
|
this.tree.invalidate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
@ -475,14 +480,17 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var visibleSubcollections = Zotero.Prefs.get('recursiveCollections')
|
||||||
|
? collectionTreeRow.ref.getDescendents(false, 'collection')
|
||||||
|
: [];
|
||||||
var splitIDs = [];
|
var splitIDs = [];
|
||||||
for (let id of ids) {
|
for (let id of ids) {
|
||||||
var split = id.split('-');
|
var split = id.split('-');
|
||||||
// Skip if not an item in this collection
|
// Include if an item in this collection or a visible subcollection
|
||||||
if (split[0] != collectionTreeRow.ref.id) {
|
if (split[0] == collectionTreeRow.ref.id
|
||||||
continue;
|
|| visibleSubcollections.some(c => split[0] == c.id)) {
|
||||||
|
splitIDs.push(split[1]);
|
||||||
}
|
}
|
||||||
splitIDs.push(split[1]);
|
|
||||||
}
|
}
|
||||||
ids = splitIDs;
|
ids = splitIDs;
|
||||||
}
|
}
|
||||||
|
|
@ -859,9 +867,9 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!event.shiftKey && !event.ctrlKey && !event.metaKey && !event.altKey && COLORED_TAGS_RE.test(event.key)) {
|
if (!event.shiftKey && !event.ctrlKey && !event.metaKey && !event.altKey && COLORED_TAGS_RE.test(event.code)) {
|
||||||
let libraryID = this.collectionTreeRow.ref.libraryID;
|
let libraryID = this.collectionTreeRow.ref.libraryID;
|
||||||
let position = parseInt(event.key) - 1;
|
let position = COLORED_TAGS_RE.exec(event.code)[1] - 1;
|
||||||
// When 0 is pressed, remove all colored tags
|
// When 0 is pressed, remove all colored tags
|
||||||
if (position == -1) {
|
if (position == -1) {
|
||||||
let items = this.getSelectedItems();
|
let items = this.getSelectedItems();
|
||||||
|
|
@ -978,7 +986,7 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
|
|
||||||
onItemContextMenu: (...args) => this.props.onContextMenu(...args),
|
onItemContextMenu: (...args) => this.props.onContextMenu(...args),
|
||||||
|
|
||||||
role: 'treegrid',
|
role: 'tree',
|
||||||
label: Zotero.getString('pane.items.title'),
|
label: Zotero.getString('pane.items.title'),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -1241,20 +1249,14 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
? `for ${itemIDs.length} ` + Zotero.Utilities.pluralize(itemIDs.length, ['item', 'items'])
|
? `for ${itemIDs.length} ` + Zotero.Utilities.pluralize(itemIDs.length, ['item', 'items'])
|
||||||
: ""));
|
: ""));
|
||||||
|
|
||||||
// Set whether rows with empty values should be displayed last,
|
// Set whether rows with empty values should sort at the beginning
|
||||||
// which may be different for primary and secondary sorting.
|
var emptyFirst = {
|
||||||
var emptyFirst = {};
|
title: true,
|
||||||
switch (primaryField) {
|
|
||||||
case 'title':
|
|
||||||
emptyFirst.title = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// When sorting by title we want empty titles at the top, but if not
|
// Date columns start descending, so put empty rows at end
|
||||||
// sorting by title, empty titles should sort to the bottom so that new
|
date: true,
|
||||||
// empty items don't get sorted to the middle of the items list.
|
year: true,
|
||||||
default:
|
};
|
||||||
emptyFirst.title = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache primary values while sorting, since base-field-mapped getField()
|
// Cache primary values while sorting, since base-field-mapped getField()
|
||||||
// calls are relatively expensive
|
// calls are relatively expensive
|
||||||
|
|
@ -1262,7 +1264,7 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
sortFields.forEach(x => cache[x] = {});
|
sortFields.forEach(x => cache[x] = {});
|
||||||
|
|
||||||
// Get the display field for a row (which might be a placeholder title)
|
// Get the display field for a row (which might be a placeholder title)
|
||||||
function getField(field, row) {
|
let getField = (field, row) => {
|
||||||
var item = row.ref;
|
var item = row.ref;
|
||||||
|
|
||||||
switch (field) {
|
switch (field) {
|
||||||
|
|
@ -1270,23 +1272,12 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
return Zotero.Items.getSortTitle(item.getDisplayTitle());
|
return Zotero.Items.getSortTitle(item.getDisplayTitle());
|
||||||
|
|
||||||
case 'hasAttachment':
|
case 'hasAttachment':
|
||||||
if (item.isFileAttachment()) {
|
if (this._canGetBestAttachmentState(item)) {
|
||||||
var state = item.fileExistsCached() ? 1 : -1;
|
return item.getBestAttachmentStateCached();
|
||||||
}
|
|
||||||
else if (item.isRegularItem()) {
|
|
||||||
var state = item.getBestAttachmentStateCached();
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// Make sort order present, missing, empty when ascending
|
|
||||||
if (state === 1) {
|
|
||||||
state = 2;
|
|
||||||
}
|
|
||||||
else if (state === -1) {
|
|
||||||
state = 1;
|
|
||||||
}
|
|
||||||
return state;
|
|
||||||
|
|
||||||
case 'numNotes':
|
case 'numNotes':
|
||||||
return row.numNotes(false, true) || 0;
|
return row.numNotes(false, true) || 0;
|
||||||
|
|
@ -1350,7 +1341,11 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sortField == 'hasAttachment') {
|
if (sortField == 'hasAttachment') {
|
||||||
return fieldB - fieldA;
|
// PDFs at the top
|
||||||
|
const order = ['pdf', 'snapshot', 'other', 'none'];
|
||||||
|
fieldA = order.indexOf(fieldA.type || 'none') + (fieldA.exists ? 0 : 4);
|
||||||
|
fieldB = order.indexOf(fieldB.type || 'none') + (fieldB.exists ? 0 : 4);
|
||||||
|
return fieldA - fieldB;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sortField == 'callNumber') {
|
if (sortField == 'callNumber') {
|
||||||
|
|
@ -1439,18 +1434,27 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
var openItemIDs = this._saveOpenState(true);
|
var openItemIDs = this._saveOpenState(true);
|
||||||
|
|
||||||
// Sort specific items
|
// Sort specific items
|
||||||
if (itemIDs) {
|
try {
|
||||||
let idsToSort = new Set(itemIDs);
|
if (itemIDs) {
|
||||||
this._rows.sort((a, b) => {
|
let idsToSort = new Set(itemIDs);
|
||||||
// Don't re-sort existing items. This assumes a stable sort(), which is the case in Firefox
|
this._rows.sort((a, b) => {
|
||||||
// but not Chrome/v8.
|
// Don't re-sort existing items. This assumes a stable sort(), which is the case in Firefox
|
||||||
if (!idsToSort.has(a.ref.id) && !idsToSort.has(b.ref.id)) return 0;
|
// but not Chrome/v8.
|
||||||
return rowSort(a, b) * order;
|
if (!idsToSort.has(a.ref.id) && !idsToSort.has(b.ref.id)) return 0;
|
||||||
});
|
return rowSort(a, b) * order;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Full sort
|
||||||
|
else {
|
||||||
|
this._rows.sort((a, b) => rowSort(a, b) * order);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Full sort
|
catch (e) {
|
||||||
else {
|
Zotero.logError("Error sorting fields: " + e.message);
|
||||||
this._rows.sort((a, b) => rowSort(a, b) * order);
|
Zotero.debug(e, 1);
|
||||||
|
// Clear anything that might be contributing to the error
|
||||||
|
Zotero.Prefs.clear('secondarySort.' + this.getSortField());
|
||||||
|
Zotero.Prefs.clear('fallbackSort');
|
||||||
}
|
}
|
||||||
|
|
||||||
this._refreshRowMap();
|
this._refreshRowMap();
|
||||||
|
|
@ -1748,8 +1752,21 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
await Zotero.Items.trashTx(ids);
|
await Zotero.Items.trashTx(ids);
|
||||||
}
|
}
|
||||||
else if (collectionTreeRow.isCollection()) {
|
else if (collectionTreeRow.isCollection()) {
|
||||||
|
let collectionIDs = [collectionTreeRow.ref.id];
|
||||||
|
if (Zotero.Prefs.get('recursiveCollections')) {
|
||||||
|
collectionIDs.push(...collectionTreeRow.ref.getDescendents(false, 'collection').map(c => c.id));
|
||||||
|
}
|
||||||
|
|
||||||
await Zotero.DB.executeTransaction(async () => {
|
await Zotero.DB.executeTransaction(async () => {
|
||||||
await collectionTreeRow.ref.removeItems(ids);
|
for (let itemID of ids) {
|
||||||
|
let item = Zotero.Items.get(itemID);
|
||||||
|
for (let collectionID of collectionIDs) {
|
||||||
|
item.removeFromCollection(collectionID);
|
||||||
|
}
|
||||||
|
await item.save({
|
||||||
|
skipDateModifiedUpdate: true
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (collectionTreeRow.isPublications()) {
|
else if (collectionTreeRow.isPublications()) {
|
||||||
|
|
@ -1788,6 +1805,11 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
return this._rows.map(row => asIDs ? row.ref.id : row.ref);
|
return this._rows.map(row => asIDs ? row.ref.id : row.ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current sort order of the items list
|
||||||
|
*
|
||||||
|
* @return {Number} - -1 for descending, 1 for ascending
|
||||||
|
*/
|
||||||
getSortDirection(sortFields) {
|
getSortDirection(sortFields) {
|
||||||
sortFields = sortFields || this.getSortFields();
|
sortFields = sortFields || this.getSortFields();
|
||||||
if (this.collectionTreeRow.isFeed()) {
|
if (this.collectionTreeRow.isFeed()) {
|
||||||
|
|
@ -1987,8 +2009,8 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
// If exporting with virtual "Markdown + Rich Text" translator, call Note Markdown
|
// If exporting with virtual "Markdown + Rich Text" translator, call Note Markdown
|
||||||
// and Note HTML translators instead
|
// and Note HTML translators instead
|
||||||
if (format.id === Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) {
|
if (format.id === Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) {
|
||||||
let markdownFormat = { mode: 'export', id: Zotero.Translators.TRANSLATOR_ID_NOTE_MARKDOWN };
|
let markdownFormat = { mode: 'export', id: Zotero.Translators.TRANSLATOR_ID_NOTE_MARKDOWN, options: format.markdownOptions };
|
||||||
let htmlFormat = { mode: 'export', id: Zotero.Translators.TRANSLATOR_ID_NOTE_HTML };
|
let htmlFormat = { mode: 'export', id: Zotero.Translators.TRANSLATOR_ID_NOTE_HTML, options: format.htmlOptions };
|
||||||
Zotero.QuickCopy.getContentFromItems(items, markdownFormat, (obj, worked) => {
|
Zotero.QuickCopy.getContentFromItems(items, markdownFormat, (obj, worked) => {
|
||||||
if (!worked) {
|
if (!worked) {
|
||||||
Zotero.log(Zotero.getString('fileInterface.exportError'), 'warning');
|
Zotero.log(Zotero.getString('fileInterface.exportError'), 'warning');
|
||||||
|
|
@ -2752,93 +2774,60 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
|
|
||||||
// TEMP: For now, we use the blue bullet for all non-PDF attachments, but there's
|
// TEMP: For now, we use the blue bullet for all non-PDF attachments, but there's
|
||||||
// commented-out code for showing different icons for snapshots, files, and URL/DOI links
|
// commented-out code for showing different icons for snapshots, files, and URL/DOI links
|
||||||
if (this.isContainer(index)) {
|
if (this._canGetBestAttachmentState(item)) {
|
||||||
if (item.isRegularItem()) {
|
const { type, exists } = item.getBestAttachmentStateCached();
|
||||||
const { type, exists } = item.getBestAttachmentStateCached();
|
|
||||||
let icon = "";
|
|
||||||
let ariaLabel;
|
|
||||||
// If the item has a child attachment
|
|
||||||
if (type !== null && type != 'none') {
|
|
||||||
if (type == 'pdf') {
|
|
||||||
icon = getDOMElement('IconTreeitemAttachmentPDF');
|
|
||||||
ariaLabel = Zotero.getString('pane.item.attachments.hasPDF');
|
|
||||||
if (!exists) {
|
|
||||||
icon.classList.add('icon-missing-file');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (type == 'snapshot') {
|
|
||||||
//icon = getDOMElement('IconTreeitemAttachmentSnapshot');
|
|
||||||
icon = exists ? getDOMElement('IconBulletBlue') : getDOMElement('IconBulletBlueEmpty');
|
|
||||||
ariaLabel = Zotero.getString('pane.item.attachments.hasSnapshot');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//icon = getDOMElement('IconTreeitem');
|
|
||||||
icon = exists ? getDOMElement('IconBulletBlue') : getDOMElement('IconBulletBlueEmpty');
|
|
||||||
ariaLabel = Zotero.getString('pane.item.attachments.has');
|
|
||||||
}
|
|
||||||
icon.classList.add('cell-icon');
|
|
||||||
//if (!exists) {
|
|
||||||
// icon.classList.add('icon-missing-file');
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
//else if (type == 'none') {
|
|
||||||
// if (item.getField('url') || item.getField('DOI')) {
|
|
||||||
// icon = getDOMElement('IconLink');
|
|
||||||
// ariaLabel = Zotero.getString('pane.item.attachments.hasLink');
|
|
||||||
// icon.classList.add('cell-icon');
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
if (ariaLabel) {
|
|
||||||
icon.setAttribute('aria-label', ariaLabel + '.');
|
|
||||||
span.setAttribute('title', ariaLabel);
|
|
||||||
}
|
|
||||||
span.append(icon);
|
|
||||||
|
|
||||||
// Don't run this immediately since it might cause a db check and disk access
|
|
||||||
// but delay for some time and see if the item is still visible in the tree
|
|
||||||
// (i.e. if we haven't scrolled right past it)
|
|
||||||
setTimeout(() => {
|
|
||||||
if (!this.tree.rowIsVisible(index)) return;
|
|
||||||
item.getBestAttachmentState()
|
|
||||||
// Refresh cell when promise is fulfilled
|
|
||||||
.then(({ type: newType, exists: newExists }) => {
|
|
||||||
if (newType !== type || newExists !== exists) {
|
|
||||||
this.tree.invalidateRow(index);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, ATTACHMENT_STATE_LOAD_DELAY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.isFileAttachment()) {
|
|
||||||
const exists = item.fileExistsCached();
|
|
||||||
let icon = "";
|
let icon = "";
|
||||||
if (exists !== null) {
|
let ariaLabel;
|
||||||
if (item.isPDFAttachment()) {
|
// If the item has a child attachment
|
||||||
|
if (type !== null && type != 'none') {
|
||||||
|
if (type == 'pdf') {
|
||||||
icon = getDOMElement('IconTreeitemAttachmentPDF');
|
icon = getDOMElement('IconTreeitemAttachmentPDF');
|
||||||
|
ariaLabel = Zotero.getString('pane.item.attachments.hasPDF');
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
icon.classList.add('icon-missing-file');
|
icon.classList.add('icon-missing-file');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (item.isSnapshotAttachment()) {
|
else if (type == 'snapshot') {
|
||||||
//icon = getDOMElement('IconTreeitemAttachmentSnapshot');
|
//icon = getDOMElement('IconTreeitemAttachmentSnapshot');
|
||||||
icon = exists ? getDOMElement('IconBulletBlue') : getDOMElement('IconBulletBlueEmpty');
|
icon = exists ? getDOMElement('IconBulletBlue') : getDOMElement('IconBulletBlueEmpty');
|
||||||
|
ariaLabel = Zotero.getString('pane.item.attachments.hasSnapshot');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//icon = getDOMElement('IconTreeitem');
|
//icon = getDOMElement('IconTreeitem');
|
||||||
icon = exists ? getDOMElement('IconBulletBlue') : getDOMElement('IconBulletBlueEmpty');
|
icon = exists ? getDOMElement('IconBulletBlue') : getDOMElement('IconBulletBlueEmpty');
|
||||||
|
ariaLabel = Zotero.getString('pane.item.attachments.has');
|
||||||
}
|
}
|
||||||
icon.classList.add('cell-icon');
|
icon.classList.add('cell-icon');
|
||||||
//if (!exists) {
|
//if (!exists) {
|
||||||
// icon.classList.add('icon-missing-file');
|
// icon.classList.add('icon-missing-file');
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
//else if (type == 'none') {
|
||||||
|
// if (item.getField('url') || item.getField('DOI')) {
|
||||||
|
// icon = getDOMElement('IconLink');
|
||||||
|
// ariaLabel = Zotero.getString('pane.item.attachments.hasLink');
|
||||||
|
// icon.classList.add('cell-icon');
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
if (ariaLabel) {
|
||||||
|
icon.setAttribute('aria-label', ariaLabel + '.');
|
||||||
|
span.setAttribute('title', ariaLabel);
|
||||||
|
}
|
||||||
span.append(icon);
|
span.append(icon);
|
||||||
|
|
||||||
item.fileExists()
|
// Don't run this immediately since it might cause a db check and disk access
|
||||||
// TODO: With no cell refreshing this is possibly somewhat inefficient
|
// but delay for some time and see if the item is still visible in the tree
|
||||||
// Refresh cell when promise is fulfilled
|
// (i.e. if we haven't scrolled right past it)
|
||||||
.then(realExists => realExists != exists && this.tree.invalidateRow(index));
|
setTimeout(() => {
|
||||||
|
if (!this.tree.rowIsVisible(index)) return;
|
||||||
|
item.getBestAttachmentState()
|
||||||
|
// Refresh cell when promise is fulfilled
|
||||||
|
.then(({ type: newType, exists: newExists }) => {
|
||||||
|
if (newType !== type || newExists !== exists) {
|
||||||
|
this.tree.invalidateRow(index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, ATTACHMENT_STATE_LOAD_DELAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
return span;
|
return span;
|
||||||
|
|
@ -2911,7 +2900,7 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accessibility
|
// Accessibility
|
||||||
div.setAttribute('role', 'row');
|
div.setAttribute('role', 'treeitem');
|
||||||
div.setAttribute('aria-level', this.getLevel(index) + 1);
|
div.setAttribute('aria-level', this.getLevel(index) + 1);
|
||||||
if (!this.isContainerEmpty(index)) {
|
if (!this.isContainerEmpty(index)) {
|
||||||
div.setAttribute('aria-expanded', this.isContainerOpen(index));
|
div.setAttribute('aria-expanded', this.isContainerOpen(index));
|
||||||
|
|
@ -3550,7 +3539,7 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
let t = new Date();
|
let t = new Date();
|
||||||
for (let i = 0; i < this._rows.length; i++) {
|
for (let i = 0; i < this._rows.length; i++) {
|
||||||
let item = this.getRow(i).ref;
|
let item = this.getRow(i).ref;
|
||||||
if (item.isRegularItem()) {
|
if (this._canGetBestAttachmentState(item)) {
|
||||||
await item.getBestAttachmentState();
|
await item.getBestAttachmentState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3738,7 +3727,7 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
// Restore Default Column Order
|
// Restore Default Column Order
|
||||||
//
|
//
|
||||||
let menuitem = doc.createElementNS(ns, 'menuitem');
|
let menuitem = doc.createElementNS(ns, 'menuitem');
|
||||||
menuitem.setAttribute('label', Zotero.Intl.strings['zotero.items.restoreColumnOrder.label']);
|
menuitem.setAttribute('label', Zotero.getString('zotero.items.restoreColumnOrder.label'));
|
||||||
menuitem.setAttribute('anonid', prefix + 'restore-order');
|
menuitem.setAttribute('anonid', prefix + 'restore-order');
|
||||||
menuitem.addEventListener('command', () => this.tree._columns.restoreDefaultOrder());
|
menuitem.addEventListener('command', () => this.tree._columns.restoreDefaultOrder());
|
||||||
menupopup.appendChild(menuitem);
|
menupopup.appendChild(menuitem);
|
||||||
|
|
@ -3825,9 +3814,9 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
_isOnlyEmoji(str) {
|
_canGetBestAttachmentState(item) {
|
||||||
// Remove emoji and zero-width joiner and see if anything's left
|
return (item.isRegularItem() && item.numAttachments())
|
||||||
return !str.replace(EMOJI_RE, '').replace(/\u200D/g,'');
|
|| (item.isFileAttachment() && item.isTopLevelItem());
|
||||||
}
|
}
|
||||||
|
|
||||||
_getTagSwatch(tag, color) {
|
_getTagSwatch(tag, color) {
|
||||||
|
|
@ -3837,7 +3826,7 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
//
|
//
|
||||||
// TODO: Check for a maximum number of graphemes, which is hard to do
|
// TODO: Check for a maximum number of graphemes, which is hard to do
|
||||||
// https://stackoverflow.com/a/54369605
|
// https://stackoverflow.com/a/54369605
|
||||||
if (this._isOnlyEmoji(tag)) {
|
if (Zotero.Utilities.Internal.isOnlyEmoji(tag)) {
|
||||||
span.textContent = tag;
|
span.textContent = tag;
|
||||||
}
|
}
|
||||||
// Otherwise display color
|
// Otherwise display color
|
||||||
|
|
|
||||||
|
|
@ -207,6 +207,14 @@ const COLUMNS = [
|
||||||
flex: 1,
|
flex: 1,
|
||||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
dataKey: "number",
|
||||||
|
disabledIn: "feed",
|
||||||
|
submenu: true,
|
||||||
|
label: "itemFields.number",
|
||||||
|
flex: 1,
|
||||||
|
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||||
|
},
|
||||||
{
|
{
|
||||||
dataKey: "pages",
|
dataKey: "pages",
|
||||||
disabledIn: "feed",
|
disabledIn: "feed",
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 4877f181b1a962ea3e26d23f12d1acba3ccce347
|
Subproject commit d704bf80afe8a692373e11f670b08553b9f74a31
|
||||||
|
|
@ -24,7 +24,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import VirtualizedTable from 'components/virtualized-table';
|
import VirtualizedTable from 'components/virtualized-table';
|
||||||
const { IntlProvider } = require('react-intl');
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
|
|
@ -41,20 +40,18 @@ function init() {
|
||||||
engines = Zotero.LocateManager.getEngines();
|
engines = Zotero.LocateManager.getEngines();
|
||||||
const domEl = document.querySelector('#locateManager-tree');
|
const domEl = document.querySelector('#locateManager-tree');
|
||||||
let elem = (
|
let elem = (
|
||||||
<IntlProvider locale={Zotero.locale} messages={Zotero.Intl.strings}>
|
<VirtualizedTable
|
||||||
<VirtualizedTable
|
getRowCount={() => engines.length}
|
||||||
getRowCount={() => engines.length}
|
id="locateManager-table"
|
||||||
id="locateManager-table"
|
ref={ref => tree = ref}
|
||||||
ref={ref => tree = ref}
|
renderItem={VirtualizedTable.makeRowRenderer(getRowData)}
|
||||||
renderItem={VirtualizedTable.makeRowRenderer(getRowData)}
|
showHeader={true}
|
||||||
showHeader={true}
|
multiSelect={true}
|
||||||
multiSelect={true}
|
columns={columns}
|
||||||
columns={columns}
|
disableFontSizeScaling={true}
|
||||||
disableFontSizeScaling={true}
|
getRowString={index => getRowData(index).name}
|
||||||
getRowString={index => getRowData(index).name}
|
onActivate={handleActivate}
|
||||||
onActivate={handleActivate}
|
/>
|
||||||
/>
|
|
||||||
</IntlProvider>
|
|
||||||
);
|
);
|
||||||
return new Promise(resolve => ReactDOM.render(elem, domEl, resolve));
|
return new Promise(resolve => ReactDOM.render(elem, domEl, resolve));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -520,7 +520,13 @@ var Zotero_LocateMenu = new function() {
|
||||||
if(attachment.attachmentLinkMode !== Zotero.Attachments.LINK_MODE_LINKED_URL) {
|
if(attachment.attachmentLinkMode !== Zotero.Attachments.LINK_MODE_LINKED_URL) {
|
||||||
var path = yield attachment.getFilePathAsync();
|
var path = yield attachment.getFilePathAsync();
|
||||||
if (path) {
|
if (path) {
|
||||||
var ext = Zotero.File.getExtension(Zotero.File.pathToFile(path));
|
try {
|
||||||
|
var ext = Zotero.File.getExtension(Zotero.File.pathToFile(path));
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
Zotero.logError(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if(!attachment.attachmentContentType ||
|
if(!attachment.attachmentContentType ||
|
||||||
Zotero.MIME.hasNativeHandler(attachment.attachmentContentType, ext) ||
|
Zotero.MIME.hasNativeHandler(attachment.attachmentContentType, ext) ||
|
||||||
!Zotero.MIME.hasInternalHandler(attachment.attachmentContentType, ext)) {
|
!Zotero.MIME.hasInternalHandler(attachment.attachmentContentType, ext)) {
|
||||||
|
|
@ -589,9 +595,23 @@ var Zotero_LocateMenu = new function() {
|
||||||
this.icon = "chrome://zotero/skin/locate-library-lookup.png";
|
this.icon = "chrome://zotero/skin/locate-library-lookup.png";
|
||||||
this.canHandleItem = function (item) { return Zotero.Promise.resolve(item.isRegularItem()); };
|
this.canHandleItem = function (item) { return Zotero.Promise.resolve(item.isRegularItem()); };
|
||||||
this.handleItems = Zotero.Promise.method(function (items, event) {
|
this.handleItems = Zotero.Promise.method(function (items, event) {
|
||||||
// If no resolver configured, just switch to the default
|
// If no resolver configured, show error
|
||||||
if (!Zotero.Prefs.get('openURL.resolver')) {
|
if (!Zotero.Prefs.get('openURL.resolver')) {
|
||||||
Zotero.Prefs.clear('openURL.resolver')
|
let ps = Services.prompt;
|
||||||
|
let buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
|
||||||
|
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL);
|
||||||
|
let index = ps.confirmEx(
|
||||||
|
null,
|
||||||
|
Zotero.getString('locate.libraryLookup.noResolver.title'),
|
||||||
|
Zotero.getString('locate.libraryLookup.noResolver.text', Zotero.appName),
|
||||||
|
buttonFlags,
|
||||||
|
Zotero.getString('general.openPreferences'),
|
||||||
|
null, null, null, {}
|
||||||
|
);
|
||||||
|
if (index == 0) {
|
||||||
|
Zotero.Utilities.Internal.openPreferences('zotero-prefpane-advanced');
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
var urls = [];
|
var urls = [];
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
* @namespace
|
* @namespace
|
||||||
*/
|
*/
|
||||||
var Zotero_Lookup = new function () {
|
var Zotero_Lookup = new function () {
|
||||||
|
this._button = null;
|
||||||
/**
|
/**
|
||||||
* Performs a lookup by DOI, PMID, or ISBN on the given textBox value
|
* Performs a lookup by DOI, PMID, or ISBN on the given textBox value
|
||||||
* and adds any items it can.
|
* and adds any items it can.
|
||||||
|
|
@ -39,7 +40,7 @@ var Zotero_Lookup = new function () {
|
||||||
* @param textBox {HTMLElement} - Textbox containing identifiers
|
* @param textBox {HTMLElement} - Textbox containing identifiers
|
||||||
* @param childItem {Zotero.Item|false} - Child item (optional)
|
* @param childItem {Zotero.Item|false} - Child item (optional)
|
||||||
* @param toggleProgress {function} - Callback to toggle progress on/off
|
* @param toggleProgress {function} - Callback to toggle progress on/off
|
||||||
* @returns {Promise<boolean>}
|
* @returns {Promise<Zotero.Item[]>}
|
||||||
*/
|
*/
|
||||||
this.addItemsFromIdentifier = async function (textBox, childItem, toggleProgress) {
|
this.addItemsFromIdentifier = async function (textBox, childItem, toggleProgress) {
|
||||||
var identifiers = Zotero.Utilities.extractIdentifiers(textBox.value);
|
var identifiers = Zotero.Utilities.extractIdentifiers(textBox.value);
|
||||||
|
|
@ -78,11 +79,28 @@ var Zotero_Lookup = new function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let newItems = false;
|
|
||||||
toggleProgress(true);
|
toggleProgress(true);
|
||||||
|
|
||||||
await Zotero.Promise.all(identifiers.map(async (identifier) => {
|
// Group PubMed IDs into batches of 200
|
||||||
var translate = new Zotero.Translate.Search();
|
//
|
||||||
|
// Up to 10,000 ids can apparently be passed in a single request, but 200 is the recommended
|
||||||
|
// limit for GET, which we currently use, and passing batches of 200 seems...fine.
|
||||||
|
//
|
||||||
|
// https://www.ncbi.nlm.nih.gov/books/NBK25499/#chapter4.id
|
||||||
|
if (identifiers.length && identifiers[0].PMID) {
|
||||||
|
let chunkSize = 200;
|
||||||
|
let newIdentifiers = [];
|
||||||
|
for (let i = 0; i < identifiers.length; i += chunkSize) {
|
||||||
|
newIdentifiers.push({
|
||||||
|
PMID: identifiers.slice(i, i + chunkSize).map(x => x.PMID)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
identifiers = newIdentifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
let newItems = [];
|
||||||
|
for (let identifier of identifiers) {
|
||||||
|
let translate = new Zotero.Translate.Search();
|
||||||
translate.setIdentifier(identifier);
|
translate.setIdentifier(identifier);
|
||||||
|
|
||||||
// be lenient about translators
|
// be lenient about translators
|
||||||
|
|
@ -90,20 +108,20 @@ var Zotero_Lookup = new function () {
|
||||||
translate.setTranslator(translators);
|
translate.setTranslator(translators);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
newItems = await translate.translate({
|
newItems.push(...await translate.translate({
|
||||||
libraryID,
|
libraryID,
|
||||||
collections,
|
collections,
|
||||||
saveAttachments: !childItem
|
saveAttachments: !childItem
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
// Continue with other ids on failure
|
// Continue with other ids on failure
|
||||||
catch (e) {
|
catch (e) {
|
||||||
Zotero.logError(e);
|
Zotero.logError(e);
|
||||||
}
|
}
|
||||||
}));
|
}
|
||||||
|
|
||||||
toggleProgress(false);
|
toggleProgress(false);
|
||||||
if (!newItems) {
|
if (!newItems.length) {
|
||||||
Zotero.alert(
|
Zotero.alert(
|
||||||
window,
|
window,
|
||||||
Zotero.getString("lookup.failure.title"),
|
Zotero.getString("lookup.failure.title"),
|
||||||
|
|
@ -125,7 +143,7 @@ var Zotero_Lookup = new function () {
|
||||||
on => Zotero_Lookup.toggleProgress(on)
|
on => Zotero_Lookup.toggleProgress(on)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (newItems) {
|
if (newItems.length) {
|
||||||
document.getElementById("zotero-lookup-panel").hidePopup();
|
document.getElementById("zotero-lookup-panel").hidePopup();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -134,6 +152,10 @@ var Zotero_Lookup = new function () {
|
||||||
|
|
||||||
this.showPanel = function (button) {
|
this.showPanel = function (button) {
|
||||||
var panel = document.getElementById('zotero-lookup-panel');
|
var panel = document.getElementById('zotero-lookup-panel');
|
||||||
|
this._button = button;
|
||||||
|
if (!button) {
|
||||||
|
button = document.getElementById("zotero-tb-lookup");
|
||||||
|
}
|
||||||
panel.openPopup(button, "after_start", 16, -2, false, false);
|
panel.openPopup(button, "after_start", 16, -2, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -202,6 +224,25 @@ var Zotero_Lookup = new function () {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.onFocusOut = function(event) {
|
||||||
|
/*
|
||||||
|
if the lookup popup was triggered by the lookup button,
|
||||||
|
we want to return there on focus out. So we check
|
||||||
|
(1) that we came from a button and (2) that
|
||||||
|
event.relatedTarget === null, i.e. that the user hasn't used
|
||||||
|
the mouse or keyboard to select something, and focus is leaving
|
||||||
|
the popup because the popup was hidden/dismissed.
|
||||||
|
*/
|
||||||
|
if (this._button && event.relatedTarget === null) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
this._button.focus();
|
||||||
|
this._button = null;
|
||||||
|
} else {
|
||||||
|
this._button = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
this.onInput = function (event, textbox) {
|
this.onInput = function (event, textbox) {
|
||||||
this.adjustTextbox(textbox);
|
this.adjustTextbox(textbox);
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,6 @@ Components.utils.import("resource://gre/modules/Services.jsm");
|
||||||
import FilePicker from 'zotero/modules/filePicker';
|
import FilePicker from 'zotero/modules/filePicker';
|
||||||
|
|
||||||
Zotero_Preferences.Advanced = {
|
Zotero_Preferences.Advanced = {
|
||||||
DEFAULT_OPENURL_RESOLVER: 'https://www.worldcat.org/registry/gateway',
|
|
||||||
|
|
||||||
_openURLResolvers: null,
|
_openURLResolvers: null,
|
||||||
|
|
||||||
init: function () {
|
init: function () {
|
||||||
|
|
@ -462,12 +460,6 @@ Zotero_Preferences.Advanced = {
|
||||||
var menupopup = openURLMenu.firstChild;
|
var menupopup = openURLMenu.firstChild;
|
||||||
menupopup.innerHTML = '';
|
menupopup.innerHTML = '';
|
||||||
|
|
||||||
var defaultMenuItem = document.createElement('menuitem');
|
|
||||||
defaultMenuItem.setAttribute('label', Zotero.getString('general.default'));
|
|
||||||
defaultMenuItem.setAttribute('value', this.DEFAULT_OPENURL_RESOLVER);
|
|
||||||
defaultMenuItem.setAttribute('type', 'checkbox');
|
|
||||||
menupopup.appendChild(defaultMenuItem);
|
|
||||||
|
|
||||||
var customMenuItem = document.createElement('menuitem');
|
var customMenuItem = document.createElement('menuitem');
|
||||||
customMenuItem.setAttribute('label', Zotero.getString('general.custom'));
|
customMenuItem.setAttribute('label', Zotero.getString('general.custom'));
|
||||||
customMenuItem.setAttribute('value', 'custom');
|
customMenuItem.setAttribute('value', 'custom');
|
||||||
|
|
@ -521,13 +513,8 @@ Zotero_Preferences.Advanced = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default
|
// From directory
|
||||||
if (currentResolver == this.DEFAULT_OPENURL_RESOLVER) {
|
if (selectedName) {
|
||||||
openURLMenu.setAttribute('label', Zotero.getString('general.default'));
|
|
||||||
defaultMenuItem.setAttribute('checked', true);
|
|
||||||
Zotero.Prefs.clear('openURL.name');
|
|
||||||
}
|
|
||||||
else if (selectedName) {
|
|
||||||
openURLMenu.setAttribute('label', selectedName);
|
openURLMenu.setAttribute('label', selectedName);
|
||||||
// If we found a match, update stored name
|
// If we found a match, update stored name
|
||||||
Zotero.Prefs.set('openURL.name', selectedName);
|
Zotero.Prefs.set('openURL.name', selectedName);
|
||||||
|
|
@ -555,15 +542,8 @@ Zotero_Preferences.Advanced = {
|
||||||
var openURLServerField = document.getElementById('openURLServerField');
|
var openURLServerField = document.getElementById('openURLServerField');
|
||||||
var openURLVersionMenu = document.getElementById('openURLVersionMenu');
|
var openURLVersionMenu = document.getElementById('openURLVersionMenu');
|
||||||
|
|
||||||
// Default
|
|
||||||
if (event.target.value == this.DEFAULT_OPENURL_RESOLVER) {
|
|
||||||
Zotero.Prefs.clear('openURL.name');
|
|
||||||
Zotero.Prefs.clear('openURL.resolver');
|
|
||||||
Zotero.Prefs.clear('openURL.version');
|
|
||||||
openURLServerField.value = this.DEFAULT_OPENURL_RESOLVER;
|
|
||||||
}
|
|
||||||
// If "Custom" selected, clear URL field
|
// If "Custom" selected, clear URL field
|
||||||
else if (event.target.value == "custom") {
|
if (event.target.value == "custom") {
|
||||||
Zotero.Prefs.clear('openURL.name');
|
Zotero.Prefs.clear('openURL.name');
|
||||||
Zotero.Prefs.set('openURL.resolver', '');
|
Zotero.Prefs.set('openURL.resolver', '');
|
||||||
Zotero.Prefs.clear('openURL.version');
|
Zotero.Prefs.clear('openURL.version');
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ import FilePicker from 'zotero/modules/filePicker';
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
var ReactDOM = require('react-dom');
|
var ReactDOM = require('react-dom');
|
||||||
var VirtualizedTable = require('components/virtualized-table');
|
var VirtualizedTable = require('components/virtualized-table');
|
||||||
var { IntlProvider } = require('react-intl');
|
|
||||||
var { makeRowRenderer } = VirtualizedTable;
|
var { makeRowRenderer } = VirtualizedTable;
|
||||||
|
|
||||||
Zotero_Preferences.Cite = {
|
Zotero_Preferences.Cite = {
|
||||||
|
|
@ -118,22 +117,20 @@ Zotero_Preferences.Cite = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let elem = (
|
let elem = (
|
||||||
<IntlProvider locale={Zotero.locale} messages={Zotero.Intl.strings}>
|
<VirtualizedTable
|
||||||
<VirtualizedTable
|
getRowCount={() => this.styles.length}
|
||||||
getRowCount={() => this.styles.length}
|
id="styleManager-table"
|
||||||
id="styleManager-table"
|
ref={ref => this._tree = ref}
|
||||||
ref={ref => this._tree = ref}
|
renderItem={makeRowRenderer(index => this.styles[index])}
|
||||||
renderItem={makeRowRenderer(index => this.styles[index])}
|
showHeader={true}
|
||||||
showHeader={true}
|
multiSelect={true}
|
||||||
multiSelect={true}
|
columns={columns}
|
||||||
columns={columns}
|
staticColumns={true}
|
||||||
staticColumns={true}
|
disableFontSizeScaling={true}
|
||||||
disableFontSizeScaling={true}
|
onSelectionChange={() => document.getElementById('styleManager-delete').disabled = undefined}
|
||||||
onSelectionChange={() => document.getElementById('styleManager-delete').disabled = undefined}
|
onKeyDown={handleKeyDown}
|
||||||
onKeyDown={handleKeyDown}
|
getRowString={index => this.styles[index].title}
|
||||||
getRowString={index => this.styles[index].title}
|
/>
|
||||||
/>
|
|
||||||
</IntlProvider>
|
|
||||||
);
|
);
|
||||||
await new Promise(resolve => ReactDOM.render(elem, document.getElementById("styleManager"), resolve));
|
await new Promise(resolve => ReactDOM.render(elem, document.getElementById("styleManager"), resolve));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
var ReactDOM = require('react-dom');
|
var ReactDOM = require('react-dom');
|
||||||
var VirtualizedTable = require('components/virtualized-table');
|
var VirtualizedTable = require('components/virtualized-table');
|
||||||
var { IntlProvider } = require('react-intl');
|
|
||||||
var { makeRowRenderer } = VirtualizedTable;
|
var { makeRowRenderer } = VirtualizedTable;
|
||||||
|
|
||||||
Zotero_Preferences.Export = {
|
Zotero_Preferences.Export = {
|
||||||
|
|
@ -89,11 +88,15 @@ Zotero_Preferences.Export = {
|
||||||
* Builds the note Quick Copy drop-down from the current global pref
|
* Builds the note Quick Copy drop-down from the current global pref
|
||||||
*/
|
*/
|
||||||
populateNoteQuickCopyList: async function () {
|
populateNoteQuickCopyList: async function () {
|
||||||
|
document.getElementById('noteQuickCopy-format-options').removeAttribute('hidden');
|
||||||
|
|
||||||
// Initialize default format drop-down
|
// Initialize default format drop-down
|
||||||
var format = Zotero.Prefs.get("export.noteQuickCopy.setting");
|
var format = Zotero.Prefs.get("export.noteQuickCopy.setting");
|
||||||
format = Zotero.QuickCopy.unserializeSetting(format);
|
format = Zotero.QuickCopy.unserializeSetting(format);
|
||||||
var menulist = document.getElementById("zotero-noteQuickCopy-menu");
|
var menulist = document.getElementById("zotero-noteQuickCopy-menu");
|
||||||
menulist.setAttribute('preference', "pref-noteQuickCopy-setting");
|
menulist.setAttribute('preference', "pref-noteQuickCopy-setting");
|
||||||
|
menulist.removeEventListener('command', this.updateNoteQuickCopyUI);
|
||||||
|
menulist.addEventListener('command', this.updateNoteQuickCopyUI);
|
||||||
|
|
||||||
if (!format) {
|
if (!format) {
|
||||||
format = menulist.value;
|
format = menulist.value;
|
||||||
|
|
@ -149,11 +152,42 @@ Zotero_Preferences.Export = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var val = JSON.stringify({ mode: 'export', id: translator.translatorID });
|
var value = { mode: 'export', id: translator.translatorID };
|
||||||
|
if (translator.translatorID == format.id) {
|
||||||
|
value = format;
|
||||||
|
}
|
||||||
|
else if (translator.translatorID == Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) {
|
||||||
|
value = {
|
||||||
|
mode: 'export',
|
||||||
|
id: translator.translatorID,
|
||||||
|
markdownOptions: {
|
||||||
|
includeAppLinks: true
|
||||||
|
},
|
||||||
|
htmlOptions: {
|
||||||
|
includeAppLinks: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (format.id == Zotero.Translators.TRANSLATOR_ID_NOTE_HTML && format.options) {
|
||||||
|
value.htmlOptions = format.options;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (translator.translatorID == Zotero.Translators.TRANSLATOR_ID_NOTE_HTML) {
|
||||||
|
value = {
|
||||||
|
mode: 'export',
|
||||||
|
id: translator.translatorID,
|
||||||
|
options: {
|
||||||
|
includeAppLinks: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (format.id == Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT && format.htmlOptions) {
|
||||||
|
value.options = format.htmlOptions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value = JSON.stringify(value);
|
||||||
var itemNode = document.createElement('menuitem');
|
var itemNode = document.createElement('menuitem');
|
||||||
itemNode.setAttribute('value', val);
|
itemNode.setAttribute('value', value);
|
||||||
itemNode.setAttribute('label', translator.label);
|
itemNode.setAttribute('label', translator.label);
|
||||||
// itemNode.setAttribute('oncommand', 'Zotero_Preferences.Export.updateQuickCopyUI()');
|
|
||||||
popup.appendChild(itemNode);
|
popup.appendChild(itemNode);
|
||||||
|
|
||||||
if (format.mode == 'export' && format.id == translator.translatorID) {
|
if (format.mode == 'export' && format.id == translator.translatorID) {
|
||||||
|
|
@ -162,6 +196,76 @@ Zotero_Preferences.Export = {
|
||||||
});
|
});
|
||||||
|
|
||||||
menulist.click();
|
menulist.click();
|
||||||
|
this.updateNoteQuickCopyUI();
|
||||||
|
},
|
||||||
|
|
||||||
|
updateNoteQuickCopyUI: () => {
|
||||||
|
var format = document.getElementById('zotero-noteQuickCopy-menu').value;
|
||||||
|
format = JSON.parse(format);
|
||||||
|
|
||||||
|
var markdownOptions = document.getElementById('noteQuickCopy-markdown-options');
|
||||||
|
var htmlOptions = document.getElementById('noteQuickCopy-html-options');
|
||||||
|
var markdownOptionsLabel = document.querySelector('#noteQuickCopy-markdown-options label');
|
||||||
|
var htmlOptionsLabel = document.querySelector('#noteQuickCopy-html-options label');
|
||||||
|
var markdownIncludeAppLinks = document.getElementById("noteQuickCopy-markdown-includeAppLinks");
|
||||||
|
var htmlIncludeAppLinks = document.getElementById("noteQuickCopy-html-includeAppLinks");
|
||||||
|
|
||||||
|
markdownOptionsLabel.value = Zotero.Utilities.Internal.stringWithColon("Markdown");
|
||||||
|
htmlOptionsLabel.value = Zotero.Utilities.Internal.stringWithColon(
|
||||||
|
Zotero.getString('zotero.preferences.export.quickCopy.note.htmlOptions.label')
|
||||||
|
);
|
||||||
|
markdownIncludeAppLinks.label = Zotero.getString('exportOptions.includeAppLinks', Zotero.appName);
|
||||||
|
htmlIncludeAppLinks.label = Zotero.getString('exportOptions.includeAppLinks', Zotero.appName);
|
||||||
|
|
||||||
|
if (format.id == Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) {
|
||||||
|
markdownOptions.hidden = false;
|
||||||
|
htmlOptions.hidden = false;
|
||||||
|
markdownIncludeAppLinks.checked = format.markdownOptions && format.markdownOptions.includeAppLinks;
|
||||||
|
htmlIncludeAppLinks.checked = format.htmlOptions && format.htmlOptions.includeAppLinks;
|
||||||
|
}
|
||||||
|
else if (format.id == Zotero.Translators.TRANSLATOR_ID_NOTE_HTML) {
|
||||||
|
markdownOptions.hidden = true;
|
||||||
|
htmlOptions.hidden = false;
|
||||||
|
htmlIncludeAppLinks.checked = format.options && format.options.includeAppLinks;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
markdownOptions.hidden = true;
|
||||||
|
htmlOptions.hidden = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onUpdateNoteExportOptions() {
|
||||||
|
var menulist = document.getElementById("zotero-noteQuickCopy-menu");
|
||||||
|
var markdownIncludeAppLinks = document.getElementById("noteQuickCopy-markdown-includeAppLinks");
|
||||||
|
var htmlIncludeAppLinks = document.getElementById("noteQuickCopy-html-includeAppLinks");
|
||||||
|
|
||||||
|
for (let i = 0; i < menulist.itemCount; i++) {
|
||||||
|
let item = menulist.getItemAtIndex(i);
|
||||||
|
let format = JSON.parse(item.getAttribute('value'));
|
||||||
|
if (format.id == Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) {
|
||||||
|
if (!format.markdownOptions) {
|
||||||
|
format.markdownOptions = {};
|
||||||
|
}
|
||||||
|
if (!format.htmlOptions) {
|
||||||
|
format.htmlOptions = {};
|
||||||
|
}
|
||||||
|
format.markdownOptions.includeAppLinks = markdownIncludeAppLinks.checked;
|
||||||
|
format.htmlOptions.includeAppLinks = htmlIncludeAppLinks.checked;
|
||||||
|
}
|
||||||
|
else if (format.id == Zotero.Translators.TRANSLATOR_ID_NOTE_HTML) {
|
||||||
|
if (!format.options) {
|
||||||
|
format.options = {};
|
||||||
|
}
|
||||||
|
format.options.includeAppLinks = htmlIncludeAppLinks.checked;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
item.value = JSON.stringify(format);
|
||||||
|
}
|
||||||
|
// After updating menulist item value we have to wait a bit before doing click(), to avoid anomalies
|
||||||
|
// like an empty row in menulist. 0 in setTimeout is not enough
|
||||||
|
setTimeout(() => menulist.click(), 50);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -387,22 +491,20 @@ Zotero_Preferences.Export = {
|
||||||
};
|
};
|
||||||
|
|
||||||
let elem = (
|
let elem = (
|
||||||
<IntlProvider locale={Zotero.locale} messages={Zotero.Intl.strings}>
|
<VirtualizedTable
|
||||||
<VirtualizedTable
|
getRowCount={() => this._rows.length}
|
||||||
getRowCount={() => this._rows.length}
|
id="quickCopy-siteSettings-table"
|
||||||
id="quickCopy-siteSettings-table"
|
ref={ref => this._tree = ref}
|
||||||
ref={ref => this._tree = ref}
|
renderItem={makeRowRenderer(index => this._rows[index])}
|
||||||
renderItem={makeRowRenderer(index => this._rows[index])}
|
showHeader={true}
|
||||||
showHeader={true}
|
columns={columns}
|
||||||
columns={columns}
|
staticColumns={true}
|
||||||
staticColumns={true}
|
disableFontSizeScaling={true}
|
||||||
disableFontSizeScaling={true}
|
onSelectionChange={handleSelectionChange}
|
||||||
onSelectionChange={handleSelectionChange}
|
onKeyDown={handleKeyDown}
|
||||||
onKeyDown={handleKeyDown}
|
getRowString={index => this._rows[index].domain}
|
||||||
getRowString={index => this._rows[index].domain}
|
onActivate={(event, indices) => Zotero_Preferences.Export.showQuickCopySiteEditor()}
|
||||||
onActivate={(event, indices) => Zotero_Preferences.Export.showQuickCopySiteEditor()}
|
/>
|
||||||
/>
|
|
||||||
</IntlProvider>
|
|
||||||
);
|
);
|
||||||
await new Promise(resolve => ReactDOM.render(elem, document.getElementById("quickCopy-siteSettings"), resolve));
|
await new Promise(resolve => ReactDOM.render(elem, document.getElementById("quickCopy-siteSettings"), resolve));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,35 @@
|
||||||
<label value="&zotero.preferences.quickCopy.noteFormat;" control="zotero-noteQuickCopy-menu"/>
|
<label value="&zotero.preferences.quickCopy.noteFormat;" control="zotero-noteQuickCopy-menu"/>
|
||||||
<menulist id="zotero-noteQuickCopy-menu" label="&zotero.general.loading;"/>
|
<menulist id="zotero-noteQuickCopy-menu" label="&zotero.general.loading;"/>
|
||||||
|
|
||||||
|
<table id="noteQuickCopy-format-options"
|
||||||
|
xmlns="http://www.w3.org/1999/xhtml"
|
||||||
|
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||||
|
hidden="hidden">
|
||||||
|
<tbody id="noteQuickCopy-markdown-options">
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<xul:label/>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<xul:checkbox id="noteQuickCopy-markdown-includeAppLinks"
|
||||||
|
oncommand="Zotero_Preferences.Export.onUpdateNoteExportOptions()"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
<tbody id="noteQuickCopy-html-options">
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<xul:label/>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<xul:checkbox id="noteQuickCopy-html-includeAppLinks"
|
||||||
|
oncommand="Zotero_Preferences.Export.onUpdateNoteExportOptions()"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
<separator/>
|
<separator/>
|
||||||
|
|
||||||
<label value="&zotero.preferences.quickCopy.siteEditor.setings;" control="quickCopy-siteSettings"/>
|
<label value="&zotero.preferences.quickCopy.siteEditor.setings;" control="quickCopy-siteSettings"/>
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,8 @@
|
||||||
<preference id="pref-automaticTags" name="extensions.zotero.automaticTags" type="bool"/>
|
<preference id="pref-automaticTags" name="extensions.zotero.automaticTags" type="bool"/>
|
||||||
<preference id="pref-trashAutoEmptyDays" name="extensions.zotero.trashAutoEmptyDays" type="int"/>
|
<preference id="pref-trashAutoEmptyDays" name="extensions.zotero.trashAutoEmptyDays" type="int"/>
|
||||||
|
|
||||||
|
<preference id="pref-reader-tabs-title" name="extensions.zotero.tabs.title.reader" type="string"/>
|
||||||
|
|
||||||
<preference id="pref-groups-copyChildNotes" name="extensions.zotero.groups.copyChildNotes" type="bool"/>
|
<preference id="pref-groups-copyChildNotes" name="extensions.zotero.groups.copyChildNotes" type="bool"/>
|
||||||
<preference id="pref-groups-copyChildFileAttachments" name="extensions.zotero.groups.copyChildFileAttachments" type="bool"/>
|
<preference id="pref-groups-copyChildFileAttachments" name="extensions.zotero.groups.copyChildFileAttachments" type="bool"/>
|
||||||
<preference id="pref-groups-copyAnnotations" name="extensions.zotero.groups.copyAnnotations" type="bool"/>
|
<preference id="pref-groups-copyAnnotations" name="extensions.zotero.groups.copyAnnotations" type="bool"/>
|
||||||
|
|
@ -90,6 +92,22 @@
|
||||||
</hbox>
|
</hbox>
|
||||||
</groupbox>
|
</groupbox>
|
||||||
|
|
||||||
|
<groupbox id="zotero-prefpane-reader-groupbox">
|
||||||
|
<caption label="&zotero.preferences.pdfReader;"/>
|
||||||
|
|
||||||
|
<hbox align="center">
|
||||||
|
<label value="&zotero.preferences.reader.tabsTitle.label;" control="reader-tabs-title"/>
|
||||||
|
<menulist id="reader-tabs-title"
|
||||||
|
preference="pref-reader-tabs-title">
|
||||||
|
<menupopup>
|
||||||
|
<menuitem label="&zotero.preferences.reader.tabsTitle.titleCreatorYear;" value="titleCreatorYear"/>
|
||||||
|
<menuitem label="&zotero.preferences.reader.tabsTitle.creatorYearTitle;" value="creatorYearTitle"/>
|
||||||
|
<menuitem label="&zotero.preferences.reader.tabsTitle.filename;" value="filename"/>
|
||||||
|
</menupopup>
|
||||||
|
</menulist>
|
||||||
|
</hbox>
|
||||||
|
</groupbox>
|
||||||
|
|
||||||
<groupbox>
|
<groupbox>
|
||||||
<caption label="&zotero.preferences.groups;"/>
|
<caption label="&zotero.preferences.groups;"/>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ var React = require('react');
|
||||||
var ReactDOM = require('react-dom');
|
var ReactDOM = require('react-dom');
|
||||||
var VirtualizedTable = require('components/virtualized-table');
|
var VirtualizedTable = require('components/virtualized-table');
|
||||||
var { getDOMElement } = require('components/icons');
|
var { getDOMElement } = require('components/icons');
|
||||||
var { IntlProvider } = require('react-intl');
|
|
||||||
var { renderCell } = VirtualizedTable;
|
var { renderCell } = VirtualizedTable;
|
||||||
|
|
||||||
Zotero_Preferences.Sync = {
|
Zotero_Preferences.Sync = {
|
||||||
|
|
@ -319,20 +318,18 @@ Zotero_Preferences.Sync = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let elem = (
|
let elem = (
|
||||||
<IntlProvider locale={Zotero.locale} messages={Zotero.Intl.strings}>
|
<VirtualizedTable
|
||||||
<VirtualizedTable
|
getRowCount={() => this._rows.length}
|
||||||
getRowCount={() => this._rows.length}
|
id="librariesToSync-table"
|
||||||
id="librariesToSync-table"
|
ref={ref => this._tree = ref}
|
||||||
ref={ref => this._tree = ref}
|
renderItem={renderItem}
|
||||||
renderItem={renderItem}
|
showHeader={true}
|
||||||
showHeader={true}
|
columns={columns}
|
||||||
columns={columns}
|
staticColumns={true}
|
||||||
staticColumns={true}
|
getRowString={index => this._rows[index].name}
|
||||||
getRowString={index => this._rows[index].name}
|
disableFontSizeScaling={true}
|
||||||
disableFontSizeScaling={true}
|
onKeyDown={handleKeyDown}
|
||||||
onKeyDown={handleKeyDown}
|
/>
|
||||||
/>
|
|
||||||
</IntlProvider>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
ReactDOM.render(elem, document.getElementById("libraries-to-sync-tree"));
|
ReactDOM.render(elem, document.getElementById("libraries-to-sync-tree"));
|
||||||
|
|
|
||||||
|
|
@ -288,15 +288,15 @@
|
||||||
|
|
||||||
<ul id="sync-reset-list">
|
<ul id="sync-reset-list">
|
||||||
<!--<li>
|
<!--<li>
|
||||||
<p>&zotero.preferences.sync.reset.restoreFromServer;</p>
|
<p>&zotero.preferences.sync.reset.restoreFromOnlineLibrary;</p>
|
||||||
<p>&zotero.preferences.sync.reset.restoreFromServer.desc;</p>
|
<p>&zotero.preferences.sync.reset.restoreFromOnlineLibrary.desc;</p>
|
||||||
</li>-->
|
</li>-->
|
||||||
|
|
||||||
<li id="restore-to-server">
|
<li id="restore-to-server">
|
||||||
<label>
|
<label>
|
||||||
<input name="sync-reset-radiogroup" value="restore-to-server" type="radio"/>
|
<input name="sync-reset-radiogroup" value="restore-to-server" type="radio"/>
|
||||||
<span class="sync-reset-option-name">&zotero.preferences.sync.reset.restoreToServer;</span>
|
<span class="sync-reset-option-name">&zotero.preferences.sync.reset.restoreToOnlineLibrary;</span>
|
||||||
<span class="sync-reset-option-desc">&zotero.preferences.sync.reset.restoreToServer.desc;</span>
|
<span class="sync-reset-option-desc">&zotero.preferences.sync.reset.restoreToOnlineLibrary.desc;</span>
|
||||||
</label>
|
</label>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,25 @@
|
||||||
<menuitem id="menu_copy"/>
|
<menuitem id="menu_copy"/>
|
||||||
<menuitem id="menu_paste"/>
|
<menuitem id="menu_paste"/>
|
||||||
<menuitem id="menu_delete"/>
|
<menuitem id="menu_delete"/>
|
||||||
|
<menuseparator class="menu-type-reader"/>
|
||||||
|
<menuitem
|
||||||
|
id="edit-menuitem-rotate-left"
|
||||||
|
class="menu-type-reader"
|
||||||
|
label="&zotero.pdfReader.rotatePageLeft;"
|
||||||
|
oncommand="menuCmd('rotateLeft')"
|
||||||
|
/>
|
||||||
|
<menuitem
|
||||||
|
id="edit-menuitem-rotate-right"
|
||||||
|
class="menu-type-reader"
|
||||||
|
label="&zotero.pdfReader.rotatePageRight;"
|
||||||
|
oncommand="menuCmd('rotateRight')"
|
||||||
|
/>
|
||||||
|
<!--<menuitem
|
||||||
|
id="edit-menuitem-rotate-180"
|
||||||
|
class="menu-type-reader"
|
||||||
|
label="&zotero.pdfReader.rotatePage180;"
|
||||||
|
oncommand="menuCmd('rotate180')"
|
||||||
|
/>-->
|
||||||
</menupopup>
|
</menupopup>
|
||||||
</menu>
|
</menu>
|
||||||
<menu label="View" onpopupshowing="onViewMenuOpen()">
|
<menu label="View" onpopupshowing="onViewMenuOpen()">
|
||||||
|
|
@ -81,15 +100,6 @@
|
||||||
-->
|
-->
|
||||||
<!-- <menuitem label="Switch to Presentation Mode" oncommand="menuCmd('presentationmode')"/>-->
|
<!-- <menuitem label="Switch to Presentation Mode" oncommand="menuCmd('presentationmode')"/>-->
|
||||||
<!-- <menuseparator/>-->
|
<!-- <menuseparator/>-->
|
||||||
<menuitem
|
|
||||||
label="&zotero.pdfReader.rotateClockwise;"
|
|
||||||
oncommand="menuCmd('rotatecw')"
|
|
||||||
/>
|
|
||||||
<menuitem
|
|
||||||
label="&zotero.pdfReader.rotateCounterclockwise;"
|
|
||||||
oncommand="menuCmd('rotateccw')"
|
|
||||||
/>
|
|
||||||
<menuseparator/>
|
|
||||||
<menuitem
|
<menuitem
|
||||||
id="view-menuitem-hand-tool"
|
id="view-menuitem-hand-tool"
|
||||||
label="&zotero.pdfReader.handTool;"
|
label="&zotero.pdfReader.handTool;"
|
||||||
|
|
@ -163,6 +173,19 @@
|
||||||
label="&zotero.pdfReader.zoomPageHeight;"
|
label="&zotero.pdfReader.zoomPageHeight;"
|
||||||
oncommand="menuCmd('zoomPageHeight')"
|
oncommand="menuCmd('zoomPageHeight')"
|
||||||
/>
|
/>
|
||||||
|
<menuseparator class="menu-type-reader"/>
|
||||||
|
<menuitem
|
||||||
|
id="view-menuitem-split-horizontally"
|
||||||
|
type="checkbox"
|
||||||
|
label="&zotero.pdfReader.splitHorizontally;"
|
||||||
|
oncommand="menuCmd('splitHorizontally')"
|
||||||
|
/>
|
||||||
|
<menuitem
|
||||||
|
id="view-menuitem-split-vertically"
|
||||||
|
type="checkbox"
|
||||||
|
label="&zotero.pdfReader.splitVertically;"
|
||||||
|
oncommand="menuCmd('splitVertically')"
|
||||||
|
/>
|
||||||
</menupopup>
|
</menupopup>
|
||||||
</menu>
|
</menu>
|
||||||
<menu
|
<menu
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,8 @@ var Zotero_Report_Interface = new function() {
|
||||||
*/
|
*/
|
||||||
this.loadCollectionReport = function () {
|
this.loadCollectionReport = function () {
|
||||||
var sortColumn = ZoteroPane_Local.getSortField();
|
var sortColumn = ZoteroPane_Local.getSortField();
|
||||||
var sortDirection = ZoteroPane_Local.getSortDirection();
|
|
||||||
var queryString = '?sort=' + sortColumn
|
var queryString = '?sort=' + sortColumn
|
||||||
+ '&direction=' + (sortDirection == 'ascending' ? 'asc' : 'desc');
|
+ '&direction=' + (ZoteroPane.getSortDirection() == 1 ? 'asc' : 'desc');
|
||||||
|
|
||||||
var url = 'zotero://report/';
|
var url = 'zotero://report/';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ import FilePicker from 'zotero/modules/filePicker';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import VirtualizedTable from 'components/virtualized-table';
|
import VirtualizedTable from 'components/virtualized-table';
|
||||||
import { IntlProvider } from 'react-intl';
|
|
||||||
import { getDOMElement } from 'components/icons';
|
import { getDOMElement } from 'components/icons';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -51,9 +50,9 @@ var Zotero_RTFScan = new function() {
|
||||||
var ids = 0;
|
var ids = 0;
|
||||||
var tree;
|
var tree;
|
||||||
this._rows = [
|
this._rows = [
|
||||||
{ id: 'unmapped', rtf: Zotero.Intl.strings['zotero.rtfScan.unmappedCitations.label'], collapsed: false },
|
{ id: 'unmapped', rtf: Zotero.getString('zotero.rtfScan.unmappedCitations.label'), collapsed: false },
|
||||||
{ id: 'ambiguous', rtf: Zotero.Intl.strings['zotero.rtfScan.ambiguousCitations.label'], collapsed: false },
|
{ id: 'ambiguous', rtf: Zotero.getString('zotero.rtfScan.ambiguousCitations.label'), collapsed: false },
|
||||||
{ id: 'mapped', rtf: Zotero.Intl.strings['zotero.rtfScan.mappedCitations.label'], collapsed: false },
|
{ id: 'mapped', rtf: Zotero.getString('zotero.rtfScan.mappedCitations.label'), collapsed: false },
|
||||||
];
|
];
|
||||||
this._rowMap = {};
|
this._rowMap = {};
|
||||||
this._rows.forEach((row, index) => this._rowMap[row.id] = index);
|
this._rows.forEach((row, index) => this._rowMap[row.id] = index);
|
||||||
|
|
@ -388,9 +387,9 @@ var Zotero_RTFScan = new function() {
|
||||||
document.documentElement.currentPage = document.getElementById('intro-page');
|
document.documentElement.currentPage = document.getElementById('intro-page');
|
||||||
|
|
||||||
this._rows = [
|
this._rows = [
|
||||||
{ id: 'unmapped', rtf: Zotero.Intl.strings['zotero.rtfScan.unmappedCitations.label'], collapsed: false },
|
{ id: 'unmapped', rtf: Zotero.getString('zotero.rtfScan.unmappedCitations.label'), collapsed: false },
|
||||||
{ id: 'ambiguous', rtf: Zotero.Intl.strings['zotero.rtfScan.ambiguousCitations.label'], collapsed: false },
|
{ id: 'ambiguous', rtf: Zotero.getString('zotero.rtfScan.ambiguousCitations.label'), collapsed: false },
|
||||||
{ id: 'mapped', rtf: Zotero.Intl.strings['zotero.rtfScan.mappedCitations.label'], collapsed: false },
|
{ id: 'mapped', rtf: Zotero.getString('zotero.rtfScan.mappedCitations.label'), collapsed: false },
|
||||||
];
|
];
|
||||||
this._rowMap = {};
|
this._rowMap = {};
|
||||||
this._rows.forEach((row, index) => this._rowMap[row.id] = index);
|
this._rows.forEach((row, index) => this._rowMap[row.id] = index);
|
||||||
|
|
@ -768,17 +767,15 @@ var Zotero_RTFScan = new function() {
|
||||||
this._initCitationTree = function () {
|
this._initCitationTree = function () {
|
||||||
const domEl = document.querySelector('#tree');
|
const domEl = document.querySelector('#tree');
|
||||||
const elem = (
|
const elem = (
|
||||||
<IntlProvider locale={Zotero.locale} messages={Zotero.Intl.strings}>
|
<VirtualizedTable
|
||||||
<VirtualizedTable
|
getRowCount={() => this._rows.length}
|
||||||
getRowCount={() => this._rows.length}
|
id="rtfScan-table"
|
||||||
id="rtfScan-table"
|
ref={ref => tree = ref}
|
||||||
ref={ref => tree = ref}
|
renderItem={this._renderItem}
|
||||||
renderItem={this._renderItem}
|
showHeader={true}
|
||||||
showHeader={true}
|
columns={columns}
|
||||||
columns={columns}
|
disableFontSizeScaling={true}
|
||||||
disableFontSizeScaling={true}
|
/>
|
||||||
/>
|
|
||||||
</IntlProvider>
|
|
||||||
);
|
);
|
||||||
return new Promise(resolve => ReactDOM.render(elem, domEl, resolve));
|
return new Promise(resolve => ReactDOM.render(elem, domEl, resolve));
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -91,8 +91,12 @@ function doUnload()
|
||||||
}
|
}
|
||||||
|
|
||||||
var onCollectionSelected = async function () {
|
var onCollectionSelected = async function () {
|
||||||
if (!collectionsView.selection.count) return;
|
|
||||||
var collectionTreeRow = collectionsView.getRow(collectionsView.selection.focused);
|
var collectionTreeRow = collectionsView.getRow(collectionsView.selection.focused);
|
||||||
|
if (!collectionsView.selection.count) return;
|
||||||
|
// Collection not changed
|
||||||
|
if (itemsView && itemsView.collectionTreeRow && itemsView.collectionTreeRow.id == collectionTreeRow.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
collectionTreeRow.setSearch('');
|
collectionTreeRow.setSearch('');
|
||||||
Zotero.Prefs.set('lastViewedFolder', collectionTreeRow.id);
|
Zotero.Prefs.set('lastViewedFolder', collectionTreeRow.id);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -160,8 +160,7 @@ const ZoteroStandalone = new function() {
|
||||||
let item = Zotero.Items.get(reader.itemID);
|
let item = Zotero.Items.get(reader.itemID);
|
||||||
let library = Zotero.Libraries.get(item.libraryID);
|
let library = Zotero.Libraries.get(item.libraryID);
|
||||||
if (item
|
if (item
|
||||||
// Don't allow annotation transfer in group libraries
|
&& library.filesEditable
|
||||||
&& library.libraryType == 'user'
|
|
||||||
&& library.editable
|
&& library.editable
|
||||||
&& !(item.deleted || item.parentItem && item.parentItem.deleted)) {
|
&& !(item.deleted || item.parentItem && item.parentItem.deleted)) {
|
||||||
let annotations = item.getAnnotations();
|
let annotations = item.getAnnotations();
|
||||||
|
|
@ -192,12 +191,9 @@ const ZoteroStandalone = new function() {
|
||||||
if (numFiles) {
|
if (numFiles) {
|
||||||
menuitem.hidden = false;
|
menuitem.hidden = false;
|
||||||
sep.hidden = false;
|
sep.hidden = false;
|
||||||
if (numFiles == 1) {
|
menuitem.label = Zotero.getString(
|
||||||
menuitem.label = 'Export PDF…';
|
'pane.items.menu.exportPDF' + (numFiles == 1 ? '' : '.multiple')
|
||||||
}
|
);
|
||||||
else {
|
|
||||||
menuitem.label = 'Export PDFs…';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
menuitem.hidden = true;
|
menuitem.hidden = true;
|
||||||
|
|
@ -408,6 +404,8 @@ const ZoteroStandalone = new function() {
|
||||||
this.updateMenuItemCheckmark('view-menuitem-zoom-auto', reader.isZoomAutoActive());
|
this.updateMenuItemCheckmark('view-menuitem-zoom-auto', reader.isZoomAutoActive());
|
||||||
this.updateMenuItemCheckmark('view-menuitem-zoom-page-width', reader.isZoomPageWidthActive());
|
this.updateMenuItemCheckmark('view-menuitem-zoom-page-width', reader.isZoomPageWidthActive());
|
||||||
this.updateMenuItemCheckmark('view-menuitem-zoom-page-height', reader.isZoomPageHeightActive());
|
this.updateMenuItemCheckmark('view-menuitem-zoom-page-height', reader.isZoomPageHeightActive());
|
||||||
|
this.updateMenuItemCheckmark('view-menuitem-split-vertically', reader.isSplitVerticallyActive());
|
||||||
|
this.updateMenuItemCheckmark('view-menuitem-split-horizontally', reader.isSplitHorizontallyActive());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Layout mode
|
// Layout mode
|
||||||
|
|
|
||||||
|
|
@ -232,6 +232,25 @@
|
||||||
label="&bidiSwitchTextDirectionItem.label;"
|
label="&bidiSwitchTextDirectionItem.label;"
|
||||||
accesskey="&bidiSwitchTextDirectionItem.accesskey;"
|
accesskey="&bidiSwitchTextDirectionItem.accesskey;"
|
||||||
hidden="true"/>
|
hidden="true"/>
|
||||||
|
<menuseparator class="menu-type-reader"/>
|
||||||
|
<menuitem
|
||||||
|
id="edit-menuitem-rotate-left"
|
||||||
|
class="menu-type-reader"
|
||||||
|
label="&zotero.pdfReader.rotatePageLeft;"
|
||||||
|
oncommand="ZoteroStandalone.onReaderCmd('rotateLeft')"
|
||||||
|
/>
|
||||||
|
<menuitem
|
||||||
|
id="edit-menuitem-rotate-right"
|
||||||
|
class="menu-type-reader"
|
||||||
|
label="&zotero.pdfReader.rotatePageRight;"
|
||||||
|
oncommand="ZoteroStandalone.onReaderCmd('rotateRight')"
|
||||||
|
/>
|
||||||
|
<!--<menuitem
|
||||||
|
id="edit-menuitem-rotate-180"
|
||||||
|
class="menu-type-reader"
|
||||||
|
label="&zotero.pdfReader.rotatePage180;"
|
||||||
|
oncommand="ZoteroStandalone.onReaderCmd('rotate180')"
|
||||||
|
/>-->
|
||||||
</menupopup>
|
</menupopup>
|
||||||
</menu>
|
</menu>
|
||||||
|
|
||||||
|
|
@ -240,17 +259,6 @@
|
||||||
accesskey="&viewMenu.accesskey;"
|
accesskey="&viewMenu.accesskey;"
|
||||||
onpopupshowing="ZoteroStandalone.onViewMenuOpen()">
|
onpopupshowing="ZoteroStandalone.onViewMenuOpen()">
|
||||||
<menupopup id="menu_viewPopup">
|
<menupopup id="menu_viewPopup">
|
||||||
<menuitem
|
|
||||||
class="menu-type-reader"
|
|
||||||
label="&zotero.pdfReader.rotateClockwise;"
|
|
||||||
oncommand="ZoteroStandalone.onReaderCmd('rotatecw')"
|
|
||||||
/>
|
|
||||||
<menuitem
|
|
||||||
class="menu-type-reader"
|
|
||||||
label="&zotero.pdfReader.rotateCounterclockwise;"
|
|
||||||
oncommand="ZoteroStandalone.onReaderCmd('rotateccw')"
|
|
||||||
/>
|
|
||||||
<menuseparator class="menu-type-reader"/>
|
|
||||||
<menuitem
|
<menuitem
|
||||||
id="view-menuitem-hand-tool"
|
id="view-menuitem-hand-tool"
|
||||||
class="menu-type-reader"
|
class="menu-type-reader"
|
||||||
|
|
@ -337,6 +345,21 @@
|
||||||
oncommand="ZoteroStandalone.onReaderCmd('zoomPageHeight')"
|
oncommand="ZoteroStandalone.onReaderCmd('zoomPageHeight')"
|
||||||
/>
|
/>
|
||||||
<menuseparator class="menu-type-reader"/>
|
<menuseparator class="menu-type-reader"/>
|
||||||
|
<menuitem
|
||||||
|
id="view-menuitem-split-horizontally"
|
||||||
|
class="menu-type-reader"
|
||||||
|
type="checkbox"
|
||||||
|
label="&zotero.pdfReader.splitHorizontally;"
|
||||||
|
oncommand="ZoteroStandalone.onReaderCmd('splitHorizontally')"
|
||||||
|
/>
|
||||||
|
<menuitem
|
||||||
|
id="view-menuitem-split-vertically"
|
||||||
|
class="menu-type-reader"
|
||||||
|
type="checkbox"
|
||||||
|
label="&zotero.pdfReader.splitVertically;"
|
||||||
|
oncommand="ZoteroStandalone.onReaderCmd('splitVertically')"
|
||||||
|
/>
|
||||||
|
<menuseparator class="menu-type-reader"/>
|
||||||
|
|
||||||
<menu id="layout-menu" label="&layout.label;">
|
<menu id="layout-menu" label="&layout.label;">
|
||||||
<menupopup oncommand="ZoteroStandalone.onViewMenuItemClick(event)">
|
<menupopup oncommand="ZoteroStandalone.onViewMenuItemClick(event)">
|
||||||
|
|
@ -390,6 +413,8 @@
|
||||||
<menuitem label="12" type="checkbox"/>
|
<menuitem label="12" type="checkbox"/>
|
||||||
<menuitem label="13" type="checkbox"/>
|
<menuitem label="13" type="checkbox"/>
|
||||||
<menuitem label="14" type="checkbox"/>
|
<menuitem label="14" type="checkbox"/>
|
||||||
|
<menuitem label="15" type="checkbox"/>
|
||||||
|
<menuitem label="16" type="checkbox"/>
|
||||||
<menuitem label="18" type="checkbox"/>
|
<menuitem label="18" type="checkbox"/>
|
||||||
<menuitem label="24" type="checkbox"/>
|
<menuitem label="24" type="checkbox"/>
|
||||||
<menuitem label="36" type="checkbox"/>
|
<menuitem label="36" type="checkbox"/>
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,10 @@ var React = require('react');
|
||||||
var ReactDOM = require('react-dom');
|
var ReactDOM = require('react-dom');
|
||||||
import TabBar from 'components/tabBar';
|
import TabBar from 'components/tabBar';
|
||||||
|
|
||||||
const MAX_LOADED_TABS = 5;
|
// Reduce loaded tabs limit if the system has 8 GB or less memory, or it's running
|
||||||
|
// on Windows which means it doesn't support more than 3 GB
|
||||||
|
// TODO: Revise this after upgrading to Zotero 7
|
||||||
|
const MAX_LOADED_TABS = Services.sysinfo.getProperty("memsize") / 1024 / 1024 / 1024 <= 8 || Zotero.isWin ? 3 : 5;
|
||||||
const UNLOAD_UNUSED_AFTER = 86400; // 24h
|
const UNLOAD_UNUSED_AFTER = 86400; // 24h
|
||||||
|
|
||||||
var Zotero_Tabs = new function () {
|
var Zotero_Tabs = new function () {
|
||||||
|
|
@ -84,6 +87,17 @@ var Zotero_Tabs = new function () {
|
||||||
window.Zotero_Tooltip.stop();
|
window.Zotero_Tooltip.stop();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.getTabIDByItemID = function (itemID) {
|
||||||
|
let tab = this._tabs.find(tab => tab.data && tab.data.itemID === itemID);
|
||||||
|
return tab && tab.id;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setSecondViewState = function (tabID, state) {
|
||||||
|
let { tab } = this._getTab(tabID);
|
||||||
|
tab.data.secondViewState = state;
|
||||||
|
Zotero.Session.debounceSave();
|
||||||
|
};
|
||||||
|
|
||||||
this.init = function () {
|
this.init = function () {
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<TabBar
|
<TabBar
|
||||||
|
|
@ -128,12 +142,24 @@ var Zotero_Tabs = new function () {
|
||||||
}
|
}
|
||||||
else if (tab.type === 'reader') {
|
else if (tab.type === 'reader') {
|
||||||
if (Zotero.Items.exists(tab.data.itemID)) {
|
if (Zotero.Items.exists(tab.data.itemID)) {
|
||||||
this.add({
|
if (tab.selected) {
|
||||||
type: 'reader-unloaded',
|
Zotero.Reader.open(tab.data.itemID,
|
||||||
title: tab.title,
|
null,
|
||||||
index: i,
|
{
|
||||||
data: tab.data
|
title: tab.title,
|
||||||
});
|
openInBackground: !tab.selected,
|
||||||
|
secondViewState: tab.data.secondViewState
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.add({
|
||||||
|
type: 'reader-unloaded',
|
||||||
|
title: tab.title,
|
||||||
|
index: i,
|
||||||
|
data: tab.data
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +178,7 @@ var Zotero_Tabs = new function () {
|
||||||
* @param {Function} onClose
|
* @param {Function} onClose
|
||||||
* @return {{ id: string, container: XULElement}} id - tab id, container - a new tab container created in the deck
|
* @return {{ id: string, container: XULElement}} id - tab id, container - a new tab container created in the deck
|
||||||
*/
|
*/
|
||||||
this.add = function ({ type, data, title, index, select, onClose }) {
|
this.add = function ({ id, type, data, title, index, select, onClose }) {
|
||||||
if (typeof type != 'string') {
|
if (typeof type != 'string') {
|
||||||
}
|
}
|
||||||
if (typeof title != 'string') {
|
if (typeof title != 'string') {
|
||||||
|
|
@ -164,7 +190,7 @@ var Zotero_Tabs = new function () {
|
||||||
if (onClose !== undefined && typeof onClose != 'function') {
|
if (onClose !== undefined && typeof onClose != 'function') {
|
||||||
throw new Error(`'onClose' should be a function (was ${typeof onClose})`);
|
throw new Error(`'onClose' should be a function (was ${typeof onClose})`);
|
||||||
}
|
}
|
||||||
var id = 'tab-' + Zotero.Utilities.randomString();
|
id = id || 'tab-' + Zotero.Utilities.randomString();
|
||||||
var container = document.createElement('vbox');
|
var container = document.createElement('vbox');
|
||||||
container.id = id;
|
container.id = id;
|
||||||
this.deck.appendChild(container);
|
this.deck.appendChild(container);
|
||||||
|
|
@ -251,7 +277,7 @@ var Zotero_Tabs = new function () {
|
||||||
closedIDs.push(id);
|
closedIDs.push(id);
|
||||||
}
|
}
|
||||||
this._history.push(historyEntry);
|
this._history.push(historyEntry);
|
||||||
Zotero.Notifier.trigger('close', 'tab', [closedIDs]);
|
Zotero.Notifier.trigger('close', 'tab', [closedIDs], true);
|
||||||
this._update();
|
this._update();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -321,7 +347,7 @@ var Zotero_Tabs = new function () {
|
||||||
* @param {String} id
|
* @param {String} id
|
||||||
* @param {Boolean} reopening
|
* @param {Boolean} reopening
|
||||||
*/
|
*/
|
||||||
this.select = function (id, reopening) {
|
this.select = function (id, reopening, options) {
|
||||||
var { tab, tabIndex } = this._getTab(id);
|
var { tab, tabIndex } = this._getTab(id);
|
||||||
if (!tab || tab.id === this._selectedID) {
|
if (!tab || tab.id === this._selectedID) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -332,10 +358,12 @@ var Zotero_Tabs = new function () {
|
||||||
}
|
}
|
||||||
if (tab.type === 'reader-unloaded') {
|
if (tab.type === 'reader-unloaded') {
|
||||||
this.close(tab.id);
|
this.close(tab.id);
|
||||||
Zotero.Reader.open(tab.data.itemID, null, {
|
Zotero.Reader.open(tab.data.itemID, options && options.location, {
|
||||||
|
tabID: tab.id,
|
||||||
title: tab.title,
|
title: tab.title,
|
||||||
tabIndex,
|
tabIndex,
|
||||||
allowDuplicate: true
|
allowDuplicate: true,
|
||||||
|
secondViewState: tab.data.secondViewState
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -385,6 +413,7 @@ var Zotero_Tabs = new function () {
|
||||||
}
|
}
|
||||||
this.close(tab.id);
|
this.close(tab.id);
|
||||||
this.add({
|
this.add({
|
||||||
|
id: tab.id,
|
||||||
type: 'reader-unloaded',
|
type: 'reader-unloaded',
|
||||||
title: tab.title,
|
title: tab.title,
|
||||||
index: tabIndex,
|
index: tabIndex,
|
||||||
|
|
@ -440,7 +469,7 @@ var Zotero_Tabs = new function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
this._openMenu = function (x, y, id) {
|
this._openMenu = function (x, y, id) {
|
||||||
var { tabIndex } = this._getTab(id);
|
var { tab, tabIndex } = this._getTab(id);
|
||||||
window.Zotero_Tooltip.stop();
|
window.Zotero_Tooltip.stop();
|
||||||
let menuitem;
|
let menuitem;
|
||||||
let popup = document.createElement('menupopup');
|
let popup = document.createElement('menupopup');
|
||||||
|
|
@ -497,7 +526,8 @@ var Zotero_Tabs = new function () {
|
||||||
var reader = Zotero.Reader.getByTabID(id);
|
var reader = Zotero.Reader.getByTabID(id);
|
||||||
if (reader) {
|
if (reader) {
|
||||||
this.close(id);
|
this.close(id);
|
||||||
Zotero.Reader.open(reader.itemID, null, { openInWindow: true });
|
let { secondViewState } = tab.data;
|
||||||
|
Zotero.Reader.open(reader.itemID, null, { openInWindow: true, secondViewState });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
menupopup.appendChild(menuitem);
|
menupopup.appendChild(menuitem);
|
||||||
|
|
@ -505,11 +535,10 @@ var Zotero_Tabs = new function () {
|
||||||
menuitem = document.createElement('menuitem');
|
menuitem = document.createElement('menuitem');
|
||||||
menuitem.setAttribute('label', Zotero.getString('tabs.duplicate'));
|
menuitem.setAttribute('label', Zotero.getString('tabs.duplicate'));
|
||||||
menuitem.addEventListener('command', () => {
|
menuitem.addEventListener('command', () => {
|
||||||
var { tab, tabIndex } = this._getTab(id);
|
|
||||||
if (tab.data.itemID) {
|
if (tab.data.itemID) {
|
||||||
tabIndex++;
|
tabIndex++;
|
||||||
Zotero.Reader.open(tab.data.itemID, null, { tabIndex, allowDuplicate: true });
|
let { secondViewState } = tab.data;
|
||||||
|
Zotero.Reader.open(tab.data.itemID, null, { tabIndex, allowDuplicate: true, secondViewState });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
popup.appendChild(menuitem);
|
popup.appendChild(menuitem);
|
||||||
|
|
|
||||||
|
|
@ -77,11 +77,12 @@ import FilePicker from 'zotero/modules/filePicker';
|
||||||
resultElem.innerHTML = '<p>Failed.</p>';
|
resultElem.innerHTML = '<p>Failed.</p>';
|
||||||
} else {
|
} else {
|
||||||
let schemaFile = Zotero.File.pathToFile(fp.file);
|
let schemaFile = Zotero.File.pathToFile(fp.file);
|
||||||
schemaFile.append("connectorTypeSchemaData.js");
|
schemaFile.append("zoteroTypeSchemaData.js");
|
||||||
await Zotero.File.putContentsAsync(
|
await Zotero.File.putContentsAsync(
|
||||||
schemaFile,
|
schemaFile,
|
||||||
`Zotero.Connector_Types.schema = ${JSON.stringify(schema)}`
|
`var ZOTERO_TYPE_SCHEMA = ${JSON.stringify(schema)};\n\n`
|
||||||
|
+ "if (typeof module !== 'undefined') {\n\tmodule.exports = ZOTERO_TYPE_SCHEMA;\n}\n"
|
||||||
);
|
);
|
||||||
resultElem.innerHTML = `<p>Wrote ${schemaFile} successfully.</p>`;
|
resultElem.innerHTML = `<p>Wrote ${schemaFile.path} successfully.</p>`;
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
@ -59,10 +59,8 @@ var Zotero_CSL_Editor = new function() {
|
||||||
|
|
||||||
var pageList = document.getElementById('zotero-csl-page-type');
|
var pageList = document.getElementById('zotero-csl-page-type');
|
||||||
var locators = Zotero.Cite.labels;
|
var locators = Zotero.Cite.labels;
|
||||||
for (let type of locators) {
|
for (let locator of locators) {
|
||||||
var locator = type;
|
pageList.appendItem(Zotero.Cite.getLocatorString(locator), locator);
|
||||||
locator = Zotero.getString('citation.locator.'+locator.replace(/\s/g,''));
|
|
||||||
pageList.appendItem(locator, type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pageList.selectedIndex = 0;
|
pageList.selectedIndex = 0;
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,10 @@ Zotero.Annotations = new function () {
|
||||||
Zotero.defineProperty(this, 'ANNOTATION_TYPE_NOTE', { value: 2 });
|
Zotero.defineProperty(this, 'ANNOTATION_TYPE_NOTE', { value: 2 });
|
||||||
Zotero.defineProperty(this, 'ANNOTATION_TYPE_IMAGE', { value: 3 });
|
Zotero.defineProperty(this, 'ANNOTATION_TYPE_IMAGE', { value: 3 });
|
||||||
Zotero.defineProperty(this, 'ANNOTATION_TYPE_INK', { value: 4 });
|
Zotero.defineProperty(this, 'ANNOTATION_TYPE_INK', { value: 4 });
|
||||||
|
Zotero.defineProperty(this, 'ANNOTATION_TYPE_UNDERLINE', { value: 5 });
|
||||||
|
Zotero.defineProperty(this, 'ANNOTATION_TYPE_TEXT', { value: 6 });
|
||||||
|
|
||||||
|
Zotero.defineProperty(this, 'DEFAULT_COLOR', { value: '#ffd400' });
|
||||||
|
|
||||||
Zotero.defineProperty(this, 'PROPS', {
|
Zotero.defineProperty(this, 'PROPS', {
|
||||||
value: ['type', 'authorName', 'text', 'comment', 'color', 'pageLabel', 'sortIndex', 'position'],
|
value: ['type', 'authorName', 'text', 'comment', 'color', 'pageLabel', 'sortIndex', 'position'],
|
||||||
|
|
@ -135,7 +139,7 @@ Zotero.Annotations = new function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
o.readOnly = o.isExternal || !isAuthor;
|
o.readOnly = o.isExternal || !isAuthor;
|
||||||
if (o.type == 'highlight') {
|
if (['highlight', 'underline'].includes(o.type)) {
|
||||||
o.text = item.annotationText;
|
o.text = item.annotationText;
|
||||||
}
|
}
|
||||||
else if (['image', 'ink'].includes(o.type)) {
|
else if (['image', 'ink'].includes(o.type)) {
|
||||||
|
|
@ -177,7 +181,7 @@ Zotero.Annotations = new function () {
|
||||||
o.tags = tags;
|
o.tags = tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
o.dateModified = item.dateModified;
|
o.dateModified = Zotero.Date.sqlToISO8601(item.dateModified);
|
||||||
return o;
|
return o;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -211,7 +215,7 @@ Zotero.Annotations = new function () {
|
||||||
item._requireData('annotationDeferred');
|
item._requireData('annotationDeferred');
|
||||||
item.annotationType = json.type;
|
item.annotationType = json.type;
|
||||||
item.annotationAuthorName = json.authorName || '';
|
item.annotationAuthorName = json.authorName || '';
|
||||||
if (json.type == 'highlight') {
|
if (['highlight', 'underline'].includes(json.type)) {
|
||||||
item.annotationText = json.text;
|
item.annotationText = json.text;
|
||||||
}
|
}
|
||||||
item.annotationIsExternal = !!json.isExternal;
|
item.annotationIsExternal = !!json.isExternal;
|
||||||
|
|
@ -231,6 +235,10 @@ Zotero.Annotations = new function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't try to select annotation, which would clear an active quick search (at least
|
||||||
|
// until annotations are visible in the items list)
|
||||||
|
saveOptions.skipSelect = true;
|
||||||
|
|
||||||
await item.saveTx(saveOptions);
|
await item.saveTx(saveOptions);
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
***** END LICENSE BLOCK *****
|
***** END LICENSE BLOCK *****
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Zotero.Attachments = new function(){
|
Zotero.Attachments = new function () {
|
||||||
// Keep in sync with Zotero.Schema.integrityCheck() and this.linkModeToName()
|
// Keep in sync with Zotero.Schema.integrityCheck() and this.linkModeToName()
|
||||||
this.LINK_MODE_IMPORTED_FILE = 0;
|
this.LINK_MODE_IMPORTED_FILE = 0;
|
||||||
this.LINK_MODE_IMPORTED_URL = 1;
|
this.LINK_MODE_IMPORTED_URL = 1;
|
||||||
|
|
@ -270,7 +270,7 @@ Zotero.Attachments = new function(){
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} options - 'file', 'url', 'title', 'contentType', 'charset', 'parentItemID', 'singleFile'
|
* @param {Object} options - 'file', 'url', 'title', 'contentType', 'charset', 'libraryID', 'parentItemID', 'singleFile'
|
||||||
* @param {Object} [options.saveOptions] - Options to pass to Zotero.Item::save()
|
* @param {Object} [options.saveOptions] - Options to pass to Zotero.Item::save()
|
||||||
* @return {Promise<Zotero.Item>}
|
* @return {Promise<Zotero.Item>}
|
||||||
*/
|
*/
|
||||||
|
|
@ -287,10 +287,19 @@ Zotero.Attachments = new function(){
|
||||||
var title = options.title;
|
var title = options.title;
|
||||||
var contentType = options.contentType;
|
var contentType = options.contentType;
|
||||||
var charset = options.charset;
|
var charset = options.charset;
|
||||||
|
var libraryID = options.libraryID;
|
||||||
var parentItemID = options.parentItemID;
|
var parentItemID = options.parentItemID;
|
||||||
var saveOptions = options.saveOptions;
|
var saveOptions = options.saveOptions;
|
||||||
|
|
||||||
if (!parentItemID) {
|
if (parentItemID) {
|
||||||
|
libraryID = Zotero.Items.getLibraryAndKeyFromID(parentItemID).libraryID;
|
||||||
|
}
|
||||||
|
else if (contentType == 'text/html') {
|
||||||
|
throw new Error("parentItemID not provided");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Webpage snapshots must have parent items
|
||||||
|
if (!parentItemID && contentType == 'text/html') {
|
||||||
throw new Error("parentItemID not provided");
|
throw new Error("parentItemID not provided");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -299,8 +308,9 @@ Zotero.Attachments = new function(){
|
||||||
yield Zotero.DB.executeTransaction(function* () {
|
yield Zotero.DB.executeTransaction(function* () {
|
||||||
// Create a new attachment
|
// Create a new attachment
|
||||||
attachmentItem = new Zotero.Item('attachment');
|
attachmentItem = new Zotero.Item('attachment');
|
||||||
let {libraryID, key: parentKey} = Zotero.Items.getLibraryAndKeyFromID(parentItemID);
|
if (libraryID) {
|
||||||
attachmentItem.libraryID = libraryID;
|
attachmentItem.libraryID = libraryID;
|
||||||
|
}
|
||||||
attachmentItem.setField('title', title);
|
attachmentItem.setField('title', title);
|
||||||
attachmentItem.setField('url', url);
|
attachmentItem.setField('url', url);
|
||||||
attachmentItem.parentID = parentItemID;
|
attachmentItem.parentID = parentItemID;
|
||||||
|
|
@ -563,7 +573,8 @@ Zotero.Attachments = new function(){
|
||||||
reject(e);
|
reject(e);
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
cookieSandbox
|
cookieSandbox,
|
||||||
|
{ allowImages: true }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -1143,67 +1154,82 @@ Zotero.Attachments = new function(){
|
||||||
*/
|
*/
|
||||||
this.downloadPDFViaBrowser = async function (url, path, options = {}) {
|
this.downloadPDFViaBrowser = async function (url, path, options = {}) {
|
||||||
Zotero.debug(`downloadPDFViaBrowser: Downloading file via browser from ${url}`);
|
Zotero.debug(`downloadPDFViaBrowser: Downloading file via browser from ${url}`);
|
||||||
const onLoadTimeout = 1e3;
|
const onLoadTimeout = Zotero.Prefs.get('downloadPDFViaBrowser.onLoadTimeout');
|
||||||
// Technically this is not a download, but the full operation timeout
|
// Technically this is not a download, but the full operation timeout
|
||||||
const downloadTimeout = 60e3;
|
const downloadTimeout = Zotero.Prefs.get('downloadPDFViaBrowser.downloadTimeout');
|
||||||
let channelBrowser, hiddenBrowser;
|
let channelBrowser, hiddenBrowser;
|
||||||
let hiddenBrowserPDFFoundDeferred = Zotero.Promise.defer();
|
let hiddenBrowserPDFFoundDeferred = Zotero.Promise.defer();
|
||||||
|
|
||||||
var pdfMIMETypeHandler = async (blob, name, _, channel) => {
|
let isOurPDF = false;
|
||||||
Zotero.debug(`downloadPDFViaBrowser: Sniffing a PDF loaded at ${name}`);
|
var pdfMIMETypeHandler = {
|
||||||
|
onStartRequest: function (name, _, channel) {
|
||||||
let isOurPDF = false;
|
Zotero.debug(`downloadPDFViaBrowser: Sniffing a PDF loaded at ${name}`);
|
||||||
// try the browser
|
// try the browser
|
||||||
try {
|
|
||||||
channelBrowser = channel.notificationCallbacks.getInterface(Ci.nsIWebNavigation)
|
|
||||||
.QueryInterface(Ci.nsIDocShell).chromeEventHandler;
|
|
||||||
}
|
|
||||||
catch (e) {}
|
|
||||||
if (channelBrowser) {
|
|
||||||
isOurPDF = hiddenBrowser === channelBrowser;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// try the document for the load group
|
|
||||||
try {
|
try {
|
||||||
channelBrowser = channel.loadGroup.notificationCallbacks.getInterface(Ci.nsIWebNavigation)
|
channelBrowser = channel.notificationCallbacks.getInterface(Ci.nsIWebNavigation)
|
||||||
.QueryInterface(Ci.nsIDocShell).chromeEventHandler;
|
.QueryInterface(Ci.nsIDocShell).chromeEventHandler;
|
||||||
}
|
}
|
||||||
catch(e) {}
|
catch (e) {}
|
||||||
if (channelBrowser) {
|
if (channelBrowser) {
|
||||||
isOurPDF = hiddenBrowser === channelBrowser;
|
isOurPDF = hiddenBrowser === channelBrowser;
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
|
// try the document for the load group
|
||||||
if (isOurPDF) {
|
try {
|
||||||
Zotero.debug(`downloadPDFViaBrowser: Found our PDF at ${name}`);
|
channelBrowser = channel.loadGroup.notificationCallbacks.getInterface(Ci.nsIWebNavigation)
|
||||||
await Zotero.File.putContentsAsync(path, blob);
|
.QueryInterface(Ci.nsIDocShell).chromeEventHandler;
|
||||||
hiddenBrowserPDFFoundDeferred.resolve();
|
}
|
||||||
return true;
|
catch(e) {}
|
||||||
}
|
if (channelBrowser) {
|
||||||
else {
|
isOurPDF = hiddenBrowser === channelBrowser;
|
||||||
Zotero.debug(`downloadPDFViaBrowser: Not our PDF at ${name}`);
|
}
|
||||||
return false;
|
}
|
||||||
|
},
|
||||||
|
onContent: async (blob, name, _, channel) => {
|
||||||
|
if (isOurPDF) {
|
||||||
|
Zotero.debug(`downloadPDFViaBrowser: Found our PDF at ${name}`);
|
||||||
|
await Zotero.File.putContentsAsync(path, blob);
|
||||||
|
hiddenBrowserPDFFoundDeferred.resolve();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Zotero.debug(`downloadPDFViaBrowser: Not our PDF at ${name}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
Zotero.MIMETypeHandler.addHandler("application/pdf", pdfMIMETypeHandler, true);
|
Zotero.MIMETypeHandler.addHandlers("application/pdf", pdfMIMETypeHandler, true);
|
||||||
let noop = () => 0;
|
function noop() {};
|
||||||
hiddenBrowser = Zotero.HTTP.loadDocuments([url], noop, noop, noop, true, options.cookieSandbox);
|
hiddenBrowser = Zotero.HTTP.loadDocuments([url], noop, noop, noop, true, options.cookieSandbox);
|
||||||
let onLoadTimeoutDeferred = Zotero.Promise.defer();
|
let onLoadTimeoutDeferred = Zotero.Promise.defer();
|
||||||
hiddenBrowser.addEventListener("load", async function onLoad() {
|
let currentUrl = "";
|
||||||
Zotero.debug(`downloadPDFViaBrowser: Page with potential JS redirect loaded, giving it ${onLoadTimeout}ms to process`);
|
hiddenBrowser.addProgressListener({
|
||||||
hiddenBrowser.addEventListener("unload", () => {
|
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIWebProgressListener,
|
||||||
Zotero.debug(`downloadPDFViaBrowser: A JS redirect occurred, short timeout cancelled`);
|
Components.interfaces.nsISupportsWeakReference]),
|
||||||
hiddenBrowser.removeEventListener('load', onLoad);
|
onProgressChange: noop,
|
||||||
});
|
onStateChange: noop,
|
||||||
await Zotero.Promise.delay(onLoadTimeout);
|
onStatusChange: noop,
|
||||||
onLoadTimeoutDeferred.reject(`Loading PDF via browser timed out on the JS challenge page after ${onLoadTimeout}ms`);
|
onSecurityChange: noop,
|
||||||
}, true);
|
async onLocationChange() {
|
||||||
|
let url = hiddenBrowser.contentDocument.location.href;
|
||||||
|
if (currentUrl) {
|
||||||
|
Zotero.debug(`downloadPDFViaBrowser: A JS redirect occurred to ${hiddenBrowser.contentDocument.location.href}`);
|
||||||
|
}
|
||||||
|
currentUrl = url;
|
||||||
|
Zotero.debug(`downloadPDFViaBrowser: Page with potential JS redirect loaded, giving it ${onLoadTimeout}ms to process`);
|
||||||
|
await Zotero.Promise.delay(onLoadTimeout);
|
||||||
|
// If URL changed that means we got redirected and the onLoadTimeout needs to restart
|
||||||
|
if (currentUrl === url && !isOurPDF) {
|
||||||
|
onLoadTimeoutDeferred.reject(new Error(`downloadPDFViaBrowser: Loading PDF via browser timed out on the JS challenge page after ${onLoadTimeout}ms`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
await Zotero.Promise.race([
|
await Zotero.Promise.race([
|
||||||
onLoadTimeoutDeferred.promise,
|
onLoadTimeoutDeferred.promise,
|
||||||
Zotero.Promise.delay(downloadTimeout).then(() => {
|
Zotero.Promise.delay(downloadTimeout).then(() => {
|
||||||
if (!hiddenBrowserPDFFoundDeferred.promise.isResolved()) {
|
if (!isOurPDF) {
|
||||||
throw new Error(`Loading PDF via browser timed out after ${downloadTimeout}ms`);
|
throw new Error(`downloadPDFViaBrowser: Loading PDF via browser timed out after ${downloadTimeout}ms`);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
hiddenBrowserPDFFoundDeferred.promise
|
hiddenBrowserPDFFoundDeferred.promise
|
||||||
|
|
@ -1213,13 +1239,13 @@ Zotero.Attachments = new function(){
|
||||||
try {
|
try {
|
||||||
await OS.File.remove(path, { ignoreAbsent: true });
|
await OS.File.remove(path, { ignoreAbsent: true });
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (err) {
|
||||||
Zotero.logError(e, 1);
|
Zotero.logError(err);
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
Zotero.MIMETypeHandler.removeHandler('application/pdf', pdfMIMETypeHandler);
|
Zotero.MIMETypeHandler.removeHandlers('application/pdf', pdfMIMETypeHandler);
|
||||||
if (hiddenBrowser) {
|
if (hiddenBrowser) {
|
||||||
Zotero.Browser.deleteHiddenBrowser(hiddenBrowser);
|
Zotero.Browser.deleteHiddenBrowser(hiddenBrowser);
|
||||||
}
|
}
|
||||||
|
|
@ -1259,13 +1285,14 @@ Zotero.Attachments = new function(){
|
||||||
|
|
||||||
this.canFindPDFForItem = function (item) {
|
this.canFindPDFForItem = function (item) {
|
||||||
return item.isRegularItem()
|
return item.isRegularItem()
|
||||||
|
&& !item.isFeedItem
|
||||||
&& (!!item.getField('DOI') || !!item.getField('url') || !!item.getExtraField('DOI'))
|
&& (!!item.getField('DOI') || !!item.getField('url') || !!item.getExtraField('DOI'))
|
||||||
&& item.numPDFAttachments() == 0;
|
&& item.numPDFAttachments() == 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Look for an available PDF for an item and add it as an attachment
|
* Get the PDF resolvers that can be used for a given item based on the available fields
|
||||||
*
|
*
|
||||||
* @param {Zotero.Item} item
|
* @param {Zotero.Item} item
|
||||||
* @param {String[]} [methods=['doi', 'url', 'oa', 'custom']]
|
* @param {String[]} [methods=['doi', 'url', 'oa', 'custom']]
|
||||||
|
|
@ -1405,6 +1432,12 @@ Zotero.Attachments = new function(){
|
||||||
? elem.getAttribute(attribute)
|
? elem.getAttribute(attribute)
|
||||||
: elem.textContent;
|
: elem.textContent;
|
||||||
if (!val) return [];
|
if (!val) return [];
|
||||||
|
|
||||||
|
// Handle relative paths
|
||||||
|
val = Services.io.newURI(
|
||||||
|
val, null, Services.io.newURI(url)
|
||||||
|
).spec;
|
||||||
|
|
||||||
return [{
|
return [{
|
||||||
accessMethod: name,
|
accessMethod: name,
|
||||||
url: val,
|
url: val,
|
||||||
|
|
@ -2011,7 +2044,7 @@ Zotero.Attachments = new function(){
|
||||||
addTriedURL(url);
|
addTriedURL(url);
|
||||||
// Backoff loop
|
// Backoff loop
|
||||||
let tries = 3;
|
let tries = 3;
|
||||||
while (tries-- >= 0) {
|
while (tries-- > 0) {
|
||||||
try {
|
try {
|
||||||
await beforeRequest(url);
|
await beforeRequest(url);
|
||||||
await this.downloadFile(url, path, options);
|
await this.downloadFile(url, path, options);
|
||||||
|
|
@ -2043,14 +2076,22 @@ Zotero.Attachments = new function(){
|
||||||
let contentType;
|
let contentType;
|
||||||
let skip = false;
|
let skip = false;
|
||||||
let domains = new Set();
|
let domains = new Set();
|
||||||
|
let redirectLimit = 10;
|
||||||
|
let redirectURLTries = new Map();
|
||||||
while (true) {
|
while (true) {
|
||||||
|
if (redirectLimit == 0) {
|
||||||
|
Zotero.debug("Too many redirects -- stopping");
|
||||||
|
skip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
let domain = urlToDomain(nextURL);
|
let domain = urlToDomain(nextURL);
|
||||||
let noDelay = domains.has(domain);
|
let noDelay = domains.has(domain);
|
||||||
domains.add(domain);
|
domains.add(domain);
|
||||||
|
|
||||||
// Backoff loop
|
// Backoff loop
|
||||||
let tries = 3;
|
let tries = 3;
|
||||||
while (tries-- >= 0) {
|
while (tries-- > 0) {
|
||||||
try {
|
try {
|
||||||
await beforeRequest(nextURL, noDelay);
|
await beforeRequest(nextURL, noDelay);
|
||||||
req = await Zotero.HTTP.request(
|
req = await Zotero.HTTP.request(
|
||||||
|
|
@ -2058,7 +2099,9 @@ Zotero.Attachments = new function(){
|
||||||
nextURL,
|
nextURL,
|
||||||
{
|
{
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
followRedirects: false
|
followRedirects: false,
|
||||||
|
// Use our own error handling
|
||||||
|
errorDelayMax: 0
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -2069,22 +2112,39 @@ Zotero.Attachments = new function(){
|
||||||
noDelay = false;
|
noDelay = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
afterRequest(nextURL);
|
afterRequest(nextURL);
|
||||||
|
if (!req) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if ([301, 302, 303, 307].includes(req.status)) {
|
if ([301, 302, 303, 307].includes(req.status)) {
|
||||||
let location = req.getResponseHeader('Location');
|
let location = req.getResponseHeader('Location');
|
||||||
if (!location) {
|
if (!location) {
|
||||||
throw new Error("Location header not provided");
|
throw new Error("Location header not provided");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let currentURL = nextURL;
|
||||||
|
|
||||||
nextURL = Services.io.newURI(nextURL, null, null).resolve(location);
|
nextURL = Services.io.newURI(nextURL, null, null).resolve(location);
|
||||||
if (isTriedURL(nextURL)) {
|
if (isTriedURL(nextURL)) {
|
||||||
Zotero.debug("Redirect URL has already been tried -- skipping");
|
Zotero.debug("Redirect URL has already been tried -- skipping");
|
||||||
skip = true;
|
skip = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep track of tries for each redirect URL, and stop if too many
|
||||||
|
let maxTriesPerRedirectURL = 2;
|
||||||
|
let tries = (redirectURLTries.get(currentURL) || 0) + 1;
|
||||||
|
if (tries > maxTriesPerRedirectURL) {
|
||||||
|
Zotero.debug(`Too many redirects to ${currentURL} -- stopping`);
|
||||||
|
skip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
redirectURLTries.set(currentURL, tries);
|
||||||
|
// And keep track of total redirects for this chain
|
||||||
|
redirectLimit--;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2131,7 +2191,7 @@ Zotero.Attachments = new function(){
|
||||||
}
|
}
|
||||||
|
|
||||||
// If DOI resolves directly to a PDF, save it to disk
|
// If DOI resolves directly to a PDF, save it to disk
|
||||||
if (contentType.startsWith('application/pdf')) {
|
if (contentType && contentType.startsWith('application/pdf')) {
|
||||||
Zotero.debug("URL resolves directly to PDF");
|
Zotero.debug("URL resolves directly to PDF");
|
||||||
await Zotero.File.putContentsAsync(path, blob);
|
await Zotero.File.putContentsAsync(path, blob);
|
||||||
await _enforcePDF(path);
|
await _enforcePDF(path);
|
||||||
|
|
@ -2147,7 +2207,7 @@ Zotero.Attachments = new function(){
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!url) {
|
if (!url) {
|
||||||
Zotero.debug(`No PDF found on ${responseURL}`);
|
Zotero.debug(`No PDF found on ${responseURL || pageURL}`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (isTriedURL(url)) {
|
if (isTriedURL(url)) {
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,110 @@ Zotero.Cite = {
|
||||||
/**
|
/**
|
||||||
* Locator labels
|
* Locator labels
|
||||||
*/
|
*/
|
||||||
"labels":["page", "book", "chapter", "column", "figure", "folio",
|
"labels": [
|
||||||
"issue", "line", "note", "opus", "paragraph", "part", "section", "sub verbo",
|
"act",
|
||||||
"volume", "verse"],
|
"appendix",
|
||||||
|
"article-locator",
|
||||||
|
"book",
|
||||||
|
"canon",
|
||||||
|
"chapter",
|
||||||
|
"column",
|
||||||
|
"elocation",
|
||||||
|
"equation",
|
||||||
|
"figure",
|
||||||
|
"folio",
|
||||||
|
"issue",
|
||||||
|
"line",
|
||||||
|
"note",
|
||||||
|
"opus",
|
||||||
|
"page",
|
||||||
|
"paragraph",
|
||||||
|
"part",
|
||||||
|
"rule",
|
||||||
|
"scene",
|
||||||
|
"section",
|
||||||
|
"sub-verbo",
|
||||||
|
"table",
|
||||||
|
//"timestamp",
|
||||||
|
"title-locator",
|
||||||
|
"verse",
|
||||||
|
"volume"
|
||||||
|
],
|
||||||
|
|
||||||
|
_locatorStrings: new Map(),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get localized string for locator
|
||||||
|
*
|
||||||
|
* @param {String} locator - Locator name (e.g., 'book')
|
||||||
|
* @return {String} - Localized string (e.g., 'Livre')
|
||||||
|
*/
|
||||||
|
getLocatorString: function (locator) {
|
||||||
|
// 'timestamp' not included in CSL locales
|
||||||
|
if (locator == 'timestamp') {
|
||||||
|
return Zotero.getString('citation.locator.timestamp');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the best CSL locale for the current Zotero locale
|
||||||
|
var cslLocale = Zotero.Utilities.Internal.resolveLocale(
|
||||||
|
Zotero.locale,
|
||||||
|
Object.keys(Zotero.Styles.locales)
|
||||||
|
);
|
||||||
|
|
||||||
|
// If locator strings are already cached for the current locale, use that
|
||||||
|
if (this._locatorStrings.has(cslLocale)) {
|
||||||
|
return this._locatorStrings.get(cslLocale).get(locator);
|
||||||
|
}
|
||||||
|
var map = new Map();
|
||||||
|
this._locatorStrings.set(cslLocale, map);
|
||||||
|
|
||||||
|
var localeXML = Zotero.Cite.Locale.get(cslLocale);
|
||||||
|
var parser;
|
||||||
|
if (Zotero.platformMajorVersion > 60) {
|
||||||
|
parser = new DOMParser();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
|
||||||
|
.createInstance(Components.interfaces.nsIDOMParser);
|
||||||
|
}
|
||||||
|
var doc = parser.parseFromString(localeXML, 'text/xml');
|
||||||
|
var englishDoc;
|
||||||
|
// Cache all locators for the current locale
|
||||||
|
for (let locator of this.labels) {
|
||||||
|
let elem = doc.querySelector(`term[name="${locator}"]:not([form="short"]) > single`);
|
||||||
|
if (!elem) {
|
||||||
|
// If locator not found, get from the U.S. English locale
|
||||||
|
if (cslLocale != 'en-US') {
|
||||||
|
Zotero.logError(`Locator '${locator}' not found in ${cslLocale} locale -- trying en-US`);
|
||||||
|
if (!englishDoc) {
|
||||||
|
englishDoc = parser.parseFromString(Zotero.Cite.Locale.get('en-US'), 'text/xml');
|
||||||
|
}
|
||||||
|
elem = englishDoc.querySelector(`term[name="${locator}"]:not([form="short"]) > single`);
|
||||||
|
if (!elem) {
|
||||||
|
Zotero.logError(`Locator '${locator}' not found in en-US locale -- using name`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Zotero.logError(`Locator '${locator}' not found in en-US locale -- using name`);
|
||||||
|
}
|
||||||
|
// If still not found, use the locator name directly
|
||||||
|
if (!elem) {
|
||||||
|
map.set(locator, Zotero.Utilities.capitalize(locator));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If <single> is empty, use the locator name directly
|
||||||
|
let str = elem.textContent;
|
||||||
|
if (!str) {
|
||||||
|
Zotero.logError(`Locator '${locator}' is empty in ${cslLocale} locale -- using name`);
|
||||||
|
map.set(locator, Zotero.Utilities.capitalize(locator));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
map.set(locator, Zotero.Utilities.capitalize(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
return map.get(locator);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove specified item IDs in-place from a citeproc-js bibliography object returned
|
* Remove specified item IDs in-place from a citeproc-js bibliography object returned
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,6 @@ var CSL = {
|
||||||
"cmt.": "comment",
|
"cmt.": "comment",
|
||||||
"dec.": "decision",
|
"dec.": "decision",
|
||||||
"dept.": "department",
|
"dept.": "department",
|
||||||
"div.": "division",
|
|
||||||
"ex.": "example",
|
"ex.": "example",
|
||||||
"fig.": "figure",
|
"fig.": "figure",
|
||||||
"fld.": "field",
|
"fld.": "field",
|
||||||
|
|
@ -170,7 +169,6 @@ var CSL = {
|
||||||
"comment": "cmt.",
|
"comment": "cmt.",
|
||||||
"decision": "dec.",
|
"decision": "dec.",
|
||||||
"department": "dept.",
|
"department": "dept.",
|
||||||
"division": "div.",
|
|
||||||
"example": "ex.",
|
"example": "ex.",
|
||||||
"figure": "fig.",
|
"figure": "fig.",
|
||||||
"field": "fld.",
|
"field": "fld.",
|
||||||
|
|
@ -219,7 +217,6 @@ var CSL = {
|
||||||
"cmt": "comment",
|
"cmt": "comment",
|
||||||
"dec": "decision",
|
"dec": "decision",
|
||||||
"dept": "department",
|
"dept": "department",
|
||||||
"div": "division",
|
|
||||||
"ex": "example",
|
"ex": "example",
|
||||||
"fig": "figure",
|
"fig": "figure",
|
||||||
"fld": "field",
|
"fld": "field",
|
||||||
|
|
@ -725,33 +722,61 @@ var CSL = {
|
||||||
|
|
||||||
NAME_VARIABLES: [
|
NAME_VARIABLES: [
|
||||||
"author",
|
"author",
|
||||||
|
"chair",
|
||||||
"collection-editor",
|
"collection-editor",
|
||||||
|
"compiler",
|
||||||
"composer",
|
"composer",
|
||||||
"container-author",
|
"container-author",
|
||||||
|
"contributor",
|
||||||
|
"curator",
|
||||||
"director",
|
"director",
|
||||||
"editor",
|
"editor",
|
||||||
|
"editor-translator",
|
||||||
"editorial-director",
|
"editorial-director",
|
||||||
|
"executive-producer",
|
||||||
|
"guest",
|
||||||
|
"host",
|
||||||
"illustrator",
|
"illustrator",
|
||||||
"interviewer",
|
"interviewer",
|
||||||
|
"narrator",
|
||||||
|
"organizer",
|
||||||
"original-author",
|
"original-author",
|
||||||
|
"performer",
|
||||||
|
"producer",
|
||||||
"recipient",
|
"recipient",
|
||||||
"reviewed-author",
|
"reviewed-author",
|
||||||
|
"script-writer",
|
||||||
|
"series-creator",
|
||||||
"translator",
|
"translator",
|
||||||
"commenter"
|
"commenter"
|
||||||
],
|
],
|
||||||
CREATORS: [
|
CREATORS: [
|
||||||
"author",
|
"author",
|
||||||
|
"chair",
|
||||||
"collection-editor",
|
"collection-editor",
|
||||||
|
"compiler",
|
||||||
"composer",
|
"composer",
|
||||||
"container-author",
|
"container-author",
|
||||||
|
"contributor",
|
||||||
|
"curator",
|
||||||
"director",
|
"director",
|
||||||
"editor",
|
"editor",
|
||||||
|
"editor-translator",
|
||||||
"editorial-director",
|
"editorial-director",
|
||||||
|
"executive-producer",
|
||||||
|
"guest",
|
||||||
|
"host",
|
||||||
"illustrator",
|
"illustrator",
|
||||||
"interviewer",
|
"interviewer",
|
||||||
|
"narrator",
|
||||||
|
"organizer",
|
||||||
"original-author",
|
"original-author",
|
||||||
|
"performer",
|
||||||
|
"producer",
|
||||||
"recipient",
|
"recipient",
|
||||||
"reviewed-author",
|
"reviewed-author",
|
||||||
|
"script-writer",
|
||||||
|
"series-creator",
|
||||||
"translator",
|
"translator",
|
||||||
"commenter"
|
"commenter"
|
||||||
],
|
],
|
||||||
|
|
@ -768,10 +793,13 @@ var CSL = {
|
||||||
"number",
|
"number",
|
||||||
"number-of-pages",
|
"number-of-pages",
|
||||||
"number-of-volumes",
|
"number-of-volumes",
|
||||||
|
"part-number",
|
||||||
|
"printing-number",
|
||||||
|
"section",
|
||||||
|
"supplement-number",
|
||||||
"version",
|
"version",
|
||||||
"volume",
|
"volume",
|
||||||
// "section", ??? add this?
|
"supplement", // maybe deprecated this? supplement-number should serve this purpose in standard CSL.
|
||||||
"supplement",
|
|
||||||
"citation-number"
|
"citation-number"
|
||||||
],
|
],
|
||||||
//var x = new Array();
|
//var x = new Array();
|
||||||
|
|
@ -1476,6 +1504,9 @@ var CSL = {
|
||||||
},
|
},
|
||||||
|
|
||||||
INIT_JURISDICTION_MACROS: function (state, Item, item, macroName) {
|
INIT_JURISDICTION_MACROS: function (state, Item, item, macroName) {
|
||||||
|
if (Item["best-jurisdiction"]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (!state.sys.retrieveStyleModule || !CSL.MODULE_MACROS[macroName] || !Item.jurisdiction) {
|
if (!state.sys.retrieveStyleModule || !CSL.MODULE_MACROS[macroName] || !Item.jurisdiction) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -1495,6 +1526,7 @@ var CSL = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var jurisdictionList = state.getJurisdictionList(Item.jurisdiction);
|
||||||
if (state.opt.parallel.enable) {
|
if (state.opt.parallel.enable) {
|
||||||
if (!state.parallel) {
|
if (!state.parallel) {
|
||||||
state.parallel = new CSL.Parallel(state);
|
state.parallel = new CSL.Parallel(state);
|
||||||
|
|
@ -1505,7 +1537,7 @@ var CSL = {
|
||||||
var jurisdiction = jurisdictionList[i];
|
var jurisdiction = jurisdictionList[i];
|
||||||
if (item) {
|
if (item) {
|
||||||
if (state.juris[jurisdiction] && !item["best-jurisdiction"] && state.juris[jurisdiction].types.locator) {
|
if (state.juris[jurisdiction] && !item["best-jurisdiction"] && state.juris[jurisdiction].types.locator) {
|
||||||
item["best-jurisdiction"] = jurisdiction;
|
Item["best-jurisdiction"] = jurisdiction;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(state.juris[jurisdiction] && state.juris[jurisdiction].types[Item.type]) {
|
if(state.juris[jurisdiction] && state.juris[jurisdiction].types[Item.type]) {
|
||||||
|
|
@ -10032,7 +10064,7 @@ CSL.Node.date = {
|
||||||
|
|
||||||
CSL.Node["date-part"] = {
|
CSL.Node["date-part"] = {
|
||||||
build: function (state, target) {
|
build: function (state, target) {
|
||||||
var func, pos, len, first_date, value, value_end, real, have_collapsed, invoked, precondition, known_year, bc, ad, bc_end, ad_end, ready, curr, dcurr, number, num, formatter, item;
|
var func, pos, len, first_date, value, value_end, real, have_collapsed, invoked, precondition, known_year, bc, ad, bc_end, ad_end, ready, curr, dcurr, number, num, formatter, item, blob;
|
||||||
if (!this.strings.form) {
|
if (!this.strings.form) {
|
||||||
this.strings.form = "long";
|
this.strings.form = "long";
|
||||||
}
|
}
|
||||||
|
|
@ -10193,13 +10225,21 @@ CSL.Node["date-part"] = {
|
||||||
last_string_output = value_end;
|
last_string_output = value_end;
|
||||||
state.dateput.append(value_end, this);
|
state.dateput.append(value_end, this);
|
||||||
if (first_date) {
|
if (first_date) {
|
||||||
state.dateput.current.value().blobs[0].strings.prefix = "";
|
blob = state.dateput.current.value().blobs[0];
|
||||||
|
if (blob) {
|
||||||
|
blob.strings.prefix = "";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
last_string_output = value;
|
last_string_output = value;
|
||||||
state.output.append(value, this);
|
state.output.append(value, this);
|
||||||
curr = state.output.current.value();
|
curr = state.output.current.value();
|
||||||
curr.blobs[(curr.blobs.length - 1)].strings.suffix = "";
|
blob = curr.blobs[(curr.blobs.length - 1)];
|
||||||
|
if (blob) {
|
||||||
|
blob.strings.suffix = "";
|
||||||
|
}
|
||||||
|
|
||||||
if (this.strings["range-delimiter"]) {
|
if (this.strings["range-delimiter"]) {
|
||||||
state.output.append(this.strings["range-delimiter"]);
|
state.output.append(this.strings["range-delimiter"]);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -10234,7 +10274,10 @@ CSL.Node["date-part"] = {
|
||||||
last_string_output = value_end;
|
last_string_output = value_end;
|
||||||
state.dateput.append(value_end, this);
|
state.dateput.append(value_end, this);
|
||||||
if (first_date) {
|
if (first_date) {
|
||||||
state.dateput.current.value().blobs[0].strings.prefix = "";
|
blob = state.dateput.current.value().blobs[0];
|
||||||
|
if (blob) {
|
||||||
|
blob.strings.prefix = "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (bc) {
|
if (bc) {
|
||||||
last_string_output = bc;
|
last_string_output = bc;
|
||||||
|
|
@ -10326,8 +10369,6 @@ CSL.Node["date-part"] = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*global CSL: true */
|
/*global CSL: true */
|
||||||
|
|
||||||
CSL.Node["else-if"] = {
|
CSL.Node["else-if"] = {
|
||||||
|
|
@ -11752,12 +11793,12 @@ CSL.Node["alternative-text"] = {
|
||||||
|
|
||||||
CSL.NameOutput = function(state, Item, item) {
|
CSL.NameOutput = function(state, Item, item) {
|
||||||
this.debug = false;
|
this.debug = false;
|
||||||
|
this.state = state;
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(1)");
|
this.state.sys.print("(1)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
this.state = state;
|
|
||||||
this.Item = Item;
|
this.Item = Item;
|
||||||
this.item = item;
|
this.item = item;
|
||||||
this.nameset_base = 0;
|
this.nameset_base = 0;
|
||||||
|
|
@ -11911,21 +11952,21 @@ CSL.NameOutput.prototype.outputNames = function () {
|
||||||
|
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(2)");
|
this.state.sys.print("(2)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
// util_names_etalconfig.js
|
// util_names_etalconfig.js
|
||||||
this.getEtAlConfig();
|
this.getEtAlConfig();
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(3)");
|
this.state.sys.print("(3)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
// util_names_divide.js
|
// util_names_divide.js
|
||||||
this.divideAndTransliterateNames();
|
this.divideAndTransliterateNames();
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(4)");
|
this.state.sys.print("(4)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
// util_names_truncate.js
|
// util_names_truncate.js
|
||||||
|
|
@ -11933,13 +11974,13 @@ CSL.NameOutput.prototype.outputNames = function () {
|
||||||
this.truncatePersonalNameLists();
|
this.truncatePersonalNameLists();
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(5)");
|
this.state.sys.print("(5)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
|
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(6)");
|
this.state.sys.print("(6)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
// util_names_disambig.js
|
// util_names_disambig.js
|
||||||
|
|
@ -11949,7 +11990,7 @@ CSL.NameOutput.prototype.outputNames = function () {
|
||||||
this.constrainNames();
|
this.constrainNames();
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(7)");
|
this.state.sys.print("(7)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
// form="count"
|
// form="count"
|
||||||
|
|
@ -11963,25 +12004,25 @@ CSL.NameOutput.prototype.outputNames = function () {
|
||||||
|
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(8)");
|
this.state.sys.print("(8)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
this.setEtAlParameters();
|
this.setEtAlParameters();
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(9)");
|
this.state.sys.print("(9)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
this.setCommonTerm(this.requireMatch);
|
this.setCommonTerm(this.requireMatch);
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(10)");
|
this.state.sys.print("(10)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
this.renderAllNames();
|
this.renderAllNames();
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(11)");
|
this.state.sys.print("(11)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
var blob_list = [];
|
var blob_list = [];
|
||||||
|
|
@ -11995,7 +12036,7 @@ CSL.NameOutput.prototype.outputNames = function () {
|
||||||
} else {
|
} else {
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(11a)");
|
this.state.sys.print("(11a)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
for (var j = 0, jlen = this.institutions[v].length; j < jlen; j += 1) {
|
for (var j = 0, jlen = this.institutions[v].length; j < jlen; j += 1) {
|
||||||
|
|
@ -12003,7 +12044,7 @@ CSL.NameOutput.prototype.outputNames = function () {
|
||||||
}
|
}
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(11b)");
|
this.state.sys.print("(11b)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
if (this.institutions[v].length) {
|
if (this.institutions[v].length) {
|
||||||
|
|
@ -12015,13 +12056,13 @@ CSL.NameOutput.prototype.outputNames = function () {
|
||||||
}
|
}
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(11c)");
|
this.state.sys.print("(11c)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
var varblob = this.joinFreetersAndInstitutionSets([this.freeters[v], institutions]);
|
var varblob = this.joinFreetersAndInstitutionSets([this.freeters[v], institutions]);
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(11d)");
|
this.state.sys.print("(11d)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
}
|
}
|
||||||
|
|
@ -12034,7 +12075,7 @@ CSL.NameOutput.prototype.outputNames = function () {
|
||||||
}
|
}
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(11e)");
|
this.state.sys.print("(11e)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
if (this.common_term) {
|
if (this.common_term) {
|
||||||
|
|
@ -12043,14 +12084,14 @@ CSL.NameOutput.prototype.outputNames = function () {
|
||||||
}
|
}
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(12)");
|
this.state.sys.print("(12)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
this.state.output.openLevel("empty");
|
this.state.output.openLevel("empty");
|
||||||
this.state.output.current.value().strings.delimiter = this.state.inheritOpt(this.names, "delimiter", "names-delimiter");
|
this.state.output.current.value().strings.delimiter = this.state.inheritOpt(this.names, "delimiter", "names-delimiter");
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(13)");
|
this.state.sys.print("(13)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
for (i = 0, ilen = blob_list.length; i < ilen; i += 1) {
|
for (i = 0, ilen = blob_list.length; i < ilen; i += 1) {
|
||||||
|
|
@ -12062,20 +12103,20 @@ CSL.NameOutput.prototype.outputNames = function () {
|
||||||
}
|
}
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(14)");
|
this.state.sys.print("(14)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
this.state.output.closeLevel("empty");
|
this.state.output.closeLevel("empty");
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(15)");
|
this.state.sys.print("(15)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
var blob = this.state.output.pop();
|
var blob = this.state.output.pop();
|
||||||
this.state.tmp.name_node.top = blob;
|
this.state.tmp.name_node.top = blob;
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(16)");
|
this.state.sys.print("(16)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
|
|
||||||
|
|
@ -12092,14 +12133,14 @@ CSL.NameOutput.prototype.outputNames = function () {
|
||||||
}
|
}
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(17)");
|
this.state.sys.print("(17)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
// Also used in CSL.Util.substituteEnd (which could do with
|
// Also used in CSL.Util.substituteEnd (which could do with
|
||||||
// some cleanup at this writing).
|
// some cleanup at this writing).
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(18)");
|
this.state.sys.print("(18)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
if (variables[0] !== "authority") {
|
if (variables[0] !== "authority") {
|
||||||
|
|
@ -12164,7 +12205,7 @@ CSL.NameOutput.prototype.outputNames = function () {
|
||||||
|
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("(19)");
|
this.state.sys.print("(19)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
};
|
};
|
||||||
|
|
@ -12256,7 +12297,7 @@ CSL.NameOutput.prototype._collapseAuthor = function () {
|
||||||
this.state.registry.authorstrings[this.Item.id] = mystr;
|
this.state.registry.authorstrings[this.Item.id] = mystr;
|
||||||
} else if (!this.state.tmp.just_looking
|
} else if (!this.state.tmp.just_looking
|
||||||
&& !this.state.tmp.suppress_decorations && ((this.state[this.state.tmp.area].opt.collapse && this.state[this.state.tmp.area].opt.collapse.length) || this.state[this.state.tmp.area].opt.cite_group_delimiter && this.state[this.state.tmp.area].opt.cite_group_delimiter)) {
|
&& !this.state.tmp.suppress_decorations && ((this.state[this.state.tmp.area].opt.collapse && this.state[this.state.tmp.area].opt.collapse.length) || this.state[this.state.tmp.area].opt.cite_group_delimiter && this.state[this.state.tmp.area].opt.cite_group_delimiter)) {
|
||||||
// XX1 print("RENDER: "+this.Item.id);
|
// XX1 this.state.sys.print("RENDER: "+this.Item.id);
|
||||||
mystr = "";
|
mystr = "";
|
||||||
myqueue = this.state.tmp.name_node.top.blobs.slice(-1)[0].blobs;
|
myqueue = this.state.tmp.name_node.top.blobs.slice(-1)[0].blobs;
|
||||||
oldchars = this.state.tmp.offset_characters;
|
oldchars = this.state.tmp.offset_characters;
|
||||||
|
|
@ -12265,7 +12306,7 @@ CSL.NameOutput.prototype._collapseAuthor = function () {
|
||||||
}
|
}
|
||||||
if (mystr === this.state.tmp.last_primary_names_string) {
|
if (mystr === this.state.tmp.last_primary_names_string) {
|
||||||
if (this.item["suppress-author"] || (this.state[this.state.tmp.area].opt.collapse && this.state[this.state.tmp.area].opt.collapse.length)) {
|
if (this.item["suppress-author"] || (this.state[this.state.tmp.area].opt.collapse && this.state[this.state.tmp.area].opt.collapse.length)) {
|
||||||
// XX1 print(" CUT!");
|
// XX1 this.state.sys.print(" CUT!");
|
||||||
this.state.tmp.name_node.top.blobs.pop();
|
this.state.tmp.name_node.top.blobs.pop();
|
||||||
this.state.tmp.name_node.children = [];
|
this.state.tmp.name_node.children = [];
|
||||||
// If popped, avoid side-effects on character counting: we're only interested
|
// If popped, avoid side-effects on character counting: we're only interested
|
||||||
|
|
@ -12277,7 +12318,7 @@ CSL.NameOutput.prototype._collapseAuthor = function () {
|
||||||
this.state.tmp.use_cite_group_delimiter = true;
|
this.state.tmp.use_cite_group_delimiter = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// XX1 print("remembering: "+mystr);
|
// XX1 this.state.sys.print("remembering: "+mystr);
|
||||||
this.state.tmp.last_primary_names_string = mystr;
|
this.state.tmp.last_primary_names_string = mystr;
|
||||||
// XXXXX A little more precision would be nice.
|
// XXXXX A little more precision would be nice.
|
||||||
// This will clobber variable="author editor" as well as variable="author".
|
// This will clobber variable="author editor" as well as variable="author".
|
||||||
|
|
@ -20879,7 +20920,7 @@ CSL.Util.outputNumericField = function(state, varname, itemID) {
|
||||||
CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, masterStyling.strings.prefix, null, masterStyling, `${num.particle}${num.value}`);
|
CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, masterStyling.strings.prefix, null, masterStyling, `${num.particle}${num.value}`);
|
||||||
if (num.collapsible) {
|
if (num.collapsible) {
|
||||||
var blob;
|
var blob;
|
||||||
if (num.value.match(/^[1-9][0-9]*$/)) {
|
if (num.value.match(/^[1-9][0-9]*$/) && Number.isSafeInteger(parseInt(num.value, 10))) {
|
||||||
blob = new CSL.NumericBlob(state, num.particle, parseInt(num.value, 10), numStyling, itemID);
|
blob = new CSL.NumericBlob(state, num.particle, parseInt(num.value, 10), numStyling, itemID);
|
||||||
} else {
|
} else {
|
||||||
blob = new CSL.NumericBlob(state, num.particle, num.value, numStyling, itemID);
|
blob = new CSL.NumericBlob(state, num.particle, num.value, numStyling, itemID);
|
||||||
|
|
@ -20913,7 +20954,7 @@ CSL.Util.outputNumericField = function(state, varname, itemID) {
|
||||||
CSL.Util.PageRangeMangler = {};
|
CSL.Util.PageRangeMangler = {};
|
||||||
|
|
||||||
CSL.Util.PageRangeMangler.getFunction = function (state, rangeType) {
|
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;
|
var rangerex, pos, len, stringify, listify, expand, minimize, minimize_internal, chicago15, chicago16, lst, m, b, e, ret, begin, end, ret_func;
|
||||||
|
|
||||||
var range_delimiter = state.getTerm(rangeType + "-range-delimiter");
|
var range_delimiter = state.getTerm(rangeType + "-range-delimiter");
|
||||||
|
|
||||||
|
|
@ -21021,7 +21062,7 @@ CSL.Util.PageRangeMangler.getFunction = function (state, rangeType) {
|
||||||
return ret.join("");
|
return ret.join("");
|
||||||
};
|
};
|
||||||
|
|
||||||
chicago = function (lst) {
|
chicago15 = function (lst) {
|
||||||
len = lst.length;
|
len = lst.length;
|
||||||
for (pos = 1; pos < len; pos += 2) {
|
for (pos = 1; pos < len; pos += 2) {
|
||||||
if ("object" === typeof lst[pos]) {
|
if ("object" === typeof lst[pos]) {
|
||||||
|
|
@ -21041,6 +21082,31 @@ CSL.Util.PageRangeMangler.getFunction = function (state, rangeType) {
|
||||||
return stringify(lst);
|
return stringify(lst);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
chicago16 = function (lst) {
|
||||||
|
len = lst.length;
|
||||||
|
for (pos = 1; pos < len; pos += 2) {
|
||||||
|
if ("object" === typeof lst[pos]) {
|
||||||
|
m = lst[pos];
|
||||||
|
begin = parseInt(m[1], 10);
|
||||||
|
end = parseInt(m[3], 10);
|
||||||
|
e = "" + end;
|
||||||
|
if (begin > 100 && begin % 100) {
|
||||||
|
for (var i = 2; i < e.length; i++) {
|
||||||
|
var divisor = Math.pow(10, i);
|
||||||
|
if (Math.floor(begin / divisor) === Math.floor(end / divisor)) {
|
||||||
|
m[3] = "" + (end % divisor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m[2].slice(1) === m[0]) {
|
||||||
|
m[2] = range_delimiter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stringify(lst);
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// The top-level option handlers.
|
// The top-level option handlers.
|
||||||
//
|
//
|
||||||
|
|
@ -21070,7 +21136,15 @@ CSL.Util.PageRangeMangler.getFunction = function (state, rangeType) {
|
||||||
};
|
};
|
||||||
} else if (state.opt[rangeType + "-range-format"] === "chicago") {
|
} else if (state.opt[rangeType + "-range-format"] === "chicago") {
|
||||||
ret_func = function (str) {
|
ret_func = function (str) {
|
||||||
return sniff(str, chicago);
|
return sniff(str, chicago15);
|
||||||
|
};
|
||||||
|
} else if (state.opt[rangeType + "-range-format"] === "chicago-15") {
|
||||||
|
ret_func = function (str) {
|
||||||
|
return sniff(str, chicago15);
|
||||||
|
};
|
||||||
|
} else if (state.opt[rangeType + "-range-format"] === "chicago-16") {
|
||||||
|
ret_func = function (str) {
|
||||||
|
return sniff(str, chicago16);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -21668,7 +21742,7 @@ CSL.Output.Formatters = (function () {
|
||||||
var rexNameStr = "(?:[-\\s]*<\\/*(?:span\s+class=\"no(?:case|decor)\"|i|sc|b|sub|sup)>[-\\s]*|[-\\s]+)";
|
var rexNameStr = "(?:[-\\s]*<\\/*(?:span\s+class=\"no(?:case|decor)\"|i|sc|b|sub|sup)>[-\\s]*|[-\\s]+)";
|
||||||
var nameDoppel = new CSL.Doppeler(rexNameStr);
|
var nameDoppel = new CSL.Doppeler(rexNameStr);
|
||||||
|
|
||||||
var wordDoppel = new CSL.Doppeler("(?:[\u0020\u00A0\u2000-\u200B\u205F\u3000]+)");
|
var wordDoppel = new CSL.Doppeler("(?:[\u00A0\u0020\u00A0\u2000-\u200B\u205F\u3000]+)");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* INTERNAL
|
* INTERNAL
|
||||||
|
|
@ -21941,7 +22015,6 @@ CSL.Output.Formatters = (function () {
|
||||||
quoteState: [],
|
quoteState: [],
|
||||||
capitaliseWords: function(str, i, followingTag) {
|
capitaliseWords: function(str, i, followingTag) {
|
||||||
if (str.trim()) {
|
if (str.trim()) {
|
||||||
var words = str.split(/[ \u00A0]+/);
|
|
||||||
var wordle = wordDoppel.split(str);
|
var wordle = wordDoppel.split(str);
|
||||||
var words = wordle.strings;
|
var words = wordle.strings;
|
||||||
for (var j=0,jlen=words.length;j<jlen;j++) {
|
for (var j=0,jlen=words.length;j<jlen;j++) {
|
||||||
|
|
@ -21949,17 +22022,23 @@ CSL.Output.Formatters = (function () {
|
||||||
if (!word) {
|
if (!word) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (word.length > 1 && !CSL.toLocaleLowerCase.call(state, word).match(config.skipWordsRex)) {
|
let lcase = CSL.toLocaleLowerCase.call(state, word);
|
||||||
|
let capitalize = false;
|
||||||
|
if (word.length > 1 && !lcase.match(config.skipWordsRex)) {
|
||||||
// Capitalize every word that is not a stop-word
|
// Capitalize every word that is not a stop-word
|
||||||
words[j] = _capitalise.call(state, words[j]);
|
capitalize = true;
|
||||||
} else if (j === (words.length - 1) && followingTag === "-") {
|
} else if (j === (words.length - 1) && followingTag === "-") {
|
||||||
words[j] = _capitalise.call(state, words[j]);
|
capitalize = true;
|
||||||
} else if (config.isFirst) {
|
} else if (config.isFirst) {
|
||||||
// Capitalize first word, even if a stop-word
|
// Capitalize first word, even if a stop-word
|
||||||
words[j] = _capitalise.call(state, words[j]);
|
capitalize = true;
|
||||||
} else if (config.afterPunct) {
|
} else if (config.afterPunct) {
|
||||||
// Capitalize after punctuation
|
// Capitalize after punctuation
|
||||||
words[j] = _capitalise.call(state, words[j]);
|
capitalize = true;
|
||||||
|
}
|
||||||
|
// Don't capitalize if word already contains capitalization
|
||||||
|
if (capitalize && word === lcase) {
|
||||||
|
words[j] = _capitalise.call(state, word);
|
||||||
}
|
}
|
||||||
config.afterPunct = false;
|
config.afterPunct = false;
|
||||||
config.isFirst = false;
|
config.isFirst = false;
|
||||||
|
|
@ -21989,18 +22068,22 @@ CSL.Output.Formatters = (function () {
|
||||||
var config = {
|
var config = {
|
||||||
quoteState: [],
|
quoteState: [],
|
||||||
capitaliseWords: function(str) {
|
capitaliseWords: function(str) {
|
||||||
var words = str.split(" ");
|
var wordle = wordDoppel.split(str);
|
||||||
|
var words = wordle.strings;
|
||||||
for (var i=0,ilen=words.length;i<ilen;i++) {
|
for (var i=0,ilen=words.length;i<ilen;i++) {
|
||||||
var word = words[i];
|
var word = words[i];
|
||||||
if (word) {
|
if (word) {
|
||||||
if (config.isFirst) {
|
if (config.isFirst) {
|
||||||
words[i] = _capitalise.call(state, word);
|
// Don't capitalize if word already contains capitalization
|
||||||
|
if (word === CSL.toLocaleLowerCase.call(state, word)) {
|
||||||
|
words[i] = _capitalise.call(state, word);
|
||||||
|
}
|
||||||
config.isFirst = false;
|
config.isFirst = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return words.join(" ");
|
return wordDoppel.join(wordle);
|
||||||
},
|
},
|
||||||
skipWordsRex: null,
|
skipWordsRex: null,
|
||||||
tagState: [],
|
tagState: [],
|
||||||
|
|
@ -22020,14 +22103,18 @@ CSL.Output.Formatters = (function () {
|
||||||
var config = {
|
var config = {
|
||||||
quoteState: [],
|
quoteState: [],
|
||||||
capitaliseWords: function(str) {
|
capitaliseWords: function(str) {
|
||||||
var words = str.split(" ");
|
var wordle = wordDoppel.split(str);
|
||||||
|
var words = wordle.strings;
|
||||||
for (var i=0,ilen=words.length;i<ilen;i++) {
|
for (var i=0,ilen=words.length;i<ilen;i++) {
|
||||||
var word = words[i];
|
var word = words[i];
|
||||||
if (word) {
|
if (word) {
|
||||||
words[i] = _capitalise.call(state, word);
|
// Don't capitalize if word already contains capitalization
|
||||||
|
if (word === CSL.toLocaleLowerCase.call(state, word)) {
|
||||||
|
words[i] = _capitalise.call(state, word);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return words.join(" ");
|
return wordDoppel.join(wordle);
|
||||||
},
|
},
|
||||||
skipWordsRex: null,
|
skipWordsRex: null,
|
||||||
tagState: [],
|
tagState: [],
|
||||||
|
|
@ -22261,7 +22348,7 @@ CSL.Output.Formats.prototype.text = {
|
||||||
return "\n"+str;
|
return "\n"+str;
|
||||||
},
|
},
|
||||||
"@display/left-margin": function (state, str) {
|
"@display/left-margin": function (state, str) {
|
||||||
return str;
|
return str + " ";
|
||||||
},
|
},
|
||||||
"@display/right-inline": function (state, str) {
|
"@display/right-inline": function (state, str) {
|
||||||
return str;
|
return str;
|
||||||
|
|
@ -22630,7 +22717,7 @@ CSL.Output.Formats.prototype.latex = {
|
||||||
return text;
|
return text;
|
||||||
},
|
},
|
||||||
"bibstart": "\\begin{thebibliography}{4}",
|
"bibstart": "\\begin{thebibliography}{4}",
|
||||||
"bibend": "\end{thebibliography}",
|
"bibend": "\\end{thebibliography}",
|
||||||
"@font-style/italic": "{\\em %%STRING%%}",
|
"@font-style/italic": "{\\em %%STRING%%}",
|
||||||
"@font-style/oblique": false,
|
"@font-style/oblique": false,
|
||||||
"@font-style/normal": false,
|
"@font-style/normal": false,
|
||||||
|
|
@ -23535,6 +23622,11 @@ CSL.Registry.NameReg = function (state) {
|
||||||
|
|
||||||
set_keys = function (state, itemid, nameobj) {
|
set_keys = function (state, itemid, nameobj) {
|
||||||
pkey = strip_periods(nameobj.family);
|
pkey = strip_periods(nameobj.family);
|
||||||
|
|
||||||
|
if (state.opt["demote-non-dropping-particle"] === "never" && nameobj["non-dropping-particle"] && nameobj["family"]) {
|
||||||
|
pkey = `${pkey} ${nameobj["non-dropping-particle"]}`;
|
||||||
|
}
|
||||||
|
|
||||||
skey = strip_periods(nameobj.given);
|
skey = strip_periods(nameobj.given);
|
||||||
// Drop lowercase suffixes (such as et al.) from given name field
|
// Drop lowercase suffixes (such as et al.) from given name field
|
||||||
// for disambiguation purposes.
|
// for disambiguation purposes.
|
||||||
|
|
@ -23748,10 +23840,8 @@ CSL.Registry.NameReg = function (state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A hack. Safe if the name object is used only here, for disambiguation purposes.
|
// A hack. Safe if the name object is used only here, for disambiguation purposes.
|
||||||
// TEMP: Disabled due to https://github.com/Juris-M/citeproc-js/issues/179
|
/*
|
||||||
/*if (state.opt["demote-non-dropping-particle"] === "never" && nameobj["non-dropping-particle"] && nameobj["family"]) {
|
*/
|
||||||
nameobj["family"] = nameobj["non-dropping-particle"] + " " + nameobj["family"];
|
|
||||||
}*/
|
|
||||||
|
|
||||||
//CSL.debug("INS");
|
//CSL.debug("INS");
|
||||||
set_keys(this.state, "" + item_id, nameobj);
|
set_keys(this.state, "" + item_id, nameobj);
|
||||||
|
|
@ -23853,7 +23943,7 @@ CSL.Disambiguation.prototype.run = function(akey) {
|
||||||
}
|
}
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("[A] === RUN ===");
|
this.state.sys.print("[A] === RUN ===");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
this.akey = akey;
|
this.akey = akey;
|
||||||
|
|
@ -23867,7 +23957,7 @@ CSL.Disambiguation.prototype.runDisambig = function () {
|
||||||
var ismax;
|
var ismax;
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("[C] === runDisambig() ===");
|
this.state.sys.print("[C] === runDisambig() ===");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
this.initGivens = true;
|
this.initGivens = true;
|
||||||
|
|
@ -23896,7 +23986,7 @@ CSL.Disambiguation.prototype.scanItems = function (list) {
|
||||||
var pos, len, otherItem;
|
var pos, len, otherItem;
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("[2] === scanItems() ===");
|
this.state.sys.print("[2] === scanItems() ===");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
|
|
||||||
|
|
@ -23915,15 +24005,15 @@ CSL.Disambiguation.prototype.scanItems = function (list) {
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
if (pos > 1) {
|
if (pos > 1) {
|
||||||
print(" -----------");
|
this.state.sys.print(" -----------");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
if (this.ItemCite === otherItemCite) {
|
if (this.ItemCite === otherItemCite) {
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print(" [CLASH]--> "+this.Item.id+": "+this.ItemCite);
|
this.state.sys.print(" [CLASH]--> "+this.Item.id+": "+this.ItemCite);
|
||||||
print(" "+otherItem.id+": "+otherItemCite);
|
this.state.sys.print(" "+otherItem.id+": "+otherItemCite);
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
clashes += 1;
|
clashes += 1;
|
||||||
|
|
@ -23931,8 +24021,8 @@ CSL.Disambiguation.prototype.scanItems = function (list) {
|
||||||
} else {
|
} else {
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print(" [clear]--> "+this.Item.id+": "+this.ItemCite);
|
this.state.sys.print(" [clear]--> "+this.Item.id+": "+this.ItemCite);
|
||||||
print(" "+otherItem.id+": "+otherItemCite);
|
this.state.sys.print(" "+otherItem.id+": "+otherItemCite);
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
this.nonpartners.push(otherItem);
|
this.nonpartners.push(otherItem);
|
||||||
|
|
@ -23958,9 +24048,9 @@ CSL.Disambiguation.prototype.disNames = function (ismax) {
|
||||||
|
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("[3] == disNames() ==");
|
this.state.sys.print("[3] == disNames() ==");
|
||||||
//print(" partners: "+[this.partners[i].id for (i in this.partners)].join(", "));
|
//this.state.sys.print(" partners: "+[this.partners[i].id for (i in this.partners)].join(", "));
|
||||||
//print(" nonpartners: "+[this.nonpartners[i].id for (i in this.nonpartners)].join(", "));
|
//this.state.sys.print(" nonpartners: "+[this.nonpartners[i].id for (i in this.nonpartners)].join(", "));
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
|
|
||||||
|
|
@ -23988,8 +24078,8 @@ CSL.Disambiguation.prototype.disNames = function (ismax) {
|
||||||
this.captureStepToBase();
|
this.captureStepToBase();
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print(" ** RESOLUTION [a]: lone partner, one nonpartner");
|
this.state.sys.print(" ** RESOLUTION [a]: lone partner, one nonpartner");
|
||||||
print(" registering "+this.partners[0].id+" and "+this.nonpartners[0].id);
|
this.state.sys.print(" registering "+this.partners[0].id+" and "+this.nonpartners[0].id);
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
this.state.registry.registerAmbigToken(this.akey, "" + this.nonpartners[0].id, this.betterbase);
|
this.state.registry.registerAmbigToken(this.akey, "" + this.nonpartners[0].id, this.betterbase);
|
||||||
|
|
@ -23999,8 +24089,8 @@ CSL.Disambiguation.prototype.disNames = function (ismax) {
|
||||||
this.captureStepToBase();
|
this.captureStepToBase();
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print(" ** RESOLUTION [b]: lone partner, unknown number of remaining nonpartners");
|
this.state.sys.print(" ** RESOLUTION [b]: lone partner, unknown number of remaining nonpartners");
|
||||||
print(" registering "+this.partners[0].id);
|
this.state.sys.print(" registering "+this.partners[0].id);
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
this.state.registry.registerAmbigToken(this.akey, "" + this.partners[0].id, this.betterbase);
|
this.state.registry.registerAmbigToken(this.akey, "" + this.partners[0].id, this.betterbase);
|
||||||
|
|
@ -24012,8 +24102,8 @@ CSL.Disambiguation.prototype.disNames = function (ismax) {
|
||||||
this.captureStepToBase();
|
this.captureStepToBase();
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print(" ** RESOLUTION [c]: lone nonpartner, unknown number of partners remaining");
|
this.state.sys.print(" ** RESOLUTION [c]: lone nonpartner, unknown number of partners remaining");
|
||||||
print(" registering "+this.nonpartners[0].id);
|
this.state.sys.print(" registering "+this.nonpartners[0].id);
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
this.state.registry.registerAmbigToken(this.akey, "" + this.nonpartners[0].id, this.betterbase);
|
this.state.registry.registerAmbigToken(this.akey, "" + this.nonpartners[0].id, this.betterbase);
|
||||||
|
|
@ -24023,7 +24113,7 @@ CSL.Disambiguation.prototype.disNames = function (ismax) {
|
||||||
this.captureStepToBase();
|
this.captureStepToBase();
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print(" ** RESOLUTION [d]: better result, but no entries safe to register");
|
this.state.sys.print(" ** RESOLUTION [d]: better result, but no entries safe to register");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
this.lists[this.listpos] = [this.betterbase, this.partners];
|
this.lists[this.listpos] = [this.betterbase, this.partners];
|
||||||
|
|
@ -24031,7 +24121,7 @@ CSL.Disambiguation.prototype.disNames = function (ismax) {
|
||||||
} else {
|
} else {
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print(" ** RESOLUTION [e]: no improvement, and clashes remain");
|
this.state.sys.print(" ** RESOLUTION [e]: no improvement, and clashes remain");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
if (ismax) {
|
if (ismax) {
|
||||||
|
|
@ -24040,7 +24130,7 @@ CSL.Disambiguation.prototype.disNames = function (ismax) {
|
||||||
if (this.modeindex === this.modes.length - 1) {
|
if (this.modeindex === this.modes.length - 1) {
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print(" (registering clashing entries because we've run out of options)");
|
this.state.sys.print(" (registering clashing entries because we've run out of options)");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
for (var i = 0, ilen = this.partners.length; i < ilen; i += 1) {
|
for (var i = 0, ilen = this.partners.length; i < ilen; i += 1) {
|
||||||
|
|
@ -24055,7 +24145,7 @@ CSL.Disambiguation.prototype.disNames = function (ismax) {
|
||||||
CSL.Disambiguation.prototype.disExtraText = function () {
|
CSL.Disambiguation.prototype.disExtraText = function () {
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("[3] === disExtraText ==");
|
this.state.sys.print("[3] === disExtraText ==");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
|
|
||||||
|
|
@ -24117,7 +24207,7 @@ CSL.Disambiguation.prototype.disYears = function () {
|
||||||
var pos, len, tokens, token;
|
var pos, len, tokens, token;
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("[3] === disYears ==");
|
this.state.sys.print("[3] === disYears ==");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
tokens = [];
|
tokens = [];
|
||||||
|
|
@ -24153,7 +24243,7 @@ CSL.Disambiguation.prototype.disYears = function () {
|
||||||
CSL.Disambiguation.prototype.incrementDisambig = function () {
|
CSL.Disambiguation.prototype.incrementDisambig = function () {
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("\n[1] === incrementDisambig() ===");
|
this.state.sys.print("\n[1] === incrementDisambig() ===");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
if (this.initGivens) {
|
if (this.initGivens) {
|
||||||
|
|
@ -24217,20 +24307,20 @@ CSL.Disambiguation.prototype.incrementDisambig = function () {
|
||||||
}
|
}
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print(" ------------------");
|
this.state.sys.print(" ------------------");
|
||||||
print(" incremented values");
|
this.state.sys.print(" incremented values");
|
||||||
print(" ------------------");
|
this.state.sys.print(" ------------------");
|
||||||
print(" | gnameset: "+this.gnameset);
|
this.state.sys.print(" | gnameset: "+this.gnameset);
|
||||||
print(" | gname: "+this.gname);
|
this.state.sys.print(" | gname: "+this.gname);
|
||||||
print(" | names value: "+this.base.names[this.gnameset]);
|
this.state.sys.print(" | names value: "+this.base.names[this.gnameset]);
|
||||||
if (this.base.givens.length) {
|
if (this.base.givens.length) {
|
||||||
print(" | givens value: "+this.base.givens[this.gnameset][this.gname]);
|
this.state.sys.print(" | givens value: "+this.base.givens[this.gnameset][this.gname]);
|
||||||
} else {
|
} else {
|
||||||
print(" | givens value: nil");
|
this.state.sys.print(" | givens value: nil");
|
||||||
}
|
}
|
||||||
print(" | namesetsMax: "+this.namesetsMax);
|
this.state.sys.print(" | namesetsMax: "+this.namesetsMax);
|
||||||
print(" | namesMax: "+this.namesMax);
|
this.state.sys.print(" | namesMax: "+this.namesMax);
|
||||||
print(" | givensMax: "+this.givensMax);
|
this.state.sys.print(" | givensMax: "+this.givensMax);
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
if (("number" !== typeof this.namesetsMax || this.namesetsMax === -1 || this.gnameset === this.namesetsMax)
|
if (("number" !== typeof this.namesetsMax || this.namesetsMax === -1 || this.gnameset === this.namesetsMax)
|
||||||
|
|
@ -24241,7 +24331,7 @@ CSL.Disambiguation.prototype.incrementDisambig = function () {
|
||||||
maxed = true;
|
maxed = true;
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print(" MAXED");
|
this.state.sys.print(" MAXED");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
}
|
}
|
||||||
|
|
@ -24256,7 +24346,7 @@ CSL.Disambiguation.prototype.initVars = function (akey) {
|
||||||
var i, ilen, myIds, myItemBundles, myItems;
|
var i, ilen, myIds, myItemBundles, myItems;
|
||||||
//SNIP-START
|
//SNIP-START
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
print("[B] === initVars() ===");
|
this.state.sys.print("[B] === initVars() ===");
|
||||||
}
|
}
|
||||||
//SNIP-END
|
//SNIP-END
|
||||||
this.lists = [];
|
this.lists = [];
|
||||||
|
|
|
||||||
|
|
@ -350,7 +350,7 @@ Zotero.CollectionTreeRow.prototype.getSearchObject = Zotero.Promise.coroutine(fu
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var s = new Zotero.Search();
|
var s = new Zotero.Search();
|
||||||
s.addCondition('libraryID', 'is', this.ref.libraryID);
|
s.libraryID = this.ref.libraryID;
|
||||||
// Library root
|
// Library root
|
||||||
if (this.isLibrary(true)) {
|
if (this.isLibrary(true)) {
|
||||||
s.addCondition('noChildren', 'true');
|
s.addCondition('noChildren', 'true');
|
||||||
|
|
@ -379,7 +379,7 @@ Zotero.CollectionTreeRow.prototype.getSearchObject = Zotero.Promise.coroutine(fu
|
||||||
|
|
||||||
// Create the outer (filter) search
|
// Create the outer (filter) search
|
||||||
var s2 = new Zotero.Search();
|
var s2 = new Zotero.Search();
|
||||||
s2.addCondition('libraryID', 'is', this.ref.libraryID);
|
s2.libraryID = this.ref.libraryID;
|
||||||
|
|
||||||
if (this.isTrash()) {
|
if (this.isTrash()) {
|
||||||
s2.addCondition('deleted', 'true');
|
s2.addCondition('deleted', 'true');
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ Zotero.HTTPIntegrationClient.Application = function() {
|
||||||
this.supportedNotes = ['footnotes'];
|
this.supportedNotes = ['footnotes'];
|
||||||
this.supportsImportExport = false;
|
this.supportsImportExport = false;
|
||||||
this.supportsTextInsertion = false;
|
this.supportsTextInsertion = false;
|
||||||
|
this.supportsCitationMerging = false;
|
||||||
this.processorName = "HTTP Integration";
|
this.processorName = "HTTP Integration";
|
||||||
};
|
};
|
||||||
Zotero.HTTPIntegrationClient.Application.prototype = {
|
Zotero.HTTPIntegrationClient.Application.prototype = {
|
||||||
|
|
@ -70,6 +71,7 @@ Zotero.HTTPIntegrationClient.Application.prototype = {
|
||||||
this.supportedNotes = result.supportedNotes || this.supportedNotes;
|
this.supportedNotes = result.supportedNotes || this.supportedNotes;
|
||||||
this.supportsImportExport = result.supportsImportExport || this.supportsImportExport;
|
this.supportsImportExport = result.supportsImportExport || this.supportsImportExport;
|
||||||
this.supportsTextInsertion = result.supportsTextInsertion || this.supportsTextInsertion;
|
this.supportsTextInsertion = result.supportsTextInsertion || this.supportsTextInsertion;
|
||||||
|
this.supportsCitationMerging = result.supportsCitationMerging || this.supportsCitationMerging;
|
||||||
this.processorName = result.processorName || this.processorName;
|
this.processorName = result.processorName || this.processorName;
|
||||||
return new Zotero.HTTPIntegrationClient.Document(result.documentID, this.processorName);
|
return new Zotero.HTTPIntegrationClient.Document(result.documentID, this.processorName);
|
||||||
}
|
}
|
||||||
|
|
@ -168,6 +170,10 @@ Zotero.HTTPIntegrationClient.Field = function(documentID, json) {
|
||||||
this._code = json.code;
|
this._code = json.code;
|
||||||
this._text = json.text;
|
this._text = json.text;
|
||||||
this._noteIndex = json.noteIndex;
|
this._noteIndex = json.noteIndex;
|
||||||
|
this._adjacent = json.adjacent;
|
||||||
|
if (this._adjacent !== 'undefined') {
|
||||||
|
this.isAdjacentToNextField = this._isAdjacentToNextField;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Zotero.HTTPIntegrationClient.Field.prototype = {};
|
Zotero.HTTPIntegrationClient.Field.prototype = {};
|
||||||
|
|
||||||
|
|
@ -202,3 +208,6 @@ Zotero.HTTPIntegrationClient.Field.prototype.getNoteIndex = async function() {
|
||||||
Zotero.HTTPIntegrationClient.Field.prototype.equals = async function(arg) {
|
Zotero.HTTPIntegrationClient.Field.prototype.equals = async function(arg) {
|
||||||
return this._id === arg._id;
|
return this._id === arg._id;
|
||||||
};
|
};
|
||||||
|
Zotero.HTTPIntegrationClient.Field.prototype._isAdjacentToNextField = async function() {
|
||||||
|
return this._adjacent;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1135,7 +1135,7 @@ Zotero.Server.Connector.SaveSnapshot.prototype = {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let item = await this.saveSnapshot(targetID, requestData);
|
var item = await this.saveSnapshot(targetID, requestData);
|
||||||
await session.addItem(item);
|
await session.addItem(item);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
|
|
@ -1143,9 +1143,15 @@ Zotero.Server.Connector.SaveSnapshot.prototype = {
|
||||||
return 500;
|
return 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let attachments = [];
|
||||||
|
let hasAttachments = !item.isAttachment() && item.getAttachments().length;
|
||||||
|
if (hasAttachments) {
|
||||||
|
attachments = [{mimeType: "text/html", title: data.title, url: data.url}];
|
||||||
|
}
|
||||||
|
|
||||||
return [201,
|
return [201,
|
||||||
"application/json",
|
"application/json",
|
||||||
JSON.stringify({ saveSingleFile: !data.skipSnapshot && !data.pdf })];
|
JSON.stringify({ saveSingleFile: !data.skipSnapshot && !data.pdf && data.singleFile, attachments })];
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -1175,6 +1181,7 @@ Zotero.Server.Connector.SaveSnapshot.prototype = {
|
||||||
let item = await Zotero.Attachments.importFromURL({
|
let item = await Zotero.Attachments.importFromURL({
|
||||||
libraryID,
|
libraryID,
|
||||||
url: data.url,
|
url: data.url,
|
||||||
|
referrer: data.referrer,
|
||||||
collections: collection ? [collection.id] : undefined,
|
collections: collection ? [collection.id] : undefined,
|
||||||
contentType: "application/pdf",
|
contentType: "application/pdf",
|
||||||
cookieSandbox
|
cookieSandbox
|
||||||
|
|
@ -1234,6 +1241,7 @@ Zotero.Server.Connector.SaveSnapshot.prototype = {
|
||||||
await Zotero.Attachments.importFromURL({
|
await Zotero.Attachments.importFromURL({
|
||||||
libraryID,
|
libraryID,
|
||||||
url: data.url,
|
url: data.url,
|
||||||
|
referrer: data.referrer,
|
||||||
title,
|
title,
|
||||||
parentItemID: itemID,
|
parentItemID: itemID,
|
||||||
contentType: "text/html",
|
contentType: "text/html",
|
||||||
|
|
@ -1689,9 +1697,8 @@ Zotero.Server.Connector.Ping.prototype = {
|
||||||
*/
|
*/
|
||||||
init: function (req) {
|
init: function (req) {
|
||||||
if (req.method == 'GET') {
|
if (req.method == 'GET') {
|
||||||
return [200, "text/html", '<!DOCTYPE html><html><head>' +
|
return [200, "text/html", '<!DOCTYPE html><html>'
|
||||||
'<title>Zotero Connector Server is Available</title></head>' +
|
+ '<body>Zotero is running</body></html>'];
|
||||||
'<body>Zotero Connector Server is Available</body></html>'];
|
|
||||||
} else {
|
} else {
|
||||||
// Store the active URL so it can be used for site-specific Quick Copy
|
// Store the active URL so it can be used for site-specific Quick Copy
|
||||||
if (req.data.activeURL) {
|
if (req.data.activeURL) {
|
||||||
|
|
@ -1846,3 +1853,103 @@ Zotero.Server.Connector.IEHack.prototype = {
|
||||||
'</head><body></body></html>');
|
'</head><body></body></html>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make an HTTP request from the client. Accepts {@link Zotero.HTTP.request} options and returns a minimal response
|
||||||
|
* object with the same form as the one returned from {@link Zotero.Utilities.Translate#request}.
|
||||||
|
*
|
||||||
|
* Accepts:
|
||||||
|
* method - The request method ('GET', 'POST', etc.)
|
||||||
|
* url - The URL to make the request to. Must be an absolute HTTP(S) URL.
|
||||||
|
* options - See Zotero.HTTP.request() documentation. Differences:
|
||||||
|
* - responseType is always set to 'text'
|
||||||
|
* - successCodes is always set to false (non-2xx status codes will not trigger an error)
|
||||||
|
* Returns:
|
||||||
|
* Response code is always 200. Body contains:
|
||||||
|
* status - The response status code, as a number
|
||||||
|
* headers - An object mapping header names to values
|
||||||
|
* body - The response body, as a string
|
||||||
|
*/
|
||||||
|
Zotero.Server.Connector.Request = function () {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of allowed hosts. Intentionally hardcoded.
|
||||||
|
*/
|
||||||
|
Zotero.Server.Connector.Request.allowedHosts = ['www.worldcat.org'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For testing: allow disabling validation so we can make requests to the server.
|
||||||
|
*/
|
||||||
|
Zotero.Server.Connector.Request.enableValidation = false;
|
||||||
|
|
||||||
|
Zotero.Server.Endpoints["/connector/request"] = Zotero.Server.Connector.Request;
|
||||||
|
Zotero.Server.Connector.Request.prototype = {
|
||||||
|
supportedMethods: ["POST"],
|
||||||
|
supportedDataTypes: ["application/json"],
|
||||||
|
|
||||||
|
init: async function (req) {
|
||||||
|
let { method, url, options } = req.data;
|
||||||
|
|
||||||
|
if (typeof method !== 'string' || typeof url !== 'string') {
|
||||||
|
return [400, 'text/plain', 'method and url are required and must be strings'];
|
||||||
|
}
|
||||||
|
|
||||||
|
let uri;
|
||||||
|
try {
|
||||||
|
uri = Services.io.newURI(url);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
return [400, 'text/plain', 'Invalid URL'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uri.scheme != 'http' && uri.scheme != 'https') {
|
||||||
|
return [400, 'text/plain', 'Unsupported scheme'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Zotero.Server.Connector.Request.enableValidation) {
|
||||||
|
if (!Zotero.Server.Connector.Request.allowedHosts.includes(uri.host)) {
|
||||||
|
return [
|
||||||
|
400,
|
||||||
|
'text/plain',
|
||||||
|
'Unsupported URL'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!req.headers['User-Agent'] || !req.headers['User-Agent'].startsWith('Mozilla/')) {
|
||||||
|
return [400, 'text/plain', 'Unsupported User-Agent'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
options.responseType = 'text';
|
||||||
|
options.successCodes = false;
|
||||||
|
|
||||||
|
let xhr;
|
||||||
|
try {
|
||||||
|
xhr = await Zotero.HTTP.request(req.data.method, req.data.url, options);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
if (e instanceof Zotero.HTTP.BrowserOfflineException) {
|
||||||
|
return [503, 'text/plain', 'Client is offline'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let status = xhr.status;
|
||||||
|
let headers = {};
|
||||||
|
xhr.getAllResponseHeaders()
|
||||||
|
.trim()
|
||||||
|
.split(/[\r\n]+/)
|
||||||
|
.map(line => line.split(': '))
|
||||||
|
.forEach(parts => headers[parts.shift()] = parts.join(': '));
|
||||||
|
let body = xhr.response;
|
||||||
|
|
||||||
|
return [200, 'application/json', JSON.stringify({
|
||||||
|
status,
|
||||||
|
headers,
|
||||||
|
body
|
||||||
|
})];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -317,10 +317,17 @@ Zotero.CookieSandbox.Observer = new function() {
|
||||||
this.trackedInterfaceRequestors.splice(i, 1);
|
this.trackedInterfaceRequestors.splice(i, 1);
|
||||||
this.trackedInterfaceRequestorSandboxes.splice(i, 1);
|
this.trackedInterfaceRequestorSandboxes.splice(i, 1);
|
||||||
i--;
|
i--;
|
||||||
} else if(ir == notificationCallbacks) {
|
} else {
|
||||||
// We are tracking this interface requestor
|
let tracked = ir === notificationCallbacks;
|
||||||
trackedBy = this.trackedInterfaceRequestorSandboxes[i];
|
try {
|
||||||
break;
|
tracked = ir === notificationCallbacks.getInterface(Ci.nsIWebBrowserPersist);
|
||||||
|
} catch (e) { }
|
||||||
|
|
||||||
|
if (tracked) {
|
||||||
|
// We are tracking this interface requestor
|
||||||
|
trackedBy = this.trackedInterfaceRequestorSandboxes[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -350,12 +357,6 @@ Zotero.CookieSandbox.Observer = new function() {
|
||||||
notificationCallbacks.QueryInterface(Components.interfaces.nsIXMLHttpRequest);
|
notificationCallbacks.QueryInterface(Components.interfaces.nsIXMLHttpRequest);
|
||||||
tested = true;
|
tested = true;
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
if(!tested) {
|
|
||||||
try {
|
|
||||||
notificationCallbacks.QueryInterface(Components.interfaces.nsIWebBrowserPersist);
|
|
||||||
tested = true;
|
|
||||||
} catch(e) {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -384,6 +385,10 @@ Zotero.CookieSandbox.Observer = new function() {
|
||||||
var cookiesForURI = trackedBy.getCookiesForURI(channel.URI);
|
var cookiesForURI = trackedBy.getCookiesForURI(channel.URI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (trackedBy && trackedBy.userAgent) {
|
||||||
|
channel.setRequestHeader("User-Agent", trackedBy.userAgent, false);
|
||||||
|
}
|
||||||
|
|
||||||
if(!trackedBy || !cookiesForURI) {
|
if(!trackedBy || !cookiesForURI) {
|
||||||
channel.setRequestHeader("Cookie", "", false);
|
channel.setRequestHeader("Cookie", "", false);
|
||||||
channel.setRequestHeader("Cookie2", "", false);
|
channel.setRequestHeader("Cookie2", "", false);
|
||||||
|
|
@ -391,10 +396,6 @@ Zotero.CookieSandbox.Observer = new function() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(trackedBy.userAgent) {
|
|
||||||
channel.setRequestHeader("User-Agent", trackedBy.userAgent, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add cookies to be sent to this domain
|
// add cookies to be sent to this domain
|
||||||
channel.setRequestHeader("Cookie", Zotero.CookieSandbox.generateCookieString(cookiesForURI), false);
|
channel.setRequestHeader("Cookie", Zotero.CookieSandbox.generateCookieString(cookiesForURI), false);
|
||||||
Zotero.debug("CookieSandbox: Added cookies for request to "+channelURI, 5);
|
Zotero.debug("CookieSandbox: Added cookies for request to "+channelURI, 5);
|
||||||
|
|
|
||||||
|
|
@ -470,6 +470,7 @@ Zotero.ItemTypes = new function() {
|
||||||
case 'case':
|
case 'case':
|
||||||
case 'computerProgram':
|
case 'computerProgram':
|
||||||
case 'conferencePaper':
|
case 'conferencePaper':
|
||||||
|
case 'dataset':
|
||||||
case 'dictionaryEntry':
|
case 'dictionaryEntry':
|
||||||
case 'email':
|
case 'email':
|
||||||
case 'encyclopediaArticle':
|
case 'encyclopediaArticle':
|
||||||
|
|
@ -488,6 +489,7 @@ Zotero.ItemTypes = new function() {
|
||||||
case 'preprint':
|
case 'preprint':
|
||||||
case 'presentation':
|
case 'presentation':
|
||||||
case 'report':
|
case 'report':
|
||||||
|
case 'standard':
|
||||||
case 'statute':
|
case 'statute':
|
||||||
case 'thesis':
|
case 'thesis':
|
||||||
case 'webpage':
|
case 'webpage':
|
||||||
|
|
|
||||||
|
|
@ -117,10 +117,10 @@ for (let name of ['deleted']) {
|
||||||
val = !!val;
|
val = !!val;
|
||||||
var oldVal = this._getLatestField(name);
|
var oldVal = this._getLatestField(name);
|
||||||
if (oldVal == val) {
|
if (oldVal == val) {
|
||||||
Zotero.debug(Zotero.Utilities.capitalize(name)
|
Zotero.debug(`Field '${name}' hasn't changed`);
|
||||||
+ ` state hasn't changed for ${this._objectType} ${this.id}`);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Zotero.debug(`Field '${name}' has changed from '${oldVal}' to '${val}'`, 4);
|
||||||
this._markFieldChange(name, val);
|
this._markFieldChange(name, val);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -987,6 +987,11 @@ Zotero.DataObject.prototype._initSave = Zotero.Promise.coroutine(function* (env)
|
||||||
|
|
||||||
env.isNew = !this.id;
|
env.isNew = !this.id;
|
||||||
|
|
||||||
|
if (!this.hasChanged()) {
|
||||||
|
Zotero.debug(this._ObjectType + ' ' + this.id + ' has not changed', 4);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!env.options.skipEditCheck) {
|
if (!env.options.skipEditCheck) {
|
||||||
if (!this.isEditable()) {
|
if (!this.isEditable()) {
|
||||||
throw new Error("Cannot edit " + this._objectType + " in library "
|
throw new Error("Cannot edit " + this._objectType + " in library "
|
||||||
|
|
@ -999,11 +1004,6 @@ Zotero.DataObject.prototype._initSave = Zotero.Promise.coroutine(function* (env)
|
||||||
throw new Error("Cannot add " + this._objectType + " to a " + targetLib.libraryType + " library");
|
throw new Error("Cannot add " + this._objectType + " to a " + targetLib.libraryType + " library");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.hasChanged()) {
|
|
||||||
Zotero.debug(this._ObjectType + ' ' + this.id + ' has not changed', 4);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Undo registerObject() on failure
|
// Undo registerObject() on failure
|
||||||
if (env.isNew) {
|
if (env.isNew) {
|
||||||
var func = function () {
|
var func = function () {
|
||||||
|
|
|
||||||
|
|
@ -275,7 +275,7 @@ Zotero.DataObjectUtilities = {
|
||||||
var pred2 = Object.keys(data2);
|
var pred2 = Object.keys(data2);
|
||||||
pred2.sort();
|
pred2.sort();
|
||||||
if (!Zotero.Utilities.arrayEquals(pred1, pred2)) return true;
|
if (!Zotero.Utilities.arrayEquals(pred1, pred2)) return true;
|
||||||
for (let pred in pred1) {
|
for (let pred of pred1) {
|
||||||
let vals1 = typeof data1[pred] == 'string' ? [data1[pred]] : data1[pred];
|
let vals1 = typeof data1[pred] == 'string' ? [data1[pred]] : data1[pred];
|
||||||
let vals2 = (!data2[pred] || data2[pred] === '')
|
let vals2 = (!data2[pred] || data2[pred] === '')
|
||||||
? []
|
? []
|
||||||
|
|
|
||||||
|
|
@ -133,8 +133,9 @@ Zotero.Feeds = new function() {
|
||||||
for (let feedElem of feedElems) {
|
for (let feedElem of feedElems) {
|
||||||
let url = feedElem.getAttribute('xmlUrl');
|
let url = feedElem.getAttribute('xmlUrl');
|
||||||
if (!url) url = feedElem.getAttribute('url');
|
if (!url) url = feedElem.getAttribute('url');
|
||||||
let name = feedElem.getAttribute('title');
|
let name = feedElem.getAttribute('title')
|
||||||
if (!name) name = feedElem.getAttribute('text');
|
|| feedElem.getAttribute('text')
|
||||||
|
|| Zotero.getString('pane.collections.untitled');
|
||||||
if (Zotero.Feeds.existsByURL(url) || registeredUrls.has(url)) {
|
if (Zotero.Feeds.existsByURL(url) || registeredUrls.has(url)) {
|
||||||
Zotero.debug("Feed Import from OPML: Feed " + name + " : " + url + " already exists. Skipping");
|
Zotero.debug("Feed Import from OPML: Feed " + name + " : " + url + " already exists. Skipping");
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,16 @@ Zotero.defineProperty(Zotero.Item.prototype, 'parentItemKey', {
|
||||||
Zotero.defineProperty(Zotero.Item.prototype, 'parentItem', {
|
Zotero.defineProperty(Zotero.Item.prototype, 'parentItem', {
|
||||||
get: function() { return Zotero.Items.get(this.parentID) || undefined; },
|
get: function() { return Zotero.Items.get(this.parentID) || undefined; },
|
||||||
});
|
});
|
||||||
|
Zotero.defineProperty(Zotero.Item.prototype, 'topLevelItem', {
|
||||||
|
get: function () {
|
||||||
|
var item = this; // eslint-disable-line consistent-this
|
||||||
|
var parentItem;
|
||||||
|
while ((parentItem = item.parentItem)) {
|
||||||
|
item = parentItem;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Zotero.defineProperty(Zotero.Item.prototype, 'firstCreator', {
|
Zotero.defineProperty(Zotero.Item.prototype, 'firstCreator', {
|
||||||
get: function() { return this._firstCreator; }
|
get: function() { return this._firstCreator; }
|
||||||
|
|
@ -768,7 +777,7 @@ Zotero.Item.prototype.setField = function(field, value, loadIn) {
|
||||||
|
|
||||||
if (value !== false && !Zotero.ItemFields.isValidForType(fieldID, itemTypeID)) {
|
if (value !== false && !Zotero.ItemFields.isValidForType(fieldID, itemTypeID)) {
|
||||||
let msg = "'" + field + "' is not a valid field for type '"
|
let msg = "'" + field + "' is not a valid field for type '"
|
||||||
+ Zotero.ItemFields.getName(itemTypeID) + "'";
|
+ Zotero.ItemTypes.getName(itemTypeID) + "'";
|
||||||
|
|
||||||
if (loadIn) {
|
if (loadIn) {
|
||||||
Zotero.debug(msg + " -- ignoring value '" + value + "'", 2);
|
Zotero.debug(msg + " -- ignoring value '" + value + "'", 2);
|
||||||
|
|
@ -1897,7 +1906,7 @@ Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
|
||||||
throw new Error("Annotation parent must be a file attachment");
|
throw new Error("Annotation parent must be a file attachment");
|
||||||
}
|
}
|
||||||
if (parentItem.attachmentContentType != 'application/pdf') {
|
if (parentItem.attachmentContentType != 'application/pdf') {
|
||||||
throw new Error("Annotation parent must be a PDF");
|
//throw new Error("Annotation parent must be a PDF");
|
||||||
}
|
}
|
||||||
let type = this._getLatestField('annotationType');
|
let type = this._getLatestField('annotationType');
|
||||||
let typeID = Zotero.Annotations[`ANNOTATION_TYPE_${type.toUpperCase()}`];
|
let typeID = Zotero.Annotations[`ANNOTATION_TYPE_${type.toUpperCase()}`];
|
||||||
|
|
@ -1926,7 +1935,7 @@ Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
|
||||||
authorName || null,
|
authorName || null,
|
||||||
text || null,
|
text || null,
|
||||||
comment || null,
|
comment || null,
|
||||||
color || null,
|
color || Zotero.Annotations.DEFAULT_COLOR,
|
||||||
pageLabel || null,
|
pageLabel || null,
|
||||||
sortIndex,
|
sortIndex,
|
||||||
position,
|
position,
|
||||||
|
|
@ -2231,6 +2240,10 @@ Zotero.Item.prototype.setNote = function(text) {
|
||||||
throw ("updateNote() can only be called on notes and attachments");
|
throw ("updateNote() can only be called on notes and attachments");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.isEmbeddedImageAttachment()) {
|
||||||
|
throw new Error("setNote() cannot be called on embedded-image attachments");
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof text != 'string') {
|
if (typeof text != 'string') {
|
||||||
throw ("text must be a string in Zotero.Item.setNote() (was " + typeof text + ")");
|
throw ("text must be a string in Zotero.Item.setNote() (was " + typeof text + ")");
|
||||||
}
|
}
|
||||||
|
|
@ -3396,7 +3409,7 @@ Zotero.Item.prototype.getAttachmentLastPageIndex = function () {
|
||||||
|
|
||||||
var id = this._getLastPageIndexSettingKey();
|
var id = this._getLastPageIndexSettingKey();
|
||||||
var val = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, id);
|
var val = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, id);
|
||||||
if (val !== null && (typeof val != 'number' || val != parseInt(val))) {
|
if (this.isPDFAttachment() && val !== null && (typeof val != 'number' || val != parseInt(val))) {
|
||||||
Zotero.logError(`Setting contains an invalid attachment page index ('${val}') -- discarding`);
|
Zotero.logError(`Setting contains an invalid attachment page index ('${val}') -- discarding`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -3408,7 +3421,7 @@ Zotero.Item.prototype.setAttachmentLastPageIndex = async function (val) {
|
||||||
throw new Error("setAttachmentLastPageIndex() can only be called on file attachments");
|
throw new Error("setAttachmentLastPageIndex() can only be called on file attachments");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof val != 'number' || val != parseInt(val)) {
|
if (this.isPDFAttachment() && (typeof val != 'number' || val != parseInt(val))) {
|
||||||
Zotero.debug(val, 2);
|
Zotero.debug(val, 2);
|
||||||
throw new Error(`setAttachmentLastPageIndex() must be passed an integer`);
|
throw new Error(`setAttachmentLastPageIndex() must be passed an integer`);
|
||||||
}
|
}
|
||||||
|
|
@ -3713,7 +3726,7 @@ Zotero.Item.prototype.getBestAttachments = Zotero.Promise.coroutine(function* ()
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return state of best attachment
|
* Return state of best attachment (or this item if it's a standalone attachment)
|
||||||
*
|
*
|
||||||
* @return {Promise<Object>} - Promise for object with string 'type' ('none'|'pdf'|'snapshot'|'other')
|
* @return {Promise<Object>} - Promise for object with string 'type' ('none'|'pdf'|'snapshot'|'other')
|
||||||
* and boolean 'exists'
|
* and boolean 'exists'
|
||||||
|
|
@ -3722,7 +3735,9 @@ Zotero.Item.prototype.getBestAttachmentState = async function () {
|
||||||
if (this._bestAttachmentState !== null) {
|
if (this._bestAttachmentState !== null) {
|
||||||
return this._bestAttachmentState;
|
return this._bestAttachmentState;
|
||||||
}
|
}
|
||||||
var item = await this.getBestAttachment();
|
var item = this.isAttachment() && this.isTopLevelItem()
|
||||||
|
? this
|
||||||
|
: await this.getBestAttachment();
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return this._bestAttachmentState = {
|
return this._bestAttachmentState = {
|
||||||
type: 'none'
|
type: 'none'
|
||||||
|
|
@ -3800,7 +3815,7 @@ for (let name of ['type', 'authorName', 'text', 'comment', 'color', 'pageLabel',
|
||||||
if (currentType && currentType != value) {
|
if (currentType && currentType != value) {
|
||||||
throw new Error("Cannot change annotation type");
|
throw new Error("Cannot change annotation type");
|
||||||
}
|
}
|
||||||
if (!['highlight', 'note', 'image', 'ink'].includes(value)) {
|
if (!['highlight', 'underline', 'note', 'text', 'image', 'ink'].includes(value)) {
|
||||||
let e = new Error(`Unknown annotation type '${value}'`);
|
let e = new Error(`Unknown annotation type '${value}'`);
|
||||||
e.name = "ZoteroInvalidDataError";
|
e.name = "ZoteroInvalidDataError";
|
||||||
throw e;
|
throw e;
|
||||||
|
|
@ -3808,8 +3823,8 @@ for (let name of ['type', 'authorName', 'text', 'comment', 'color', 'pageLabel',
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'text':
|
case 'text':
|
||||||
if (this._getLatestField('annotationType') != 'highlight') {
|
if (!['highlight', 'underline'].includes(this._getLatestField('annotationType'))) {
|
||||||
throw new Error("'annotationText' can only be set for highlight annotations");
|
throw new Error("'annotationText' can only be set for highlight and underline annotations");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -3823,7 +3838,7 @@ for (let name of ['type', 'authorName', 'text', 'comment', 'color', 'pageLabel',
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'sortIndex':
|
case 'sortIndex':
|
||||||
if (!/^\d{5}\|\d{6}\|\d{5}$/.test(value)) {
|
if (!/^(\d{5}\|\d{6}\|\d{5}|\d{5}\|\d{8}|\d{8})$/.test(value)) {
|
||||||
throw new Error(`Invalid sortIndex '${value}'`);
|
throw new Error(`Invalid sortIndex '${value}'`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -3949,10 +3964,35 @@ Zotero.Item.prototype.getAnnotations = function (includeTrashed) {
|
||||||
}
|
}
|
||||||
var ids = rows.map(row => row.itemID);
|
var ids = rows.map(row => row.itemID);
|
||||||
this._annotations[cacheKey] = ids;
|
this._annotations[cacheKey] = ids;
|
||||||
return Zotero.Items.get(ids);
|
return Zotero.Items.get(ids)
|
||||||
|
// Filter out underline and text annotations in Zotero 6
|
||||||
|
.filter(x => !['underline', 'text'].includes(x.annotationType));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the item is a PDF attachment that exists on disk and contains
|
||||||
|
* embedded markup annotations.
|
||||||
|
*
|
||||||
|
* @return {Promise<Boolean>}
|
||||||
|
*/
|
||||||
|
Zotero.Item.prototype.hasEmbeddedAnnotations = async function () {
|
||||||
|
if (!this.isPDFAttachment()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = await this.getFilePathAsync();
|
||||||
|
if (!path) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let contents = await Zotero.File.getContentsAsync(path);
|
||||||
|
// Check for "markup" annotations per the PDF spec
|
||||||
|
// https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf, p. 390
|
||||||
|
let re = /\s\/Subtype\s+\/(Text|FreeText|Line|Square|Circle|Polygon|PolyLine|Highlight|Underline|Squiggly|StrikeOut|Stamp|Caret|Ink|FileAttachment|Sound|Redact)\s/;
|
||||||
|
return re.test(contents);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Methods dealing with item tags
|
// Methods dealing with item tags
|
||||||
|
|
@ -5009,7 +5049,6 @@ Zotero.Item.prototype.fromJSON = function (json, options = {}) {
|
||||||
// Annotation fields
|
// Annotation fields
|
||||||
//
|
//
|
||||||
case 'annotationType':
|
case 'annotationType':
|
||||||
case 'annotationType':
|
|
||||||
case 'annotationAuthorName':
|
case 'annotationAuthorName':
|
||||||
case 'annotationText':
|
case 'annotationText':
|
||||||
case 'annotationComment':
|
case 'annotationComment':
|
||||||
|
|
@ -5189,7 +5228,7 @@ Zotero.Item.prototype.fromJSON = function (json, options = {}) {
|
||||||
let parentKey = json.parentItem;
|
let parentKey = json.parentItem;
|
||||||
this.parentKey = parentKey ? parentKey : false;
|
this.parentKey = parentKey ? parentKey : false;
|
||||||
|
|
||||||
if (!this.isAnnotation()) {
|
if (!this.isAnnotation() && !this.isEmbeddedImageAttachment()) {
|
||||||
let note = json.note;
|
let note = json.note;
|
||||||
this.setNote(note !== undefined ? note : "");
|
this.setNote(note !== undefined ? note : "");
|
||||||
}
|
}
|
||||||
|
|
@ -5288,7 +5327,7 @@ Zotero.Item.prototype.toJSON = function (options = {}) {
|
||||||
let type = this.annotationType;
|
let type = this.annotationType;
|
||||||
obj.annotationType = type;
|
obj.annotationType = type;
|
||||||
obj.annotationAuthorName = this.annotationAuthorName || '';
|
obj.annotationAuthorName = this.annotationAuthorName || '';
|
||||||
if (type == 'highlight') {
|
if (['highlight', 'underline'].includes(type)) {
|
||||||
obj.annotationText = this.annotationText || '';
|
obj.annotationText = this.annotationText || '';
|
||||||
}
|
}
|
||||||
obj.annotationComment = this.annotationComment || '';
|
obj.annotationComment = this.annotationComment || '';
|
||||||
|
|
@ -5408,7 +5447,16 @@ Zotero.Item.prototype.migrateExtraFields = function () {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var { itemType, fields, creators, extra } = Zotero.Utilities.Internal.extractExtraFields(
|
var { itemType, fields, creators, extra } = Zotero.Utilities.Internal.extractExtraFields(
|
||||||
originalExtra, this
|
originalExtra,
|
||||||
|
this,
|
||||||
|
[
|
||||||
|
// Skip 'publisher-place' and 'event-place' for now, since the mappings will be changed
|
||||||
|
// https://github.com/citation-style-language/zotero-bits/issues/6
|
||||||
|
'place',
|
||||||
|
// Skip 'issued' for now, since we don't support date ranges in Date
|
||||||
|
// https://github.com/zotero/zotero/issues/3030
|
||||||
|
'date'
|
||||||
|
]
|
||||||
);
|
);
|
||||||
if (itemType) {
|
if (itemType) {
|
||||||
let originalType = this.itemTypeID;
|
let originalType = this.itemTypeID;
|
||||||
|
|
|
||||||
|
|
@ -76,11 +76,9 @@ Zotero.ItemFields = new function() {
|
||||||
var baseFields = yield Zotero.DB.columnQueryAsync(sql);
|
var baseFields = yield Zotero.DB.columnQueryAsync(sql);
|
||||||
|
|
||||||
for (let field of fields) {
|
for (let field of fields) {
|
||||||
let isBaseField = baseFields.includes(field.fieldID);
|
|
||||||
let label = field.label || Zotero.Schema.globalSchemaLocale.fields[field.fieldName];
|
let label = field.label || Zotero.Schema.globalSchemaLocale.fields[field.fieldName];
|
||||||
// If string not available, use the field name, except for some base fields that aren't
|
// If string not available, use the field name
|
||||||
// used in the UI and therefore aren't localized
|
if (!label) {
|
||||||
if (!label && !['number', 'type', 'medium'].includes(field.fieldName)) {
|
|
||||||
Zotero.logError(`Localized string not available for field '${field.fieldName}'`);
|
Zotero.logError(`Localized string not available for field '${field.fieldName}'`);
|
||||||
label = Zotero.Utilities.Internal.camelToTitleCase(field.fieldName);
|
label = Zotero.Utilities.Internal.camelToTitleCase(field.fieldName);
|
||||||
}
|
}
|
||||||
|
|
@ -347,7 +345,11 @@ Zotero.ItemFields = new function() {
|
||||||
|
|
||||||
|
|
||||||
this.isAutocompleteField = function (field) {
|
this.isAutocompleteField = function (field) {
|
||||||
field = this.getName(field);
|
var fieldName = this.getName(field);
|
||||||
|
if (!fieldName) {
|
||||||
|
Zotero.logError(`Can't check autocomplete for invalid field '${field}'`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var autoCompleteFields = [
|
var autoCompleteFields = [
|
||||||
'journalAbbreviation',
|
'journalAbbreviation',
|
||||||
|
|
@ -377,7 +379,7 @@ Zotero.ItemFields = new function() {
|
||||||
autoCompleteFields = autoCompleteFields.concat(add);
|
autoCompleteFields = autoCompleteFields.concat(add);
|
||||||
}
|
}
|
||||||
|
|
||||||
return autoCompleteFields.indexOf(field) != -1;
|
return autoCompleteFields.includes(fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -500,55 +500,76 @@ Zotero.Items = function() {
|
||||||
+ "FROM items JOIN itemAnnotations IA USING (itemID) "
|
+ "FROM items JOIN itemAnnotations IA USING (itemID) "
|
||||||
+ "WHERE libraryID=?" + idSQL;
|
+ "WHERE libraryID=?" + idSQL;
|
||||||
var params = [libraryID];
|
var params = [libraryID];
|
||||||
await Zotero.DB.queryAsync(
|
|
||||||
sql,
|
|
||||||
params,
|
|
||||||
{
|
|
||||||
noCache: true,
|
|
||||||
onRow: function (row) {
|
|
||||||
let itemID = row.getResultByIndex(0);
|
|
||||||
|
|
||||||
let item = this._objectCache[itemID];
|
// TEMP: Fix faulty upgrade from early 6.0 beta
|
||||||
if (!item) {
|
// https://github.com/zotero/zotero/issues/3013
|
||||||
throw new Error("Item " + itemID + " not found");
|
try {
|
||||||
}
|
await Zotero.DB.queryAsync(
|
||||||
|
sql,
|
||||||
|
params,
|
||||||
|
{
|
||||||
|
noCache: true,
|
||||||
|
onRow: function (row) {
|
||||||
|
let itemID = row.getResultByIndex(0);
|
||||||
|
|
||||||
item._parentItemID = row.getResultByIndex(1);
|
let item = this._objectCache[itemID];
|
||||||
var typeID = row.getResultByIndex(2);
|
if (!item) {
|
||||||
var type;
|
throw new Error("Item " + itemID + " not found");
|
||||||
switch (typeID) {
|
}
|
||||||
case Zotero.Annotations.ANNOTATION_TYPE_HIGHLIGHT:
|
|
||||||
type = 'highlight';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Zotero.Annotations.ANNOTATION_TYPE_NOTE:
|
item._parentItemID = row.getResultByIndex(1);
|
||||||
type = 'note';
|
var typeID = row.getResultByIndex(2);
|
||||||
break;
|
var type;
|
||||||
|
switch (typeID) {
|
||||||
|
case Zotero.Annotations.ANNOTATION_TYPE_HIGHLIGHT:
|
||||||
|
type = 'highlight';
|
||||||
|
break;
|
||||||
|
|
||||||
case Zotero.Annotations.ANNOTATION_TYPE_IMAGE:
|
case Zotero.Annotations.ANNOTATION_TYPE_UNDERLINE:
|
||||||
type = 'image';
|
type = 'underline';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Zotero.Annotations.ANNOTATION_TYPE_INK:
|
case Zotero.Annotations.ANNOTATION_TYPE_NOTE:
|
||||||
type = 'ink';
|
type = 'note';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
case Zotero.Annotations.ANNOTATION_TYPE_TEXT:
|
||||||
throw new Error(`Unknown annotation type id ${typeID}`);
|
type = 'text';
|
||||||
}
|
break;
|
||||||
item._annotationType = type;
|
|
||||||
item._annotationAuthorName = row.getResultByIndex(3);
|
|
||||||
item._annotationText = row.getResultByIndex(4);
|
|
||||||
item._annotationComment = row.getResultByIndex(5);
|
|
||||||
item._annotationColor = row.getResultByIndex(6);
|
|
||||||
item._annotationSortIndex = row.getResultByIndex(7);
|
|
||||||
item._annotationIsExternal = !!row.getResultByIndex(8);
|
|
||||||
|
|
||||||
item._loaded.annotation = true;
|
case Zotero.Annotations.ANNOTATION_TYPE_IMAGE:
|
||||||
item._clearChanged('annotation');
|
type = 'image';
|
||||||
}.bind(this)
|
break;
|
||||||
|
|
||||||
|
case Zotero.Annotations.ANNOTATION_TYPE_INK:
|
||||||
|
type = 'ink';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown annotation type id ${typeID}`);
|
||||||
|
}
|
||||||
|
item._annotationType = type;
|
||||||
|
item._annotationAuthorName = row.getResultByIndex(3);
|
||||||
|
item._annotationText = row.getResultByIndex(4);
|
||||||
|
item._annotationComment = row.getResultByIndex(5);
|
||||||
|
item._annotationColor = row.getResultByIndex(6);
|
||||||
|
item._annotationSortIndex = row.getResultByIndex(7);
|
||||||
|
item._annotationIsExternal = !!row.getResultByIndex(8);
|
||||||
|
|
||||||
|
item._loaded.annotation = true;
|
||||||
|
item._clearChanged('annotation');
|
||||||
|
}.bind(this)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
if (e.message.includes('no such column: IA.authorName')
|
||||||
|
&& await Zotero.DB.valueQueryAsync("SELECT COUNT(*) FROM version WHERE schema='userdata' AND version IN (120, 121)")) {
|
||||||
|
await Zotero.DB.queryAsync("UPDATE version SET version=119 WHERE schema='userdata'");
|
||||||
|
Zotero.crash();
|
||||||
}
|
}
|
||||||
);
|
throw e;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1035,6 +1056,29 @@ Zotero.Items = function() {
|
||||||
for (let otherItem of otherItems) {
|
for (let otherItem of otherItems) {
|
||||||
let mergedMasterAttachments = new Set();
|
let mergedMasterAttachments = new Set();
|
||||||
|
|
||||||
|
let doMerge = async (fromAttachment, toAttachment) => {
|
||||||
|
mergedMasterAttachments.add(toAttachment.id);
|
||||||
|
|
||||||
|
await this.moveChildItems(fromAttachment, toAttachment, true);
|
||||||
|
await this._moveEmbeddedNote(fromAttachment, toAttachment);
|
||||||
|
await this._moveRelations(fromAttachment, toAttachment);
|
||||||
|
|
||||||
|
fromAttachment.deleted = true;
|
||||||
|
await fromAttachment.save();
|
||||||
|
|
||||||
|
// Later on, when processing notes, we'll use this to remap
|
||||||
|
// URLs pointing to the old attachment.
|
||||||
|
remapAttachmentKeys.set(fromAttachment.key, toAttachment.key);
|
||||||
|
|
||||||
|
// Items can only have one replaced item predicate
|
||||||
|
if (!toAttachment.getRelationsByPredicate(Zotero.Relations.replacedItemPredicate)) {
|
||||||
|
toAttachment.addRelation(Zotero.Relations.replacedItemPredicate,
|
||||||
|
Zotero.URI.getItemURI(fromAttachment));
|
||||||
|
}
|
||||||
|
|
||||||
|
await toAttachment.save();
|
||||||
|
};
|
||||||
|
|
||||||
for (let otherAttachment of await this.getAsync(otherItem.getAttachments(true))) {
|
for (let otherAttachment of await this.getAsync(otherItem.getAttachments(true))) {
|
||||||
if (!otherAttachment.isPDFAttachment()) {
|
if (!otherAttachment.isPDFAttachment()) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1065,13 +1109,12 @@ Zotero.Items = function() {
|
||||||
await otherAttachment.save();
|
await otherAttachment.save();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
mergedMasterAttachments.add(masterAttachmentID);
|
|
||||||
|
|
||||||
let masterAttachment = await this.getAsync(masterAttachmentID);
|
let masterAttachment = await this.getAsync(masterAttachmentID);
|
||||||
|
|
||||||
if (masterAttachment.attachmentContentType !== otherAttachment.attachmentContentType) {
|
if (masterAttachment.attachmentContentType !== otherAttachment.attachmentContentType) {
|
||||||
Zotero.debug(`Master attachment ${masterAttachment.key} matches ${otherAttachment.key}, `
|
Zotero.debug(`Master attachment ${masterAttachment.key} matches ${otherAttachment.key}, `
|
||||||
+ 'but content types differ - moving');
|
+ 'but content types differ - keeping both');
|
||||||
otherAttachment.parentItemID = item.id;
|
otherAttachment.parentItemID = item.id;
|
||||||
await otherAttachment.save();
|
await otherAttachment.save();
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1080,31 +1123,35 @@ Zotero.Items = function() {
|
||||||
if (!((masterAttachment.isImportedAttachment() && otherAttachment.isImportedAttachment())
|
if (!((masterAttachment.isImportedAttachment() && otherAttachment.isImportedAttachment())
|
||||||
|| (masterAttachment.isLinkedFileAttachment() && otherAttachment.isLinkedFileAttachment()))) {
|
|| (masterAttachment.isLinkedFileAttachment() && otherAttachment.isLinkedFileAttachment()))) {
|
||||||
Zotero.debug(`Master attachment ${masterAttachment.key} matches ${otherAttachment.key}, `
|
Zotero.debug(`Master attachment ${masterAttachment.key} matches ${otherAttachment.key}, `
|
||||||
+ 'but link modes differ - moving');
|
+ 'but link modes differ - keeping both');
|
||||||
otherAttachment.parentItemID = item.id;
|
otherAttachment.parentItemID = item.id;
|
||||||
await otherAttachment.save();
|
await otherAttachment.save();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.debug(`Master attachment ${masterAttachment.key} matches ${otherAttachment.key} - merging`);
|
// Check whether master and other have embedded annotations
|
||||||
await this.moveChildItems(otherAttachment, masterAttachment, true);
|
// Master yes, other yes -> keep both
|
||||||
await this._moveEmbeddedNote(otherAttachment, masterAttachment);
|
// Master yes, other no -> keep master
|
||||||
await this._moveRelations(otherAttachment, masterAttachment);
|
// Master no, other yes -> keep other
|
||||||
|
if (await otherAttachment.hasEmbeddedAnnotations()) {
|
||||||
otherAttachment.deleted = true;
|
if (await masterAttachment.hasEmbeddedAnnotations()) {
|
||||||
await otherAttachment.save();
|
Zotero.debug(`Master attachment ${masterAttachment.key} matches ${otherAttachment.key}, `
|
||||||
|
+ 'but both have embedded annotations - keeping both');
|
||||||
// Later on, when processing notes, we'll use this to remap
|
otherAttachment.parentItemID = item.id;
|
||||||
// URLs pointing to the old attachment.
|
await otherAttachment.save();
|
||||||
remapAttachmentKeys.set(otherAttachment.key, masterAttachment.key);
|
}
|
||||||
|
else {
|
||||||
// Items can only have one replaced item predicate
|
Zotero.debug(`Master attachment ${masterAttachment.key} matches ${otherAttachment.key}, `
|
||||||
if (!masterAttachment.getRelationsByPredicate(Zotero.Relations.replacedItemPredicate)) {
|
+ 'but other has embedded annotations - merging into other');
|
||||||
masterAttachment.addRelation(Zotero.Relations.replacedItemPredicate,
|
await doMerge(masterAttachment, otherAttachment);
|
||||||
Zotero.URI.getItemURI(otherAttachment));
|
otherAttachment.parentItemID = item.id;
|
||||||
|
await otherAttachment.save();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
await masterAttachment.save();
|
Zotero.debug(`Master attachment ${masterAttachment.key} matches ${otherAttachment.key} - merging into master`);
|
||||||
|
await doMerge(otherAttachment, masterAttachment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1477,6 +1524,7 @@ Zotero.Items = function() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Zotero.debug("Emptied " + deleted.length + " item(s) from trash in " + (new Date() - t) + " ms");
|
Zotero.debug("Emptied " + deleted.length + " item(s) from trash in " + (new Date() - t) + " ms");
|
||||||
|
Zotero.Notifier.trigger('refresh', 'trash', libraryID);
|
||||||
}
|
}
|
||||||
|
|
||||||
return deleted.length;
|
return deleted.length;
|
||||||
|
|
@ -1709,22 +1757,40 @@ Zotero.Items = function() {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of items with children of selected parents removed
|
* Get the top-level items of all passed items
|
||||||
*
|
*
|
||||||
|
* @param {Zotero.Item[]} items
|
||||||
* @return {Zotero.Item[]}
|
* @return {Zotero.Item[]}
|
||||||
*/
|
*/
|
||||||
this.keepParents = function (items) {
|
this.getTopLevel = function (items) {
|
||||||
var parentItems = new Set(
|
return [...new Set(items.map(item => item.topLevelItem))];
|
||||||
items
|
};
|
||||||
.filter(item => item.isTopLevelItem())
|
|
||||||
.map(item => item.id)
|
|
||||||
|
/**
|
||||||
|
* Return an array of items with descendants of selected top-level items removed
|
||||||
|
*
|
||||||
|
* Non-top-level items that aren't descendents of selected items are kept.
|
||||||
|
*
|
||||||
|
* @param {Zotero.Item[]}
|
||||||
|
* @return {Zotero.Item[]}
|
||||||
|
*/
|
||||||
|
this.keepTopLevel = function (items) {
|
||||||
|
var topLevelItems = new Set(
|
||||||
|
items.filter(item => item.isTopLevelItem())
|
||||||
);
|
);
|
||||||
return items.filter(item => {
|
return items.filter((item) => {
|
||||||
var parentItemID = item.parentItemID;
|
var topLevelItem = !item.isTopLevelItem() && item.topLevelItem;
|
||||||
// Not a child item or not a child of one of the passed items
|
// Not a child item or not a child of one of the passed items
|
||||||
return !parentItemID || !parentItems.has(parentItemID);
|
return !topLevelItem || !topLevelItems.has(topLevelItem);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.keepParents = function (items) {
|
||||||
|
Zotero.debug("Zotero.Items.keepParents() is deprecated -- use Zotero.Items.keepTopLevel() instead");
|
||||||
|
return this.keepTopLevel(items);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -2013,6 +2079,28 @@ Zotero.Items = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find attachment items whose paths begin with the passed `pathPrefix` and don't exist on disk
|
||||||
|
*
|
||||||
|
* @param {Number} libraryID
|
||||||
|
* @param {String} pathPrefix
|
||||||
|
* @return {Zotero.Item[]}
|
||||||
|
*/
|
||||||
|
this.findMissingLinkedFiles = async function (libraryID, pathPrefix) {
|
||||||
|
let sql = "SELECT itemID FROM items JOIN itemAttachments USING (itemID) "
|
||||||
|
+ "WHERE itemID NOT IN (SELECT itemID FROM deletedItems) "
|
||||||
|
+ `AND linkMode=${Zotero.Attachments.LINK_MODE_LINKED_FILE} `
|
||||||
|
+ "AND path LIKE ? ESCAPE '\\' "
|
||||||
|
+ "AND libraryID=?";
|
||||||
|
let ids = await Zotero.DB.columnQueryAsync(sql, [Zotero.DB.escapeSQLExpression(pathPrefix) + '%', libraryID]);
|
||||||
|
let items = await this.getAsync(ids);
|
||||||
|
let missingItems = await Promise.all(
|
||||||
|
items.map(async item => (await item.fileExists() ? false : item))
|
||||||
|
);
|
||||||
|
return missingItems.filter(Boolean);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
Zotero.DataObjects.call(this);
|
Zotero.DataObjects.call(this);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,12 @@ Zotero.defineProperty(Zotero.Search.prototype, 'key', {
|
||||||
});
|
});
|
||||||
Zotero.defineProperty(Zotero.Search.prototype, 'name', {
|
Zotero.defineProperty(Zotero.Search.prototype, 'name', {
|
||||||
get: function() { return this._get('name'); },
|
get: function() { return this._get('name'); },
|
||||||
set: function(val) { return this._set('name', val); }
|
set: function (val) {
|
||||||
|
if (!val) {
|
||||||
|
throw new Error("Saved search name cannot be empty");
|
||||||
|
}
|
||||||
|
return this._set('name', val);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
Zotero.defineProperty(Zotero.Search.prototype, 'version', {
|
Zotero.defineProperty(Zotero.Search.prototype, 'version', {
|
||||||
get: function() { return this._get('version'); },
|
get: function() { return this._get('version'); },
|
||||||
|
|
@ -948,7 +953,11 @@ Zotero.Search.idsToTempTable = Zotero.Promise.coroutine(function* (ids) {
|
||||||
Zotero.Search.prototype._buildQuery = Zotero.Promise.coroutine(function* () {
|
Zotero.Search.prototype._buildQuery = Zotero.Promise.coroutine(function* () {
|
||||||
this._requireData('conditions');
|
this._requireData('conditions');
|
||||||
|
|
||||||
var sql = 'SELECT itemID FROM items';
|
// TEMP: Match parent attachment for annotation matches
|
||||||
|
// var sql = 'SELECT itemID FROM items';
|
||||||
|
var sql = "SELECT COALESCE(IA.parentItemID, itemID) AS itemID FROM items "
|
||||||
|
+ "LEFT JOIN itemAnnotations IA USING (itemID)";
|
||||||
|
|
||||||
var sqlParams = [];
|
var sqlParams = [];
|
||||||
// Separate ANY conditions for 'required' condition support
|
// Separate ANY conditions for 'required' condition support
|
||||||
var anySQL = '';
|
var anySQL = '';
|
||||||
|
|
@ -957,12 +966,14 @@ Zotero.Search.prototype._buildQuery = Zotero.Promise.coroutine(function* () {
|
||||||
var conditions = [];
|
var conditions = [];
|
||||||
|
|
||||||
let lastCondition;
|
let lastCondition;
|
||||||
for (let condition of Object.values(this._conditions)) {
|
let conditionsToProcess = Object.values(this._conditions);
|
||||||
|
for (let condition of conditionsToProcess) {
|
||||||
let name = condition.condition;
|
let name = condition.condition;
|
||||||
let conditionData = Zotero.SearchConditions.get(name);
|
let conditionData = Zotero.SearchConditions.get(name);
|
||||||
|
|
||||||
// Has a table (or 'savedSearch', which doesn't have a table but isn't special)
|
// Has a table (or 'savedSearch', which doesn't have a table but isn't special)
|
||||||
if (conditionData.table || name == 'savedSearch' || name == 'tempTable') {
|
// TEMP: Or 'tag', which needs to match annotation parents
|
||||||
|
if (conditionData.table || name == 'savedSearch' || name == 'tempTable' || name == 'tag') {
|
||||||
// For conditions with an inline filter using 'is'/'isNot', combine with last condition
|
// For conditions with an inline filter using 'is'/'isNot', combine with last condition
|
||||||
// if the same
|
// if the same
|
||||||
if (lastCondition
|
if (lastCondition
|
||||||
|
|
@ -1050,6 +1061,39 @@ Zotero.Search.prototype._buildQuery = Zotero.Promise.coroutine(function* () {
|
||||||
case 'blockEnd':
|
case 'blockEnd':
|
||||||
conditions.push({name:'blockEnd'});
|
conditions.push({name:'blockEnd'});
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
case 'anyField':
|
||||||
|
// We expand this condition to the same underlying set of conditions as 'quicksearch-fields'
|
||||||
|
// (although we don't detect keys or split into quoted and unquoted segments). 'quicksearch-fields'
|
||||||
|
// is expanded in addCondition(), but we can't do that with this condition because we don't want
|
||||||
|
// to save the conditions it expands to in the search object
|
||||||
|
conditionsToProcess.push({ condition: 'blockStart' });
|
||||||
|
conditionsToProcess.push({
|
||||||
|
condition: 'field',
|
||||||
|
operator: condition.operator,
|
||||||
|
value: condition.value,
|
||||||
|
required: false
|
||||||
|
});
|
||||||
|
conditionsToProcess.push({
|
||||||
|
condition: 'tag',
|
||||||
|
operator: condition.operator,
|
||||||
|
value: condition.value,
|
||||||
|
required: false
|
||||||
|
});
|
||||||
|
conditionsToProcess.push({
|
||||||
|
condition: 'note',
|
||||||
|
operator: condition.operator,
|
||||||
|
value: condition.value,
|
||||||
|
required: false
|
||||||
|
});
|
||||||
|
conditionsToProcess.push({
|
||||||
|
condition: 'creator',
|
||||||
|
operator: condition.operator,
|
||||||
|
value: condition.value,
|
||||||
|
required: false
|
||||||
|
});
|
||||||
|
conditionsToProcess.push({ condition: 'blockEnd' });
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('Unhandled special condition ' + name);
|
throw new Error('Unhandled special condition ' + name);
|
||||||
|
|
@ -1058,16 +1102,24 @@ Zotero.Search.prototype._buildQuery = Zotero.Promise.coroutine(function* () {
|
||||||
|
|
||||||
// Exclude deleted items (and their child items) by default
|
// Exclude deleted items (and their child items) by default
|
||||||
let not = deleted ? "" : "NOT ";
|
let not = deleted ? "" : "NOT ";
|
||||||
let op = deleted ? "OR" : "AND";
|
sql += ` WHERE (itemID ${not} IN (`
|
||||||
sql += " WHERE ("
|
// Deleted items
|
||||||
+ `itemID ${not} IN (SELECT itemID FROM deletedItems) `
|
+ "SELECT itemID FROM deletedItems "
|
||||||
+ `${op} itemID ${not}IN (SELECT itemID FROM itemNotes `
|
// Child notes of deleted items
|
||||||
|
+ "UNION SELECT itemID FROM itemNotes "
|
||||||
+ "WHERE parentItemID IS NOT NULL AND "
|
+ "WHERE parentItemID IS NOT NULL AND "
|
||||||
+ "parentItemID IN (SELECT itemID FROM deletedItems)) "
|
+ "parentItemID IN (SELECT itemID FROM deletedItems) "
|
||||||
+ `${op} itemID ${not}IN (SELECT itemID FROM itemAttachments `
|
// Child attachments of deleted items
|
||||||
|
+ "UNION SELECT itemID FROM itemAttachments "
|
||||||
+ "WHERE parentItemID IS NOT NULL AND "
|
+ "WHERE parentItemID IS NOT NULL AND "
|
||||||
+ "parentItemID IN (SELECT itemID FROM deletedItems))"
|
+ "parentItemID IN (SELECT itemID FROM deletedItems)"
|
||||||
+ ")";
|
// Annotations of deleted attachments
|
||||||
|
+ "UNION SELECT itemID FROM itemAnnotations "
|
||||||
|
+ "WHERE parentItemID IN (SELECT itemID FROM deletedItems)"
|
||||||
|
// Annotations of attachments of deleted items
|
||||||
|
+ "UNION SELECT itemID FROM itemAnnotations "
|
||||||
|
+ "WHERE parentItemID IN (SELECT itemID FROM itemAttachments WHERE parentItemID IN (SELECT itemID FROM deletedItems))"
|
||||||
|
+ "))";
|
||||||
|
|
||||||
if (noChildren){
|
if (noChildren){
|
||||||
sql += " AND (itemID NOT IN (SELECT itemID FROM itemNotes "
|
sql += " AND (itemID NOT IN (SELECT itemID FROM itemNotes "
|
||||||
|
|
@ -1077,14 +1129,15 @@ Zotero.Search.prototype._buildQuery = Zotero.Promise.coroutine(function* () {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unfiled) {
|
if (unfiled) {
|
||||||
sql += " AND (itemID NOT IN (SELECT itemID FROM collectionItems) "
|
sql += " AND (itemID NOT IN ("
|
||||||
|
+ "SELECT itemID FROM collectionItems "
|
||||||
// Exclude children
|
// Exclude children
|
||||||
+ "AND itemID NOT IN "
|
+ "UNION SELECT itemID FROM itemAttachments WHERE parentItemID IS NOT NULL "
|
||||||
+ "(SELECT itemID FROM itemAttachments WHERE parentItemID IS NOT NULL "
|
+ "UNION SELECT itemID FROM itemNotes WHERE parentItemID IS NOT NULL "
|
||||||
+ "UNION SELECT itemID FROM itemNotes WHERE parentItemID IS NOT NULL)"
|
+ "UNION SELECT itemID FROM itemAnnotations "
|
||||||
+ ") "
|
|
||||||
// Exclude My Publications
|
// Exclude My Publications
|
||||||
+ "AND itemID NOT IN (SELECT itemID FROM publicationsItems)";
|
+ "UNION SELECT itemID FROM publicationsItems "
|
||||||
|
+ "))";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (retracted) {
|
if (retracted) {
|
||||||
|
|
@ -1119,22 +1172,42 @@ Zotero.Search.prototype._buildQuery = Zotero.Promise.coroutine(function* () {
|
||||||
//
|
//
|
||||||
// Special table handling
|
// Special table handling
|
||||||
//
|
//
|
||||||
if (condition['table']){
|
if (condition.table) {
|
||||||
switch (condition['table']){
|
let negationOperators = ['isNot', 'doesNotContain'];
|
||||||
default:
|
let isNegationOperator = negationOperators.includes(condition.operator);
|
||||||
condSelectSQL += 'itemID '
|
|
||||||
switch (condition['operator']){
|
condSelectSQL += 'itemID '
|
||||||
case 'isNot':
|
if (isNegationOperator) {
|
||||||
case 'doesNotContain':
|
condSelectSQL += 'NOT ';
|
||||||
condSelectSQL += 'NOT ';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
condSelectSQL += 'IN (';
|
|
||||||
selectOpenParens = 1;
|
|
||||||
condSQL += 'SELECT itemID FROM ' +
|
|
||||||
condition['table'] + ' WHERE (';
|
|
||||||
openParens = 1;
|
|
||||||
}
|
}
|
||||||
|
condSelectSQL += 'IN (';
|
||||||
|
selectOpenParens = 1;
|
||||||
|
|
||||||
|
// TEMP: Don't match annotations for negation operators, since it would result in
|
||||||
|
// all parent attachments being returned
|
||||||
|
if (isNegationOperator) {
|
||||||
|
condSelectSQL += "SELECT itemID FROM items WHERE itemTypeID="
|
||||||
|
+ Zotero.ItemTypes.getID('annotation') + " UNION ";
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (condition.name) {
|
||||||
|
// TEMP: Match parent attachments of matching annotations
|
||||||
|
case 'tag':
|
||||||
|
condSQL += "SELECT COALESCE(IAnT.parentItemID, itemID) FROM itemTags "
|
||||||
|
+ "LEFT JOIN itemAnnotations IAnT USING (itemID) WHERE (";
|
||||||
|
break;
|
||||||
|
|
||||||
|
// TEMP: Match parent attachments of matching annotations
|
||||||
|
case 'annotationText':
|
||||||
|
case 'annotationComment':
|
||||||
|
condSQL += `SELECT parentItemID FROM ${condition.table} WHERE (`
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
condSQL += `SELECT itemID FROM ${condition.table} WHERE (`;
|
||||||
|
}
|
||||||
|
|
||||||
|
openParens = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
@ -1709,7 +1782,11 @@ Zotero.Search.prototype._buildQuery = Zotero.Promise.coroutine(function* () {
|
||||||
|
|
||||||
// Add on quicksearch conditions
|
// Add on quicksearch conditions
|
||||||
if (quicksearchSQLSet) {
|
if (quicksearchSQLSet) {
|
||||||
sql = "SELECT itemID FROM items WHERE itemID IN (" + sql + ") "
|
// TEMP: Match parent attachments for annotations
|
||||||
|
//sql = "SELECT itemID FROM items WHERE itemID IN (" + sql + ") "
|
||||||
|
sql = "SELECT COALESCE(IAn.parentItemID, itemID) AS itemID FROM items "
|
||||||
|
+ "LEFT JOIN itemAnnotations IAn USING (itemID) "
|
||||||
|
+ "WHERE itemID IN (" + sql + ") "
|
||||||
+ "AND ((" + quicksearchSQLSet.join(') AND (') + "))";
|
+ "AND ((" + quicksearchSQLSet.join(') AND (') + "))";
|
||||||
|
|
||||||
for (var k=0; k<quicksearchParamsSet.length; k++) {
|
for (var k=0; k<quicksearchParamsSet.length; k++) {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
***** END LICENSE BLOCK *****
|
***** END LICENSE BLOCK *****
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Zotero.SearchConditions = new function(){
|
Zotero.SearchConditions = new function () {
|
||||||
this.get = get;
|
this.get = get;
|
||||||
this.getStandardConditions = getStandardConditions;
|
this.getStandardConditions = getStandardConditions;
|
||||||
this.hasOperator = hasOperator;
|
this.hasOperator = hasOperator;
|
||||||
|
|
@ -452,6 +452,18 @@ Zotero.SearchConditions = new function(){
|
||||||
template: true // mark for special handling
|
template: true // mark for special handling
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'anyField',
|
||||||
|
operators: {
|
||||||
|
is: true,
|
||||||
|
isNot: true,
|
||||||
|
contains: true,
|
||||||
|
doesNotContain: true,
|
||||||
|
beginsWith: true
|
||||||
|
},
|
||||||
|
special: false
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'datefield',
|
name: 'datefield',
|
||||||
operators: {
|
operators: {
|
||||||
|
|
@ -652,6 +664,10 @@ Zotero.SearchConditions = new function(){
|
||||||
|
|
||||||
var collation = Zotero.getLocaleCollation();
|
var collation = Zotero.getLocaleCollation();
|
||||||
_standardConditions.sort(function(a, b) {
|
_standardConditions.sort(function(a, b) {
|
||||||
|
// Sort Any Field to the top
|
||||||
|
if (a.name == 'anyField') {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
return collation.compareString(1, a.localized, b.localized);
|
return collation.compareString(1, a.localized, b.localized);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -163,9 +163,6 @@ Zotero.Tags = new function() {
|
||||||
if (libraryID) {
|
if (libraryID) {
|
||||||
sql += "JOIN items USING (itemID) WHERE libraryID = ? ";
|
sql += "JOIN items USING (itemID) WHERE libraryID = ? ";
|
||||||
params.push(libraryID);
|
params.push(libraryID);
|
||||||
// TEMP: Don't show annotation tags in tag selector
|
|
||||||
sql += "AND itemTypeID != ? ";
|
|
||||||
params.push(Zotero.ItemTypes.getID('annotation'));
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sql += "WHERE 1 ";
|
sql += "WHERE 1 ";
|
||||||
|
|
@ -174,7 +171,11 @@ Zotero.Tags = new function() {
|
||||||
if (libraryID) {
|
if (libraryID) {
|
||||||
throw new Error("tmpTable and libraryID are mutually exclusive");
|
throw new Error("tmpTable and libraryID are mutually exclusive");
|
||||||
}
|
}
|
||||||
sql += "AND itemID IN (SELECT itemID FROM " + tmpTable + ") ";
|
sql += "AND itemID IN (SELECT itemID FROM " + tmpTable;
|
||||||
|
// TEMP: Match parent attachments for annotation tags
|
||||||
|
sql += " UNION SELECT itemID FROM itemAnnotations WHERE parentItemID IN "
|
||||||
|
+ "(SELECT itemID FROM " + tmpTable + ")";
|
||||||
|
sql += ") ";
|
||||||
}
|
}
|
||||||
if (types && types.length) {
|
if (types && types.length) {
|
||||||
sql += "AND type IN (" + new Array(types.length).fill('?').join(', ') + ") ";
|
sql += "AND type IN (" + new Array(types.length).fill('?').join(', ') + ") ";
|
||||||
|
|
|
||||||
|
|
@ -909,39 +909,6 @@ Zotero.DataDirectory = {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for an existing pipe from other running versions of Zotero pointing at the same data
|
|
||||||
// directory, and skip migration if found
|
|
||||||
try {
|
|
||||||
let foundPipe = yield Zotero.IPC.pipeExists();
|
|
||||||
if (foundPipe) {
|
|
||||||
Zotero.debug("Found existing pipe -- skipping migration");
|
|
||||||
|
|
||||||
if (!automatic) {
|
|
||||||
let ps = Services.prompt;
|
|
||||||
let buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
|
|
||||||
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_IS_STRING);
|
|
||||||
let index = ps.confirmEx(null,
|
|
||||||
Zotero.getString('dataDir.migration.failure.title'),
|
|
||||||
Zotero.getString('dataDir.migration.failure.full.firefoxOpen'),
|
|
||||||
buttonFlags,
|
|
||||||
Zotero.getString('general.tryAgain'),
|
|
||||||
Zotero.getString('general.tryLater'),
|
|
||||||
null, null, {}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (index == 0) {
|
|
||||||
return this.checkForMigration(newDir, newDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Zotero.logError("Error checking for pipe -- skipping migration:\n\n" + e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are other profiles pointing to the old directory, make sure we can edit the prefs.js
|
// If there are other profiles pointing to the old directory, make sure we can edit the prefs.js
|
||||||
// file before doing anything, or else we risk orphaning a 4.0 installation
|
// file before doing anything, or else we risk orphaning a 4.0 installation
|
||||||
try {
|
try {
|
||||||
|
|
@ -1043,10 +1010,6 @@ Zotero.DataDirectory = {
|
||||||
// Set data directory again
|
// Set data directory again
|
||||||
Zotero.debug("Using new data directory " + newDir);
|
Zotero.debug("Using new data directory " + newDir);
|
||||||
this._cache(newDir);
|
this._cache(newDir);
|
||||||
// Tell Zotero for Firefox in connector mode to reload and find the new data directory
|
|
||||||
if (Zotero.isStandalone) {
|
|
||||||
Zotero.IPC.broadcast('reinit');
|
|
||||||
}
|
|
||||||
|
|
||||||
// At least the database was copied, but other things failed
|
// At least the database was copied, but other things failed
|
||||||
if (errors.length) {
|
if (errors.length) {
|
||||||
|
|
|
||||||
|
|
@ -1303,7 +1303,6 @@ Zotero.DBConnection.prototype._checkException = async function (e) {
|
||||||
* @return {Boolean} - True if recovered, false if not
|
* @return {Boolean} - True if recovered, false if not
|
||||||
*/
|
*/
|
||||||
Zotero.DBConnection.prototype._handleCorruptionMarker = async function () {
|
Zotero.DBConnection.prototype._handleCorruptionMarker = async function () {
|
||||||
var storage = Services.storage;
|
|
||||||
var file = this._dbPath;
|
var file = this._dbPath;
|
||||||
var fileName = OS.Path.basename(file);
|
var fileName = OS.Path.basename(file);
|
||||||
var backupFile = this._dbPath + '.bak';
|
var backupFile = this._dbPath + '.bak';
|
||||||
|
|
@ -1315,26 +1314,38 @@ Zotero.DBConnection.prototype._handleCorruptionMarker = async function () {
|
||||||
if (!await OS.File.exists(backupFile)) {
|
if (!await OS.File.exists(backupFile)) {
|
||||||
this._debug("No backup file for DB '" + this._dbName + "' exists", 1);
|
this._debug("No backup file for DB '" + this._dbName + "' exists", 1);
|
||||||
|
|
||||||
// Save damaged filed
|
let damagedFile;
|
||||||
this._debug('Saving damaged DB file with .damaged extension', 1);
|
|
||||||
let damagedFile = this._dbPath + '.damaged';
|
// If database file exists, move it to .damaged
|
||||||
damagedFile = await Zotero.File.moveToUnique(file, damagedFile);
|
if (await OS.File.exists(file)) {
|
||||||
|
this._debug('Saving damaged DB file with .damaged extension', 1);
|
||||||
|
damagedFile = this._dbPath + '.damaged';
|
||||||
|
damagedFile = await Zotero.File.moveToUnique(file, damagedFile);
|
||||||
|
}
|
||||||
|
// If it doesn't exist, assume we already showed a warning and moved it
|
||||||
|
else {
|
||||||
|
this._debug(`Database file '${fileName}' doesn't exist!`);
|
||||||
|
}
|
||||||
|
|
||||||
// Create new main database
|
// Create new main database
|
||||||
this._connection = storage.openDatabase(file);
|
this._connection = await Zotero.Promise.resolve(this.Sqlite.openConnection({
|
||||||
|
path: file
|
||||||
|
}));
|
||||||
|
|
||||||
if (await OS.File.exists(corruptMarker)) {
|
if (await OS.File.exists(corruptMarker)) {
|
||||||
await OS.File.remove(corruptMarker);
|
await OS.File.remove(corruptMarker);
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.alert(
|
if (damagedFile) {
|
||||||
null,
|
Zotero.alert(
|
||||||
Zotero.getString('startupError', Zotero.appName),
|
null,
|
||||||
Zotero.getString(
|
Zotero.getString('startupError', Zotero.appName),
|
||||||
'db.dbCorruptedNoBackup',
|
Zotero.getString(
|
||||||
[Zotero.appName, fileName, OS.Path.basename(damagedFile)]
|
'db.dbCorruptedNoBackup',
|
||||||
)
|
[Zotero.appName, fileName, OS.Path.basename(damagedFile)]
|
||||||
);
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -384,12 +384,12 @@ Zotero.Duplicates.prototype._findDuplicates = Zotero.Promise.coroutine(function*
|
||||||
for (let i = 0; i < aCreatorRows.length; i++) {
|
for (let i = 0; i < aCreatorRows.length; i++) {
|
||||||
let aCreatorRow = aCreatorRows[i];
|
let aCreatorRow = aCreatorRows[i];
|
||||||
let aLastName = aCreatorRow.lastName;
|
let aLastName = aCreatorRow.lastName;
|
||||||
let aFirstInitial = aCreatorRow.firstInitial;
|
let aFirstInitial = aCreatorRow.firstInitial || "";
|
||||||
|
|
||||||
for (let j = 0; j < bCreatorRows.length; j++) {
|
for (let j = 0; j < bCreatorRows.length; j++) {
|
||||||
let bCreatorRow = bCreatorRows[j];
|
let bCreatorRow = bCreatorRows[j];
|
||||||
let bLastName = bCreatorRow.lastName;
|
let bLastName = bCreatorRow.lastName;
|
||||||
let bFirstInitial = bCreatorRow.firstInitial;
|
let bFirstInitial = bCreatorRow.firstInitial || "";
|
||||||
|
|
||||||
if (aLastName === bLastName && aFirstInitial === bFirstInitial) {
|
if (aLastName === bLastName && aFirstInitial === bFirstInitial) {
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@
|
||||||
|
|
||||||
Components.utils.import("resource://gre/modules/InlineSpellChecker.jsm");
|
Components.utils.import("resource://gre/modules/InlineSpellChecker.jsm");
|
||||||
|
|
||||||
|
import FilePicker from 'zotero/modules/filePicker';
|
||||||
|
|
||||||
// Note: TinyMCE is automatically doing some meaningless corrections to
|
// Note: TinyMCE is automatically doing some meaningless corrections to
|
||||||
// note-editor produced HTML. Which might result to more
|
// note-editor produced HTML. Which might result to more
|
||||||
// conflicts, especially in group libraries
|
// conflicts, especially in group libraries
|
||||||
|
|
@ -43,9 +45,6 @@ const DOWNLOADED_IMAGE_TYPE = [
|
||||||
'image/png'
|
'image/png'
|
||||||
];
|
];
|
||||||
|
|
||||||
// Schema version here has to be the same as in note-editor!
|
|
||||||
const SCHEMA_VERSION = 8;
|
|
||||||
|
|
||||||
class EditorInstance {
|
class EditorInstance {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.instanceID = Zotero.Utilities.randomString();
|
this.instanceID = Zotero.Utilities.randomString();
|
||||||
|
|
@ -89,6 +88,76 @@ class EditorInstance {
|
||||||
return doc.execCommand(command, ui, value);
|
return doc.execCommand(command, ui, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Translate note HTML into Markdown, for setting it as text/plain in clipboard (on text copy/drag)
|
||||||
|
this._iframeWindow.wrappedJSObject.zoteroTranslateToMarkdown = (html) => {
|
||||||
|
let item = new Zotero.Item('note');
|
||||||
|
item.libraryID = this._item.libraryID;
|
||||||
|
item.setNote(html);
|
||||||
|
let text = '';
|
||||||
|
var translation = new Zotero.Translate.Export;
|
||||||
|
translation.noWait = true;
|
||||||
|
translation.setItems([item]);
|
||||||
|
translation.setTranslator(Zotero.Translators.TRANSLATOR_ID_NOTE_MARKDOWN);
|
||||||
|
translation.setHandler("done", (obj, worked) => {
|
||||||
|
if (worked) {
|
||||||
|
text = obj.string.replace(/\r\n/g, '\n');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
translation.translate();
|
||||||
|
return text;
|
||||||
|
};
|
||||||
|
|
||||||
|
this._iframeWindow.wrappedJSObject.zoteroCopyImage = async (dataURL) => {
|
||||||
|
let parts = dataURL.split(',');
|
||||||
|
if (!parts[0].includes('base64')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mime = parts[0].match(/:(.*?);/)[1];
|
||||||
|
let bstr = atob(parts[1]);
|
||||||
|
let n = bstr.length;
|
||||||
|
let u8arr = new Uint8Array(n);
|
||||||
|
while (n--) {
|
||||||
|
u8arr[n] = bstr.charCodeAt(n);
|
||||||
|
}
|
||||||
|
let imgTools = Components.classes["@mozilla.org/image/tools;1"]
|
||||||
|
.getService(Components.interfaces.imgITools);
|
||||||
|
let transferable = Components.classes['@mozilla.org/widget/transferable;1']
|
||||||
|
.createInstance(Components.interfaces.nsITransferable);
|
||||||
|
let clipboardService = Components.classes['@mozilla.org/widget/clipboard;1']
|
||||||
|
.getService(Components.interfaces.nsIClipboard);
|
||||||
|
let imgPtr = Components.classes["@mozilla.org/supports-interface-pointer;1"]
|
||||||
|
.createInstance(Components.interfaces.nsISupportsInterfacePointer);
|
||||||
|
imgPtr.data = imgTools.decodeImageFromArrayBuffer(u8arr.buffer, mime);
|
||||||
|
transferable.init(null);
|
||||||
|
transferable.addDataFlavor(mime);
|
||||||
|
transferable.setTransferData(mime, imgPtr, 0);
|
||||||
|
clipboardService.setData(transferable, null, Components.interfaces.nsIClipboard.kGlobalClipboard);
|
||||||
|
};
|
||||||
|
|
||||||
|
this._iframeWindow.wrappedJSObject.zoteroSaveImageAs = async (dataURL) => {
|
||||||
|
let parts = dataURL.split(',');
|
||||||
|
if (!parts[0].includes('base64')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mime = parts[0].match(/:(.*?);/)[1];
|
||||||
|
let bstr = atob(parts[1]);
|
||||||
|
let n = bstr.length;
|
||||||
|
let u8arr = new Uint8Array(n);
|
||||||
|
while (n--) {
|
||||||
|
u8arr[n] = bstr.charCodeAt(n);
|
||||||
|
}
|
||||||
|
let ext = Zotero.MIME.getPrimaryExtension(mime, '');
|
||||||
|
let fp = new FilePicker();
|
||||||
|
fp.init(this._iframeWindow, Zotero.getString('noteEditor.saveImageAs'), fp.modeSave);
|
||||||
|
fp.appendFilters(fp.filterImages);
|
||||||
|
fp.defaultString = Zotero.getString('fileTypes.image').toLowerCase() + '.' + ext;
|
||||||
|
let rv = await fp.show();
|
||||||
|
if (rv === fp.returnOK || rv === fp.returnReplace) {
|
||||||
|
let outputPath = fp.file;
|
||||||
|
await OS.File.writeAtomic(outputPath, u8arr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
this._iframeWindow.addEventListener('message', this._messageHandler);
|
this._iframeWindow.addEventListener('message', this._messageHandler);
|
||||||
this._iframeWindow.addEventListener('error', (event) => {
|
this._iframeWindow.addEventListener('error', (event) => {
|
||||||
Zotero.logError(event.error);
|
Zotero.logError(event.error);
|
||||||
|
|
@ -697,6 +766,9 @@ class EditorInstance {
|
||||||
}
|
}
|
||||||
menuitem.setAttribute('checked', item.checked);
|
menuitem.setAttribute('checked', item.checked);
|
||||||
menuitem.addEventListener('command', () => {
|
menuitem.addEventListener('command', () => {
|
||||||
|
if (item.name === 'insertImage') {
|
||||||
|
return this._iframeWindow.eval('openImageFilePicker()');
|
||||||
|
}
|
||||||
this._postMessage({
|
this._postMessage({
|
||||||
action: 'contextMenuAction',
|
action: 'contextMenuAction',
|
||||||
ctxAction: item.name,
|
ctxAction: item.name,
|
||||||
|
|
@ -1193,10 +1265,12 @@ class EditorInstance {
|
||||||
* Create note from annotations
|
* Create note from annotations
|
||||||
*
|
*
|
||||||
* @param {Zotero.Item[]} annotations
|
* @param {Zotero.Item[]} annotations
|
||||||
* @param {Integer} parentID Creates standalone note if not provided
|
* @param {Object} options
|
||||||
|
* @param {Integer} options.parentID - Creates standalone note if not provided
|
||||||
|
* @param {Integer} options.collectionID - Only valid if parentID not provided
|
||||||
* @returns {Promise<Zotero.Item>}
|
* @returns {Promise<Zotero.Item>}
|
||||||
*/
|
*/
|
||||||
static async createNoteFromAnnotations(annotations, parentID) {
|
static async createNoteFromAnnotations(annotations, { parentID, collectionID } = {}) {
|
||||||
if (!annotations.length) {
|
if (!annotations.length) {
|
||||||
throw new Error("No annotations provided");
|
throw new Error("No annotations provided");
|
||||||
}
|
}
|
||||||
|
|
@ -1217,7 +1291,12 @@ class EditorInstance {
|
||||||
|
|
||||||
let note = new Zotero.Item('note');
|
let note = new Zotero.Item('note');
|
||||||
note.libraryID = annotations[0].libraryID;
|
note.libraryID = annotations[0].libraryID;
|
||||||
note.parentID = parentID;
|
if (parentID) {
|
||||||
|
note.parentID = parentID;
|
||||||
|
}
|
||||||
|
else if (collectionID) {
|
||||||
|
note.addToCollection(collectionID);
|
||||||
|
}
|
||||||
await note.saveTx();
|
await note.saveTx();
|
||||||
let editorInstance = new EditorInstance();
|
let editorInstance = new EditorInstance();
|
||||||
editorInstance._item = note;
|
editorInstance._item = note;
|
||||||
|
|
@ -1239,10 +1318,61 @@ class EditorInstance {
|
||||||
html += '\n';
|
html += '\n';
|
||||||
|
|
||||||
await editorInstance.importImages(jsonAnnotations);
|
await editorInstance.importImages(jsonAnnotations);
|
||||||
let { html: serializedHTML, citationItems } = Zotero.EditorInstanceUtilities.serializeAnnotations(jsonAnnotations, true);
|
|
||||||
html += serializedHTML;
|
let multipleParentParent = false;
|
||||||
|
let lastParentParentID;
|
||||||
|
let lastParentID;
|
||||||
|
// Group annotations per attachment
|
||||||
|
let groups = [];
|
||||||
|
for (let i = 0; i < annotations.length; i++) {
|
||||||
|
let annotation = annotations[i];
|
||||||
|
let jsonAnnotation = jsonAnnotations[i];
|
||||||
|
let parentParentID = annotation.parentItem.parentID;
|
||||||
|
let parentID = annotation.parentID;
|
||||||
|
if (groups.length) {
|
||||||
|
if (parentParentID !== lastParentParentID) {
|
||||||
|
// Multiple top level regular items detected, allow including their titles
|
||||||
|
multipleParentParent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!groups.length || parentID !== lastParentID) {
|
||||||
|
groups.push({
|
||||||
|
parentTitle: annotation.parentItem.getDisplayTitle(),
|
||||||
|
parentParentID,
|
||||||
|
parentParentTitle: annotation.parentItem.parentItem && annotation.parentItem.parentItem.getDisplayTitle(),
|
||||||
|
jsonAnnotations: [jsonAnnotation]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let group = groups[groups.length - 1];
|
||||||
|
group.jsonAnnotations.push(jsonAnnotation);
|
||||||
|
}
|
||||||
|
lastParentParentID = parentParentID;
|
||||||
|
lastParentID = parentID;
|
||||||
|
}
|
||||||
|
let citationItems = [];
|
||||||
|
lastParentParentID = null;
|
||||||
|
for (let group of groups) {
|
||||||
|
if (multipleParentParent && group.parentParentTitle && lastParentParentID !== group.parentParentID) {
|
||||||
|
html += `<h2>${group.parentParentTitle}</h2>\n`;
|
||||||
|
}
|
||||||
|
lastParentParentID = group.parentParentID;
|
||||||
|
// If attachment doesn't have a parent or there are more attachments with the same parent, show attachment title
|
||||||
|
if (!group.parentParentID || groups.filter(x => x.parentParentID === group.parentParentID).length > 1) {
|
||||||
|
html += `<h3>${group.parentTitle}</h3>\n`;
|
||||||
|
}
|
||||||
|
let { html: _html, citationItems: _citationItems } = Zotero.EditorInstanceUtilities.serializeAnnotations(group.jsonAnnotations, true);
|
||||||
|
html += _html + '\n';
|
||||||
|
for (let _citationItem of _citationItems) {
|
||||||
|
if (!citationItems.find(item => item.uris.some(uri => _citationItem.uris.includes(uri)))) {
|
||||||
|
citationItems.push(_citationItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
citationItems = encodeURIComponent(JSON.stringify(citationItems));
|
citationItems = encodeURIComponent(JSON.stringify(citationItems));
|
||||||
html = `<div data-citation-items="${citationItems}" data-schema-version="${SCHEMA_VERSION}">${html}</div>`;
|
// Note: Update schema version only if using new features
|
||||||
|
let schemaVersion = 8;
|
||||||
|
html = `<div data-citation-items="${citationItems}" data-schema-version="${schemaVersion}">${html}</div>`;
|
||||||
note.setNote(html);
|
note.setNote(html);
|
||||||
await note.saveTx();
|
await note.saveTx();
|
||||||
return note;
|
return note;
|
||||||
|
|
@ -1268,7 +1398,8 @@ class EditorInstanceUtilities {
|
||||||
|
|
||||||
if (!annotation.text
|
if (!annotation.text
|
||||||
&& !annotation.comment
|
&& !annotation.comment
|
||||||
&& !annotation.imageAttachmentKey) {
|
&& !annotation.imageAttachmentKey
|
||||||
|
|| annotation.type === 'ink') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1359,16 +1490,24 @@ class EditorInstanceUtilities {
|
||||||
template = '<p>{{image}}<br/>{{citation}} {{comment}}</p>';
|
template = '<p>{{image}}<br/>{{citation}} {{comment}}</p>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Zotero.debug('Using note template:');
|
||||||
|
Zotero.debug(template);
|
||||||
|
|
||||||
|
template = template.replace(
|
||||||
|
/(<blockquote>[^<>]*?)({{highlight}})([\s\S]*?<\/blockquote>)/g,
|
||||||
|
(match, p1, p2, p3) => p1 + "{{highlight quotes='false'}}" + p3
|
||||||
|
);
|
||||||
|
|
||||||
let vars = {
|
let vars = {
|
||||||
color: annotation.color || '',
|
color: annotation.color || '',
|
||||||
highlight: (attrs) => attrs.quotes === 'true' ? quotedHighlightHTML : highlightHTML,
|
// Include quotation marks by default, but allow to disable with `quotes='false'`
|
||||||
|
highlight: (attrs) => attrs.quotes === 'false' ? highlightHTML : quotedHighlightHTML,
|
||||||
comment: commentHTML,
|
comment: commentHTML,
|
||||||
citation: citationHTML,
|
citation: citationHTML,
|
||||||
image: imageHTML,
|
image: imageHTML,
|
||||||
tags: (attrs) => (annotation.tags && annotation.tags.map(tag => tag.name) || []).join(attrs.join || ' ')
|
tags: (attrs) => (annotation.tags && annotation.tags.map(tag => tag.name) || []).join(attrs.join || ' ')
|
||||||
};
|
};
|
||||||
Zotero.debug('Using note template:');
|
|
||||||
Zotero.debug(template);
|
|
||||||
let templateHTML = Zotero.Utilities.Internal.generateHTMLFromTemplate(template, vars);
|
let templateHTML = Zotero.Utilities.Internal.generateHTMLFromTemplate(template, vars);
|
||||||
// Remove some spaces at the end of paragraph
|
// Remove some spaces at the end of paragraph
|
||||||
templateHTML = templateHTML.replace(/([\s]*)(<\/p)/g, '$2');
|
templateHTML = templateHTML.replace(/([\s]*)(<\/p)/g, '$2');
|
||||||
|
|
@ -1529,5 +1668,4 @@ class EditorInstanceUtilities {
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.EditorInstance = EditorInstance;
|
Zotero.EditorInstance = EditorInstance;
|
||||||
Zotero.EditorInstance.SCHEMA_VERSION = SCHEMA_VERSION;
|
|
||||||
Zotero.EditorInstanceUtilities = new EditorInstanceUtilities();
|
Zotero.EditorInstanceUtilities = new EditorInstanceUtilities();
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
* Functions for reading files
|
* Functions for reading files
|
||||||
* @namespace
|
* @namespace
|
||||||
*/
|
*/
|
||||||
Zotero.File = new function(){
|
Zotero.File = new function () {
|
||||||
Components.utils.import("resource://gre/modules/NetUtil.jsm");
|
Components.utils.import("resource://gre/modules/NetUtil.jsm");
|
||||||
Components.utils.import("resource://gre/modules/FileUtils.jsm");
|
Components.utils.import("resource://gre/modules/FileUtils.jsm");
|
||||||
|
|
||||||
|
|
@ -1086,7 +1086,31 @@ Zotero.File = new function(){
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize to a Unix-style path, replacing backslashes (interpreted as
|
||||||
|
* separators only on Windows) with forward slashes (interpreted as
|
||||||
|
* separators everywhere)
|
||||||
|
*
|
||||||
|
* @param {String} path
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
this.normalizeToUnix = function (path) {
|
||||||
|
// If we're on Windows, we need to normalize first and then replace
|
||||||
|
// the slashes, because OS.Path.normalize won't handle forward slashes
|
||||||
|
// correctly. Otherwise, we replace slashes first and *then* normalize.
|
||||||
|
// This should ensure consistent behavior across platforms.
|
||||||
|
if (Zotero.isWin) {
|
||||||
|
let normalized = OS.Path.normalize(path);
|
||||||
|
return normalized.replace(/\\/g, '/');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let replaced = path.replace(/\\/g, '/');
|
||||||
|
return OS.Path.normalize(replaced);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1096,8 +1120,8 @@ Zotero.File = new function(){
|
||||||
if (typeof dir != 'string') throw new Error("dir must be a string");
|
if (typeof dir != 'string') throw new Error("dir must be a string");
|
||||||
if (typeof file != 'string') throw new Error("file must be a string");
|
if (typeof file != 'string') throw new Error("file must be a string");
|
||||||
|
|
||||||
dir = OS.Path.normalize(dir).replace(/\\/g, "/");
|
dir = this.normalizeToUnix(dir);
|
||||||
file = OS.Path.normalize(file).replace(/\\/g, "/");
|
file = this.normalizeToUnix(file);
|
||||||
// Normalize D:\ vs. D:\foo
|
// Normalize D:\ vs. D:\foo
|
||||||
if (dir != file && !dir.endsWith('/')) {
|
if (dir != file && !dir.endsWith('/')) {
|
||||||
dir += '/';
|
dir += '/';
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
***** END LICENSE BLOCK *****
|
***** END LICENSE BLOCK *****
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Zotero.Fulltext = Zotero.FullText = new function(){
|
Zotero.Fulltext = Zotero.FullText = new function () {
|
||||||
this.isCachedMIMEType = isCachedMIMEType;
|
this.isCachedMIMEType = isCachedMIMEType;
|
||||||
|
|
||||||
this.__defineGetter__("pdfConverterCacheFile", function () { return '.zotero-ft-cache'; });
|
this.__defineGetter__("pdfConverterCacheFile", function () { return '.zotero-ft-cache'; });
|
||||||
|
|
@ -381,8 +381,8 @@ Zotero.Fulltext = Zotero.FullText = new function(){
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!charset) {
|
if (!charset) {
|
||||||
Zotero.logError(`Item ${itemID} didn't have a charset`);
|
Zotero.debug(`Item ${itemID} doesn't have a charset set -- using 'utf-8'`);
|
||||||
return false;
|
charset = 'utf-8';
|
||||||
}
|
}
|
||||||
|
|
||||||
var maxLength = Zotero.Prefs.get('fulltext.textMaxLength');
|
var maxLength = Zotero.Prefs.get('fulltext.textMaxLength');
|
||||||
|
|
|
||||||
|
|
@ -166,46 +166,48 @@ Zotero.HTTP = new function() {
|
||||||
if (e.xmlhttp.status == 503 && await _checkRetry(e.xmlhttp)) {
|
if (e.xmlhttp.status == 503 && await _checkRetry(e.xmlhttp)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// Don't retry if errorDelayMax is 0
|
||||||
|
if (options.errorDelayMax === 0) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
// Automatically retry other 5xx errors by default
|
// Automatically retry other 5xx errors by default
|
||||||
if (options.errorDelayMax !== 0) {
|
if (!errorDelayGenerator) {
|
||||||
if (!errorDelayGenerator) {
|
// Keep trying for up to an hour
|
||||||
// Keep trying for up to an hour
|
errorDelayGenerator = Zotero.Utilities.Internal.delayGenerator(
|
||||||
errorDelayGenerator = Zotero.Utilities.Internal.delayGenerator(
|
options.errorDelayIntervals || _errorDelayIntervals,
|
||||||
options.errorDelayIntervals || _errorDelayIntervals,
|
options.errorDelayMax !== undefined
|
||||||
options.errorDelayMax !== undefined
|
? options.errorDelayMax
|
||||||
? options.errorDelayMax
|
: _errorDelayMax
|
||||||
: _errorDelayMax
|
);
|
||||||
);
|
}
|
||||||
|
let delayPromise = errorDelayGenerator.next().value;
|
||||||
|
let keepGoing;
|
||||||
|
// Provide caller with a callback to cancel while waiting to retry
|
||||||
|
if (options.cancellerReceiver) {
|
||||||
|
let resolve;
|
||||||
|
let reject;
|
||||||
|
let cancelPromise = new Zotero.Promise((res, rej) => {
|
||||||
|
resolve = res;
|
||||||
|
reject = function () {
|
||||||
|
rej(new Zotero.HTTP.CancelledException);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
options.cancellerReceiver(reject);
|
||||||
|
try {
|
||||||
|
keepGoing = await Promise.race([delayPromise, cancelPromise]);
|
||||||
}
|
}
|
||||||
let delayPromise = errorDelayGenerator.next().value;
|
catch (e) {
|
||||||
let keepGoing;
|
Zotero.debug("Request cancelled");
|
||||||
// Provide caller with a callback to cancel while waiting to retry
|
|
||||||
if (options.cancellerReceiver) {
|
|
||||||
let resolve;
|
|
||||||
let reject;
|
|
||||||
let cancelPromise = new Zotero.Promise((res, rej) => {
|
|
||||||
resolve = res;
|
|
||||||
reject = function () {
|
|
||||||
rej(new Zotero.HTTP.CancelledException);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
options.cancellerReceiver(reject);
|
|
||||||
try {
|
|
||||||
keepGoing = await Promise.race([delayPromise, cancelPromise]);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Zotero.debug("Request cancelled");
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
keepGoing = await delayPromise;
|
|
||||||
}
|
|
||||||
if (!keepGoing) {
|
|
||||||
Zotero.logError("Failed too many times");
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
keepGoing = await delayPromise;
|
||||||
|
}
|
||||||
|
if (!keepGoing) {
|
||||||
|
Zotero.logError("Failed too many times");
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -1217,9 +1219,10 @@ Zotero.HTTP = new function() {
|
||||||
* @param {Boolean} dontDelete Don't delete the hidden browser upon completion; calling function
|
* @param {Boolean} dontDelete Don't delete the hidden browser upon completion; calling function
|
||||||
* must call deleteHiddenBrowser itself.
|
* must call deleteHiddenBrowser itself.
|
||||||
* @param {Zotero.CookieSandbox} [cookieSandbox] Cookie sandbox object
|
* @param {Zotero.CookieSandbox} [cookieSandbox] Cookie sandbox object
|
||||||
|
* @param {Object} [docShellPrefs] See Zotero.Browser.createHiddenBrowser
|
||||||
* @return {browser} Hidden browser used for loading
|
* @return {browser} Hidden browser used for loading
|
||||||
*/
|
*/
|
||||||
this.loadDocuments = function (urls, processor, onDone, onError, dontDelete, cookieSandbox) {
|
this.loadDocuments = function (urls, processor, onDone, onError, dontDelete, cookieSandbox, docShellPrefs={}) {
|
||||||
// (Approximately) how many seconds to wait if the document is left in the loading state and
|
// (Approximately) how many seconds to wait if the document is left in the loading state and
|
||||||
// pageshow is called before we call pageshow with an incomplete document
|
// pageshow is called before we call pageshow with an incomplete document
|
||||||
const LOADING_STATE_TIMEOUT = 120;
|
const LOADING_STATE_TIMEOUT = 120;
|
||||||
|
|
@ -1342,6 +1345,9 @@ Zotero.HTTP = new function() {
|
||||||
currentURL = 0;
|
currentURL = 0;
|
||||||
for(var i=0; i<urls.length; i++) {
|
for(var i=0; i<urls.length; i++) {
|
||||||
let hiddenBrowser = Zotero.Browser.createHiddenBrowser();
|
let hiddenBrowser = Zotero.Browser.createHiddenBrowser();
|
||||||
|
for (let pref in docShellPrefs) {
|
||||||
|
hiddenBrowser.docShell[pref] = docShellPrefs[pref];
|
||||||
|
}
|
||||||
if (cookieSandbox) {
|
if (cookieSandbox) {
|
||||||
cookieSandbox.attachToBrowser(hiddenBrowser);
|
cookieSandbox.attachToBrowser(hiddenBrowser);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,13 @@ const EXPORTED_DOCUMENT_MARKER = "ZOTERO_TRANSFER_DOCUMENT";
|
||||||
|
|
||||||
const NOTE_CITATION_PLACEHOLDER_LINK = 'https://www.zotero.org/?';
|
const NOTE_CITATION_PLACEHOLDER_LINK = 'https://www.zotero.org/?';
|
||||||
|
|
||||||
const TEMPLATE_VERSION = 1;
|
const TEMPLATE_VERSIONS = {
|
||||||
|
MacWord16: 2,
|
||||||
|
WinWord: 1,
|
||||||
|
OpenOffice: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const MENDELEY_URI_RE = /^http:\/\/www\.mendeley\.com\/documents\/\?uuid=(.*)/;
|
||||||
|
|
||||||
|
|
||||||
Zotero.Integration = new function() {
|
Zotero.Integration = new function() {
|
||||||
|
|
@ -104,7 +110,6 @@ Zotero.Integration = new function() {
|
||||||
this.deletePipe = function(pipe) {
|
this.deletePipe = function(pipe) {
|
||||||
try {
|
try {
|
||||||
if(pipe.exists()) {
|
if(pipe.exists()) {
|
||||||
Zotero.IPC.safePipeWrite(pipe, "Zotero shutdown\n");
|
|
||||||
pipe.remove(false);
|
pipe.remove(false);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -140,8 +145,8 @@ Zotero.Integration = new function() {
|
||||||
* @returns {Boolean} true if integration operation should be cancelled
|
* @returns {Boolean} true if integration operation should be cancelled
|
||||||
*/
|
*/
|
||||||
this.warnOutdatedTemplate = function (agent, templateVersion) {
|
this.warnOutdatedTemplate = function (agent, templateVersion) {
|
||||||
const validAgents = new Set(['OpenOffice', 'MacWord2016', 'MacWord16', 'WinWord']);
|
const expectedTemplateVersion = TEMPLATE_VERSIONS[agent];
|
||||||
if (!validAgents.has(agent) || templateVersion >= TEMPLATE_VERSION) return false;
|
if (typeof expectedTemplateVersion == 'undefined' || templateVersion >= expectedTemplateVersion) return false;
|
||||||
const daysToIgnore = 30;
|
const daysToIgnore = 30;
|
||||||
const now = Math.floor(Date.now() / 1000);
|
const now = Math.floor(Date.now() / 1000);
|
||||||
const updateTemplateDelayedOn = Zotero.Prefs.get('integration.updateTemplateDelayedOn');
|
const updateTemplateDelayedOn = Zotero.Prefs.get('integration.updateTemplateDelayedOn');
|
||||||
|
|
@ -505,6 +510,7 @@ Zotero.Integration = new function() {
|
||||||
session._removeCodeFields = {};
|
session._removeCodeFields = {};
|
||||||
session._deleteFields = {};
|
session._deleteFields = {};
|
||||||
session._bibliographyFields = [];
|
session._bibliographyFields = [];
|
||||||
|
session._shouldMerge = false;
|
||||||
|
|
||||||
if (dataString == EXPORTED_DOCUMENT_MARKER) {
|
if (dataString == EXPORTED_DOCUMENT_MARKER) {
|
||||||
Zotero.Integration.currentSession = session;
|
Zotero.Integration.currentSession = session;
|
||||||
|
|
@ -798,7 +804,8 @@ Zotero.Integration.Interface.prototype.addEditBibliography = Zotero.Promise.coro
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
Zotero.Integration.Interface.prototype.refresh = async function() {
|
Zotero.Integration.Interface.prototype.refresh = async function() {
|
||||||
await this._session.init(true, false)
|
await this._session.init(true, false);
|
||||||
|
this._session._shouldMerge = true;
|
||||||
|
|
||||||
this._session.reload = this._session.reload || this._session.data.prefs.delayCitationUpdates;
|
this._session.reload = this._session.reload || this._session.data.prefs.delayCitationUpdates;
|
||||||
await this._session.updateFromDocument(FORCE_CITATIONS_REGENERATE);
|
await this._session.updateFromDocument(FORCE_CITATIONS_REGENERATE);
|
||||||
|
|
@ -1037,6 +1044,10 @@ Zotero.Integration.Session.prototype.updateFromDocument = Zotero.Promise.corouti
|
||||||
this.progressBar.start();
|
this.progressBar.start();
|
||||||
if (forceCitations) {
|
if (forceCitations) {
|
||||||
this.regenAll = true;
|
this.regenAll = true;
|
||||||
|
// See Session.restoreProcessorState() for a comment
|
||||||
|
if (!Zotero.Prefs.get('cite.useCiteprocRs')) {
|
||||||
|
this.reload = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
yield this._processFields();
|
yield this._processFields();
|
||||||
try {
|
try {
|
||||||
|
|
@ -1068,6 +1079,7 @@ Zotero.Integration.Session.prototype._processFields = async function () {
|
||||||
throw new Error("_processFields called without fetching fields first");
|
throw new Error("_processFields called without fetching fields first");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let adjacentCitations = [];
|
||||||
for (var i = 0; i < this._fields.length; i++) {
|
for (var i = 0; i < this._fields.length; i++) {
|
||||||
let field = await Zotero.Integration.Field.loadExisting(this._fields[i]);
|
let field = await Zotero.Integration.Field.loadExisting(this._fields[i]);
|
||||||
if (field.type === INTEGRATION_TYPE_ITEM) {
|
if (field.type === INTEGRATION_TYPE_ITEM) {
|
||||||
|
|
@ -1075,7 +1087,15 @@ Zotero.Integration.Session.prototype._processFields = async function () {
|
||||||
data = await field.unserialize(),
|
data = await field.unserialize(),
|
||||||
citation = new Zotero.Integration.Citation(field, data, noteIndex);
|
citation = new Zotero.Integration.Citation(field, data, noteIndex);
|
||||||
|
|
||||||
await this.addCitation(i, noteIndex, citation);
|
if (this._shouldMerge && typeof field.isAdjacentToNextField === 'function' && await field.isAdjacentToNextField()) {
|
||||||
|
adjacentCitations.push(citation);
|
||||||
|
this._deleteFields[i] = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
await this.addCitation(i, noteIndex, citation, adjacentCitations);
|
||||||
|
if (adjacentCitations.length) {
|
||||||
|
adjacentCitations = [];
|
||||||
|
}
|
||||||
} else if (field.type === INTEGRATION_TYPE_BIBLIOGRAPHY) {
|
} else if (field.type === INTEGRATION_TYPE_BIBLIOGRAPHY) {
|
||||||
if (this.ignoreEmptyBibliography && (await field.getText()).trim() === "") {
|
if (this.ignoreEmptyBibliography && (await field.getText()).trim() === "") {
|
||||||
this._removeCodeFields[i] = true;
|
this._removeCodeFields[i] = true;
|
||||||
|
|
@ -1957,9 +1977,17 @@ Zotero.Integration.Session.prototype.importDocument = async function() {
|
||||||
/**
|
/**
|
||||||
* Adds a citation to the arrays representing the document
|
* Adds a citation to the arrays representing the document
|
||||||
*/
|
*/
|
||||||
Zotero.Integration.Session.prototype.addCitation = async function (index, noteIndex, citation) {
|
Zotero.Integration.Session.prototype.addCitation = async function (index, noteIndex, citation, adjacentCitations=[]) {
|
||||||
index = parseInt(index, 10);
|
index = parseInt(index, 10);
|
||||||
|
|
||||||
|
if (adjacentCitations.length) {
|
||||||
|
Zotero.debug(`Merging adjacent citations ${adjacentCitations.map(c => c.citationID)} to citation ${citation.citationID}`);
|
||||||
|
for (let adjacentCitation of adjacentCitations) {
|
||||||
|
citation.mergeCitation(adjacentCitation);
|
||||||
|
}
|
||||||
|
this.updateIndices[index] = true;
|
||||||
|
}
|
||||||
|
|
||||||
var action = await citation.loadItemData();
|
var action = await citation.loadItemData();
|
||||||
|
|
||||||
if (action == Zotero.Integration.REMOVE_CODE) {
|
if (action == Zotero.Integration.REMOVE_CODE) {
|
||||||
|
|
@ -2157,6 +2185,12 @@ Zotero.Integration.Session.prototype.restoreProcessorState = function() {
|
||||||
citations.push(this.citationsByIndex[i]);
|
citations.push(this.citationsByIndex[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!Zotero.Prefs.get('cite.useCiteprocRs')) {
|
||||||
|
// Due to a bug in citeproc-js there are disambiguation issues after changing items in Zotero library
|
||||||
|
// and rebuilding the processor state, so we reinitialize the processor altogether
|
||||||
|
let style = Zotero.Styles.get(this.data.style.styleID);
|
||||||
|
this.style = style.getCiteProc(this.data.style.locale, this.outputFormat, this.data.prefs.automaticJournalAbbreviations);
|
||||||
|
}
|
||||||
this.style.rebuildProcessorState(citations, this.outputFormat, uncited);
|
this.style.rebuildProcessorState(citations, this.outputFormat, uncited);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2564,7 +2598,7 @@ Zotero.Integration.URIMap.prototype.getURIsForItemID = function(id) {
|
||||||
/**
|
/**
|
||||||
* Gets Zotero item for a given set of URIs
|
* Gets Zotero item for a given set of URIs
|
||||||
*/
|
*/
|
||||||
Zotero.Integration.URIMap.prototype.getZoteroItemForURIs = Zotero.Promise.coroutine(function* (uris) {
|
Zotero.Integration.URIMap.prototype.getZoteroItemForURIs = async function (uris) {
|
||||||
var zoteroItem = false;
|
var zoteroItem = false;
|
||||||
var needUpdate = false;
|
var needUpdate = false;
|
||||||
var embeddedItem = false;;
|
var embeddedItem = false;;
|
||||||
|
|
@ -2579,43 +2613,94 @@ Zotero.Integration.URIMap.prototype.getZoteroItemForURIs = Zotero.Promise.corout
|
||||||
|
|
||||||
// Next try getting URI directly
|
// Next try getting URI directly
|
||||||
try {
|
try {
|
||||||
zoteroItem = yield Zotero.URI.getURIItem(uri);
|
var replacer = await Zotero.URI.getURIItem(uri);
|
||||||
if(zoteroItem) {
|
if (replacer && !replacer.deleted) {
|
||||||
// Ignore items in the trash
|
zoteroItem = replacer;
|
||||||
if(zoteroItem.deleted) {
|
break;
|
||||||
zoteroItem = false;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
|
|
||||||
// Try merged item mapping
|
// Try merged item mapping
|
||||||
var replacer = yield Zotero.Relations.getByPredicateAndObject(
|
var replacer = await Zotero.Relations.getByPredicateAndObject(
|
||||||
'item', Zotero.Relations.replacedItemPredicate, uri
|
'item', Zotero.Relations.replacedItemPredicate, uri
|
||||||
);
|
);
|
||||||
if (replacer.length && !replacer[0].deleted) {
|
if (replacer.length && !replacer[0].deleted) {
|
||||||
zoteroItem = replacer[0];
|
zoteroItem = replacer[0];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(zoteroItem) break;
|
// Check if it's a mendeley URI and if we have imported the item
|
||||||
|
let m = MENDELEY_URI_RE.exec(uri);
|
||||||
|
if (m) {
|
||||||
|
replacer = await Zotero.Relations.getByPredicateAndObject(
|
||||||
|
'item', 'mendeleyDB:documentUUID', m[1]
|
||||||
|
);
|
||||||
|
if (replacer.length) {
|
||||||
|
if (!replacer[0].deleted) {
|
||||||
|
zoteroItem = replacer[0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If not blocked by user having pressed skip in this session,
|
||||||
|
// or user having checked the checkbox to not be prompted about this,
|
||||||
|
// or user having imported their library with the new version of importer
|
||||||
|
else if (!(this.session.dontPromptForMendeley
|
||||||
|
|| Zotero.Prefs.get('integration.dontPromptMendeleyImport')
|
||||||
|
|| await Zotero.DB.valueQueryAsync("SELECT value FROM settings WHERE setting='mendeleyImport' AND key='version'")
|
||||||
|
)) {
|
||||||
|
// Prompt user to (re)import their mendeley database which might make us recognize
|
||||||
|
// these items
|
||||||
|
let checkbox = {};
|
||||||
|
let result = Zotero.Prompt.confirm({
|
||||||
|
title: Zotero.getString('integration.mendeleyImport.title'),
|
||||||
|
text: Zotero.getString('integration.mendeleyImport.description', [Zotero.appName]),
|
||||||
|
button0: Zotero.getString('integration.mendeleyImport.openImporter'),
|
||||||
|
button1: Zotero.getString('general.skip'),
|
||||||
|
button2: Zotero.getString('general.moreInformation'),
|
||||||
|
checkLabel: Zotero.getString('general.dontAskAgain'),
|
||||||
|
checkbox
|
||||||
|
});
|
||||||
|
if (result === 0) {
|
||||||
|
setTimeout(
|
||||||
|
() => Zotero.getMainWindow().Zotero_File_Interface.showImportWizard(
|
||||||
|
{
|
||||||
|
pageID: 'mendeley-online-explanation',
|
||||||
|
relinkOnly: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
throw new Zotero.Exception.UserCancelled("Importing mendeley citations");
|
||||||
|
}
|
||||||
|
else if (result == 1) {
|
||||||
|
this.session.dontPromptForMendeley = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Zotero.launchURL("https://www.zotero.org/support/kb/mendeley_import#using_mendeley_citations");
|
||||||
|
}
|
||||||
|
if (checkbox.value) {
|
||||||
|
Zotero.Prefs.set('integration.dontPromptMendeleyImport', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(zoteroItem) {
|
if (zoteroItem) {
|
||||||
// make sure URI is up to date (in case user just began syncing)
|
// make sure URI is up to date (in case user just began syncing)
|
||||||
var newURI = Zotero.URI.getItemURI(zoteroItem);
|
var newURI = Zotero.URI.getItemURI(zoteroItem);
|
||||||
if(newURI != uris[i]) {
|
if (!uris.includes(newURI)) {
|
||||||
uris[i] = newURI;
|
uris.push(newURI);
|
||||||
needUpdate = true;
|
needUpdate = true;
|
||||||
}
|
}
|
||||||
// cache uris
|
// cache uris
|
||||||
this.itemIDURIs[zoteroItem.id] = uris;
|
this.add(zoteroItem.id, uris)
|
||||||
} else if(embeddedItem) {
|
}
|
||||||
|
else if (embeddedItem) {
|
||||||
return [embeddedItem, false];
|
return [embeddedItem, false];
|
||||||
}
|
}
|
||||||
|
|
||||||
return [zoteroItem, needUpdate];
|
return [zoteroItem, needUpdate];
|
||||||
});
|
};
|
||||||
|
|
||||||
Zotero.Integration.Field = class {
|
Zotero.Integration.Field = class {
|
||||||
constructor(field, rawCode) {
|
constructor(field, rawCode) {
|
||||||
|
|
@ -2623,7 +2708,7 @@ Zotero.Integration.Field = class {
|
||||||
throw new Error("Trying to instantiate Integration.Field with Integration.Field, not doc field");
|
throw new Error("Trying to instantiate Integration.Field with Integration.Field, not doc field");
|
||||||
}
|
}
|
||||||
for (let func of Zotero.Integration.Field.INTERFACE) {
|
for (let func of Zotero.Integration.Field.INTERFACE) {
|
||||||
if (!(func in this)) {
|
if (!(func in this) && (func in field)) {
|
||||||
this[func] = field[func].bind(field);
|
this[func] = field[func].bind(field);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2682,7 +2767,7 @@ Zotero.Integration.Field = class {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Zotero.Integration.Field.INTERFACE = ['delete', 'removeCode', 'select', 'setText',
|
Zotero.Integration.Field.INTERFACE = ['delete', 'removeCode', 'select', 'setText',
|
||||||
'getText', 'setCode', 'getCode', 'equals', 'getNoteIndex'];
|
'getText', 'setCode', 'getCode', 'equals', 'getNoteIndex', 'isAdjacentToNextField'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load existing field in document and return correct instance of field type
|
* Load existing field in document and return correct instance of field type
|
||||||
|
|
@ -2794,8 +2879,13 @@ Zotero.Integration.CitationField = class extends Zotero.Integration.Field {
|
||||||
}
|
}
|
||||||
|
|
||||||
// for update from Zotero 2.1 or earlier
|
// for update from Zotero 2.1 or earlier
|
||||||
if(citationItem.uri) {
|
if (citationItem.uri) {
|
||||||
citationItem.uris = citationItem.uri;
|
if (Array.isArray(citationItem.uris)) {
|
||||||
|
citationItem.uris.push(citationItem.uri);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
citationItem.uris = [citationItem.uri];
|
||||||
|
}
|
||||||
delete citationItem.uri;
|
delete citationItem.uri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2862,7 +2952,7 @@ Zotero.Integration.CitationField = class extends Zotero.Integration.Field {
|
||||||
Zotero.Integration.currentDoc.activate();
|
Zotero.Integration.currentDoc.activate();
|
||||||
var result = await Zotero.Integration.currentSession.displayAlert(msg, DIALOG_ICON_CAUTION, DIALOG_BUTTONS_YES_NO_CANCEL);
|
var result = await Zotero.Integration.currentSession.displayAlert(msg, DIALOG_ICON_CAUTION, DIALOG_BUTTONS_YES_NO_CANCEL);
|
||||||
if (result == 0) { // Cancel
|
if (result == 0) { // Cancel
|
||||||
return new Zotero.Exception.UserCancelled("corrupt citation resolution");
|
throw new Zotero.Exception.UserCancelled("corrupt citation resolution");
|
||||||
} else if (result == 1) { // No
|
} else if (result == 1) { // No
|
||||||
return false;
|
return false;
|
||||||
} else { // Yes
|
} else { // Yes
|
||||||
|
|
@ -2921,6 +3011,23 @@ Zotero.Integration.Citation = class {
|
||||||
this._field = citationField;
|
this._field = citationField;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge citation items and remove duplicates, unless the items have different
|
||||||
|
* @param citation {Citation}
|
||||||
|
*/
|
||||||
|
mergeCitation(citation) {
|
||||||
|
let items = this.citationItems.concat(citation.citationItems);
|
||||||
|
let addedItems = new Set();
|
||||||
|
this.citationItems = []
|
||||||
|
for (let item of items) {
|
||||||
|
if (addedItems.has(item.id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
addedItems.add(item.id);
|
||||||
|
this.citationItems.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load citation item data
|
* Load citation item data
|
||||||
* @param {Boolean} [promptToReselect=true] - will throw a MissingItemException if false
|
* @param {Boolean} [promptToReselect=true] - will throw a MissingItemException if false
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,8 @@ Zotero.Intl = new function () {
|
||||||
|
|
||||||
Components.utils.import("resource://gre/modules/PluralForm.jsm");
|
Components.utils.import("resource://gre/modules/PluralForm.jsm");
|
||||||
|
|
||||||
bundle = Services.strings.createBundle('chrome://zotero/locale/zotero.properties');
|
// Exposed for tests
|
||||||
|
this._bundle = bundle = Services.strings.createBundle('chrome://zotero/locale/zotero.properties');
|
||||||
intlProps = Services.strings.createBundle('chrome://zotero/locale/mozilla/intl.properties');
|
intlProps = Services.strings.createBundle('chrome://zotero/locale/mozilla/intl.properties');
|
||||||
|
|
||||||
[pluralFormGet, pluralFormNumForms] = PluralForm.makeGetter(parseInt(getIntlProp('pluralRule', 1)));
|
[pluralFormGet, pluralFormNumForms] = PluralForm.makeGetter(parseInt(getIntlProp('pluralRule', 1)));
|
||||||
|
|
@ -105,6 +106,9 @@ Zotero.Intl = new function () {
|
||||||
}
|
}
|
||||||
l10n = bundle.formatStringFromName(name, params, params.length);
|
l10n = bundle.formatStringFromName(name, params, params.length);
|
||||||
}
|
}
|
||||||
|
else if (this.strings[name]) {
|
||||||
|
return this.strings[name];
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
l10n = bundle.GetStringFromName(name);
|
l10n = bundle.GetStringFromName(name);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,79 +27,6 @@ Zotero.IPC = new function() {
|
||||||
var _libc, _libcPath, _instancePipe, _user32, open, write, close;
|
var _libc, _libcPath, _instancePipe, _user32, open, write, close;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize pipe for communication with connector
|
|
||||||
*/
|
|
||||||
this.init = function() {
|
|
||||||
if(!Zotero.isWin) { // no pipe support on Fx 3.6
|
|
||||||
_instancePipe = _getPipeDirectory();
|
|
||||||
if(!_instancePipe.exists()) {
|
|
||||||
_instancePipe.create(Ci.nsIFile.DIRECTORY_TYPE, 0o700);
|
|
||||||
}
|
|
||||||
_instancePipe.append(Zotero.instanceID);
|
|
||||||
|
|
||||||
Zotero.IPC.Pipe.initPipeListener(_instancePipe, this.parsePipeInput);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses input received via instance pipe
|
|
||||||
*/
|
|
||||||
this.parsePipeInput = function(msgs) {
|
|
||||||
for (let msg of msgs.split("\n")) {
|
|
||||||
if(!msg) continue;
|
|
||||||
Zotero.debug('IPC: Received "'+msg+'"');
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The below messages coordinate switching Zotero for Firefox from extension mode to
|
|
||||||
* connector mode without restarting after Zotero Standalone has been launched. The
|
|
||||||
* dance typically proceeds as follows:
|
|
||||||
*
|
|
||||||
* 1. SA sends a releaseLock message to Z4Fx that tells it to release its lock.
|
|
||||||
* 2. Z4Fx releases its lock and sends a lockReleased message to SA.
|
|
||||||
* 3. Z4Fx restarts in connector mode. Once it's ready for an IPC command, it sends
|
|
||||||
* a checkInitComplete message to SA.
|
|
||||||
* 4. Once SA finishes initializing, or immediately after a checkInitComplete message
|
|
||||||
* has been received if it is already initialized, SA sends an initComplete message
|
|
||||||
* to Z4Fx.
|
|
||||||
*/
|
|
||||||
if(msg.substr(0, 11) === "releaseLock") {
|
|
||||||
// Standalone sends this to the Firefox extension to tell the Firefox extension to
|
|
||||||
// release its lock on the Zotero database
|
|
||||||
if(!Zotero.isConnector && (msg.length === 11 ||
|
|
||||||
msg.substr(12) === Zotero.DataDirectory.getDatabase())) {
|
|
||||||
switchConnectorMode(true);
|
|
||||||
}
|
|
||||||
} else if(msg === "lockReleased") {
|
|
||||||
// The Firefox extension sends this to Standalone to let Standalone know that it has
|
|
||||||
// released its lock
|
|
||||||
Zotero.onDBLockReleased();
|
|
||||||
} else if(msg === "checkInitComplete") {
|
|
||||||
// The Firefox extension sends this to Standalone to tell Standalone to send an
|
|
||||||
// initComplete message when it is fully initialized
|
|
||||||
if(Zotero.initialized) {
|
|
||||||
Zotero.IPC.broadcast("initComplete");
|
|
||||||
} else {
|
|
||||||
var observerService = Components.classes["@mozilla.org/observer-service;1"]
|
|
||||||
.getService(Components.interfaces.nsIObserverService);
|
|
||||||
var _loadObserver = function() {
|
|
||||||
Zotero.IPC.broadcast("initComplete");
|
|
||||||
observerService.removeObserver(_loadObserver, "zotero-loaded");
|
|
||||||
};
|
|
||||||
observerService.addObserver(_loadObserver, "zotero-loaded", false);
|
|
||||||
}
|
|
||||||
} else if(msg === "initComplete") {
|
|
||||||
// Standalone sends this to the Firefox extension to let the Firefox extension
|
|
||||||
// know that Standalone has fully initialized and it should pull the list of
|
|
||||||
// translators
|
|
||||||
Zotero.initComplete();
|
|
||||||
}
|
|
||||||
else if (msg == "reinit") {
|
|
||||||
if (Zotero.isConnector) {
|
|
||||||
reinit(false, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes safely to a file, avoiding blocking.
|
* Writes safely to a file, avoiding blocking.
|
||||||
|
|
@ -135,165 +62,6 @@ Zotero.IPC = new function() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Broadcast a message to all other Zotero instances
|
|
||||||
*/
|
|
||||||
this.broadcast = function(msg) {
|
|
||||||
if(Zotero.isWin) { // communicate via WM_COPYDATA method
|
|
||||||
Components.utils.import("resource://gre/modules/ctypes.jsm");
|
|
||||||
|
|
||||||
// communicate via message window
|
|
||||||
var user32 = ctypes.open("user32.dll");
|
|
||||||
|
|
||||||
/* http://msdn.microsoft.com/en-us/library/ms633499%28v=vs.85%29.aspx
|
|
||||||
* HWND WINAPI FindWindow(
|
|
||||||
* __in_opt LPCTSTR lpClassName,
|
|
||||||
* __in_opt LPCTSTR lpWindowName
|
|
||||||
* );
|
|
||||||
*/
|
|
||||||
var FindWindow = user32.declare("FindWindowW", ctypes.winapi_abi, ctypes.int32_t,
|
|
||||||
ctypes.jschar.ptr, ctypes.jschar.ptr);
|
|
||||||
|
|
||||||
/* http://msdn.microsoft.com/en-us/library/ms633539%28v=vs.85%29.aspx
|
|
||||||
* BOOL WINAPI SetForegroundWindow(
|
|
||||||
* __in HWND hWnd
|
|
||||||
* );
|
|
||||||
*/
|
|
||||||
var SetForegroundWindow = user32.declare("SetForegroundWindow", ctypes.winapi_abi,
|
|
||||||
ctypes.bool, ctypes.int32_t);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* LRESULT WINAPI SendMessage(
|
|
||||||
* __in HWND hWnd,
|
|
||||||
* __in UINT Msg,
|
|
||||||
* __in WPARAM wParam,
|
|
||||||
* __in LPARAM lParam
|
|
||||||
* );
|
|
||||||
*/
|
|
||||||
var SendMessage = user32.declare("SendMessageW", ctypes.winapi_abi, ctypes.uintptr_t,
|
|
||||||
ctypes.int32_t, ctypes.unsigned_int, ctypes.voidptr_t, ctypes.voidptr_t);
|
|
||||||
|
|
||||||
/* http://msdn.microsoft.com/en-us/library/ms649010%28v=vs.85%29.aspx
|
|
||||||
* typedef struct tagCOPYDATASTRUCT {
|
|
||||||
* ULONG_PTR dwData;
|
|
||||||
* DWORD cbData;
|
|
||||||
* PVOID lpData;
|
|
||||||
* } COPYDATASTRUCT, *PCOPYDATASTRUCT;
|
|
||||||
*/
|
|
||||||
var COPYDATASTRUCT = ctypes.StructType("COPYDATASTRUCT", [
|
|
||||||
{"dwData":ctypes.voidptr_t},
|
|
||||||
{"cbData":ctypes.uint32_t},
|
|
||||||
{"lpData":ctypes.voidptr_t}
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Aurora/Nightly are always named "Firefox" in
|
|
||||||
// application.ini
|
|
||||||
const appNames = ["Firefox", "Zotero"];
|
|
||||||
|
|
||||||
// Different from Zotero.appName; this corresponds to the
|
|
||||||
// name in application.ini
|
|
||||||
const myAppName = Services.appinfo.name;
|
|
||||||
|
|
||||||
for (let appName of appNames) {
|
|
||||||
// don't send messages to ourself
|
|
||||||
if(appName === myAppName) continue;
|
|
||||||
|
|
||||||
var thWnd = FindWindow(appName+"MessageWindow", null);
|
|
||||||
if(thWnd) {
|
|
||||||
Zotero.debug('IPC: Broadcasting "'+msg+'" to window "'+appName+'MessageWindow"');
|
|
||||||
|
|
||||||
// allocate message
|
|
||||||
var data = ctypes.char.array()('firefox.exe -silent -ZoteroIPC "'+msg.replace('"', '""', "g")+'"\x00C:\\');
|
|
||||||
var dataSize = data.length*data.constructor.size;
|
|
||||||
|
|
||||||
// create new COPYDATASTRUCT
|
|
||||||
var cds = new COPYDATASTRUCT();
|
|
||||||
cds.dwData = null;
|
|
||||||
cds.cbData = dataSize;
|
|
||||||
cds.lpData = data.address();
|
|
||||||
|
|
||||||
// send COPYDATASTRUCT
|
|
||||||
var success = SendMessage(thWnd, 0x004A /** WM_COPYDATA **/, null, cds.address());
|
|
||||||
|
|
||||||
user32.close();
|
|
||||||
return !!success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
user32.close();
|
|
||||||
return false;
|
|
||||||
} else { // communicate via pipes
|
|
||||||
// look for other Zotero instances
|
|
||||||
var pipes = [];
|
|
||||||
var pipeDir = _getPipeDirectory();
|
|
||||||
if(pipeDir.exists()) {
|
|
||||||
var dirEntries = pipeDir.directoryEntries;
|
|
||||||
while (dirEntries.hasMoreElements()) {
|
|
||||||
var pipe = dirEntries.getNext().QueryInterface(Ci.nsIFile);
|
|
||||||
if(pipe.leafName[0] !== "." && (!_instancePipe || !pipe.equals(_instancePipe))) {
|
|
||||||
pipes.push(pipe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!pipes.length) return false;
|
|
||||||
var success = false;
|
|
||||||
for (let pipe of pipes) {
|
|
||||||
Zotero.debug('IPC: Trying to broadcast "'+msg+'" to instance '+pipe.leafName);
|
|
||||||
|
|
||||||
var defunct = false;
|
|
||||||
|
|
||||||
if(pipe.isFile()) {
|
|
||||||
// not actually a pipe
|
|
||||||
if(pipe.isDirectory()) {
|
|
||||||
// not a file, so definitely defunct
|
|
||||||
defunct = true;
|
|
||||||
} else {
|
|
||||||
// check to see whether the size exceeds a certain threshold that we find
|
|
||||||
// reasonable for the queue, and if not, delete the pipe, because it's
|
|
||||||
// probably just a file that wasn't deleted on shutdown and is now
|
|
||||||
// accumulating vast amounts of data
|
|
||||||
defunct = pipe.fileSize > 1024;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!defunct) {
|
|
||||||
// Try to write to the pipe for 100 ms
|
|
||||||
var time = Date.now(), timeout = time+100, wroteToPipe;
|
|
||||||
do {
|
|
||||||
wroteToPipe = Zotero.IPC.safePipeWrite(pipe, msg+"\n");
|
|
||||||
} while(Date.now() < timeout && !wroteToPipe);
|
|
||||||
if (wroteToPipe) Zotero.debug('IPC: Pipe took '+(Date.now()-time)+' ms to become available');
|
|
||||||
success = success || wroteToPipe;
|
|
||||||
defunct = !wroteToPipe;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(defunct) {
|
|
||||||
Zotero.debug('IPC: Removing defunct pipe '+pipe.leafName);
|
|
||||||
try {
|
|
||||||
pipe.remove(true);
|
|
||||||
} catch(e) {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get directory containing Zotero pipes
|
|
||||||
*/
|
|
||||||
function _getPipeDirectory() {
|
|
||||||
var dir = Zotero.File.pathToFile(Zotero.DataDirectory.dir);
|
|
||||||
dir.append("pipes");
|
|
||||||
return dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.pipeExists = Zotero.Promise.coroutine(function* () {
|
|
||||||
var dir = _getPipeDirectory().path;
|
|
||||||
return (yield OS.File.exists(dir)) && !(yield Zotero.File.directoryIsEmpty(dir));
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the path to libc as a string
|
* Gets the path to libc as a string
|
||||||
*/
|
*/
|
||||||
|
|
@ -359,19 +127,8 @@ Zotero.IPC.Pipe = new function() {
|
||||||
this.initPipeListener = function(file, callback) {
|
this.initPipeListener = function(file, callback) {
|
||||||
Zotero.debug("IPC: Initializing pipe at "+file.path);
|
Zotero.debug("IPC: Initializing pipe at "+file.path);
|
||||||
|
|
||||||
// determine type of pipe
|
|
||||||
if(!_pipeClass) {
|
|
||||||
var verComp = Components.classes["@mozilla.org/xpcom/version-comparator;1"]
|
|
||||||
.getService(Components.interfaces.nsIVersionComparator);
|
|
||||||
var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].
|
|
||||||
getService(Components.interfaces.nsIXULAppInfo);
|
|
||||||
if(verComp.compare("2.2a1pre", appInfo.platformVersion) <= 0) { // Gecko 5
|
|
||||||
_pipeClass = Zotero.IPC.Pipe.DeferredOpen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// make new pipe
|
// make new pipe
|
||||||
new _pipeClass(file, callback);
|
new Zotero.IPC.Pipe.DeferredOpen(file, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -395,7 +152,7 @@ Zotero.IPC.Pipe = new function() {
|
||||||
* Adds a shutdown listener for a pipe that writes "Zotero shutdown\n" to the pipe and then
|
* Adds a shutdown listener for a pipe that writes "Zotero shutdown\n" to the pipe and then
|
||||||
* deletes it
|
* deletes it
|
||||||
*/
|
*/
|
||||||
this.writeShutdownMessage = function(pipe, file) {
|
this.remove = function(pipe, file) {
|
||||||
// Make sure pipe actually exists
|
// Make sure pipe actually exists
|
||||||
if(!file.exists()) {
|
if(!file.exists()) {
|
||||||
Zotero.debug("IPC: Not closing pipe "+file.path+": already deleted");
|
Zotero.debug("IPC: Not closing pipe "+file.path+": already deleted");
|
||||||
|
|
@ -425,7 +182,7 @@ Zotero.IPC.Pipe.DeferredOpen = function(file, callback) {
|
||||||
this._initPump();
|
this._initPump();
|
||||||
|
|
||||||
// add shutdown listener
|
// add shutdown listener
|
||||||
Zotero.addShutdownListener(Zotero.IPC.Pipe.writeShutdownMessage.bind(null, this, file));
|
Zotero.addShutdownListener(Zotero.IPC.Pipe.remove.bind(null, this, file));
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.IPC.Pipe.DeferredOpen.prototype = {
|
Zotero.IPC.Pipe.DeferredOpen.prototype = {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
***** END LICENSE BLOCK *****
|
***** END LICENSE BLOCK *****
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Zotero.MIME = new function(){
|
Zotero.MIME = new function () {
|
||||||
this.isTextType = isTextType;
|
this.isTextType = isTextType;
|
||||||
this.getPrimaryExtension = getPrimaryExtension;
|
this.getPrimaryExtension = getPrimaryExtension;
|
||||||
this.sniffForBinary = sniffForBinary;
|
this.sniffForBinary = sniffForBinary;
|
||||||
|
|
|
||||||
|
|
@ -55,20 +55,22 @@ Zotero.MIMETypeHandler = new function () {
|
||||||
_observers = [];
|
_observers = [];
|
||||||
|
|
||||||
// Install styles from the Cite preferences
|
// Install styles from the Cite preferences
|
||||||
this.addHandler("application/vnd.citationstyles.style+xml", async function (blob, origin) {
|
this.addHandlers("application/vnd.citationstyles.style+xml", {
|
||||||
let win = Services.wm.getMostRecentWindow("zotero:basicViewer");
|
onContent: async function (blob, origin) {
|
||||||
var data = await Zotero.Utilities.Internal.blobToText(blob);
|
let win = Services.wm.getMostRecentWindow("zotero:basicViewer");
|
||||||
try {
|
var data = await Zotero.Utilities.Internal.blobToText(blob);
|
||||||
await Zotero.Styles.install(data, origin, true);
|
try {
|
||||||
}
|
await Zotero.Styles.install(data, origin, true);
|
||||||
catch (e) {
|
}
|
||||||
Zotero.logError(e);
|
catch (e) {
|
||||||
(new Zotero.Exception.Alert("styles.install.unexpectedError",
|
Zotero.logError(e);
|
||||||
origin, "styles.install.title", e)).present();
|
(new Zotero.Exception.Alert("styles.install.unexpectedError",
|
||||||
}
|
origin, "styles.install.title", e)).present();
|
||||||
// Close styles page in basic viewer after installing a style
|
}
|
||||||
if (win) {
|
// Close styles page in basic viewer after installing a style
|
||||||
win.close();
|
if (win) {
|
||||||
|
win.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
};
|
};
|
||||||
|
|
@ -76,17 +78,25 @@ Zotero.MIMETypeHandler = new function () {
|
||||||
/**
|
/**
|
||||||
* Adds a handler to handle a specific MIME type
|
* Adds a handler to handle a specific MIME type
|
||||||
* @param {String} type MIME type to handle
|
* @param {String} type MIME type to handle
|
||||||
* @param {Function} fn Function to call to handle type - fn(string, uri)
|
* @param {Object} handlers
|
||||||
|
* - handlers.onContent - function to call when content is received
|
||||||
|
* - handlers.onStartRequest - function to call when response for content type is first received
|
||||||
* @param {Boolean} ignoreContentDisposition If true, ignores the Content-Disposition header,
|
* @param {Boolean} ignoreContentDisposition If true, ignores the Content-Disposition header,
|
||||||
* which is often used to force a file to download rather than let it be handled by the web
|
* which is often used to force a file to download rather than let it be handled by the web
|
||||||
* browser
|
* browser
|
||||||
*/
|
*/
|
||||||
this.addHandler = function (type, fn, ignoreContentDisposition) {
|
this.addHandlers = function (type, handlers, ignoreContentDisposition) {
|
||||||
|
if (typeof handlers == 'function') {
|
||||||
|
handlers = {
|
||||||
|
onContent: handlers
|
||||||
|
};
|
||||||
|
Zotero.debug('MIMETypeHandler.addHandler: second parameter function is deprecated. Pass an object', 1)
|
||||||
|
}
|
||||||
if (_typeHandlers[type]) {
|
if (_typeHandlers[type]) {
|
||||||
_typeHandlers[type].push(fn);
|
_typeHandlers[type].push(handlers);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
_typeHandlers[type] = [fn];
|
_typeHandlers[type] = [handlers];
|
||||||
}
|
}
|
||||||
if (ignoreContentDisposition) {
|
if (ignoreContentDisposition) {
|
||||||
_ignoreContentDispositionTypes.add(type);
|
_ignoreContentDispositionTypes.add(type);
|
||||||
|
|
@ -96,19 +106,19 @@ Zotero.MIMETypeHandler = new function () {
|
||||||
/**
|
/**
|
||||||
* Removes a handler for a specific MIME type
|
* Removes a handler for a specific MIME type
|
||||||
* @param {String} type MIME type to handle
|
* @param {String} type MIME type to handle
|
||||||
* @param {Function} handler Function handler to remove
|
* @param {Object} handlers Function handlers to remove
|
||||||
*/
|
*/
|
||||||
this.removeHandler = function (type, handler) {
|
this.removeHandlers = function (type, handlers) {
|
||||||
// If no handler specified or this is the last handler for the type
|
// If no handler specified or this is the last handler for the type
|
||||||
// stop monitoring the content type completely.
|
// stop monitoring the content type completely.
|
||||||
if (!handler || _typeHandlers[type] && _typeHandlers[type].length <= 1) {
|
if (!handlers || _typeHandlers[type] && _typeHandlers[type].length <= 1) {
|
||||||
delete _typeHandlers[type];
|
delete _typeHandlers[type];
|
||||||
_ignoreContentDispositionTypes.delete(type);
|
_ignoreContentDispositionTypes.delete(type);
|
||||||
}
|
}
|
||||||
else if (_typeHandlers[type]) {
|
else if (_typeHandlers[type]) {
|
||||||
var i = _typeHandlers[type].indexOf(handler);
|
var i = _typeHandlers[type].indexOf(handlers);
|
||||||
if (i != -1) {
|
if (i != -1) {
|
||||||
_typeHandlers.splice(i, 1);
|
_typeHandlers[type].splice(i, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -132,8 +142,10 @@ Zotero.MIMETypeHandler = new function () {
|
||||||
if(channel.loadFlags & Components.interfaces.nsIHttpChannel.LOAD_DOCUMENT_URI) {
|
if(channel.loadFlags & Components.interfaces.nsIHttpChannel.LOAD_DOCUMENT_URI) {
|
||||||
channel.QueryInterface(Components.interfaces.nsIHttpChannel);
|
channel.QueryInterface(Components.interfaces.nsIHttpChannel);
|
||||||
try {
|
try {
|
||||||
|
// Get the main directive of the Content-Type header
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type#syntax
|
||||||
|
var contentType = channel.getResponseHeader("Content-Type").split(';')[0].toLowerCase();
|
||||||
// remove content-disposition headers for EndNote, etc.
|
// remove content-disposition headers for EndNote, etc.
|
||||||
var contentType = channel.getResponseHeader("Content-Type").toLowerCase();
|
|
||||||
for (let handledType of _ignoreContentDispositionTypes) {
|
for (let handledType of _ignoreContentDispositionTypes) {
|
||||||
if (contentType.startsWith(handledType)) {
|
if (contentType.startsWith(handledType)) {
|
||||||
channel.setResponseHeader("Content-Disposition", "inline", false);
|
channel.setResponseHeader("Content-Disposition", "inline", false);
|
||||||
|
|
@ -214,10 +226,27 @@ Zotero.MIMETypeHandler = new function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the request is started; we ignore this
|
* Called when the request is started
|
||||||
*/
|
*/
|
||||||
_StreamListener.prototype.onStartRequest = function(channel, context) {}
|
_StreamListener.prototype.onStartRequest = async function(channel, context) {
|
||||||
|
try {
|
||||||
|
if (!_typeHandlers[this._contentType]) return;
|
||||||
|
for (let handlers of _typeHandlers[this._contentType]) {
|
||||||
|
if (!handlers.onStartRequest) continue;
|
||||||
|
let maybePromise = handlers.onStartRequest(
|
||||||
|
this._request.name ? this._request.name : null,
|
||||||
|
this._contentType,
|
||||||
|
channel
|
||||||
|
);
|
||||||
|
if (maybePromise && maybePromise.then) {
|
||||||
|
maybePromise = await maybePromise;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
Zotero.logError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when there's data available; we collect this data and keep it until the request is
|
* Called when there's data available; we collect this data and keep it until the request is
|
||||||
|
|
@ -259,14 +288,16 @@ Zotero.MIMETypeHandler = new function () {
|
||||||
|
|
||||||
var handled = false;
|
var handled = false;
|
||||||
try {
|
try {
|
||||||
for (let handler of _typeHandlers[this._contentType]) {
|
if (!_typeHandlers[this._contentType]) return;
|
||||||
let maybePromise = handler(
|
for (let handlers of _typeHandlers[this._contentType]) {
|
||||||
|
if (!handlers.onContent) continue;
|
||||||
|
let maybePromise = handlers.onContent(
|
||||||
blob,
|
blob,
|
||||||
this._request.name ? this._request.name : null,
|
this._request.name ? this._request.name : null,
|
||||||
this._contentType,
|
this._contentType,
|
||||||
channel
|
channel
|
||||||
);
|
);
|
||||||
if (maybePromise.then) {
|
if (maybePromise && maybePromise.then) {
|
||||||
maybePromise = await maybePromise;
|
maybePromise = await maybePromise;
|
||||||
}
|
}
|
||||||
handled = handled || maybePromise;
|
handled = handled || maybePromise;
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
Zotero.Notifier = new function(){
|
Zotero.Notifier = new function () {
|
||||||
// Options that apply to an entire event, not a specific object
|
// Options that apply to an entire event, not a specific object
|
||||||
this.EVENT_LEVEL_OPTIONS = ['autoSyncDelay', 'skipAutoSync'];
|
this.EVENT_LEVEL_OPTIONS = ['autoSyncDelay', 'skipAutoSync'];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -271,6 +271,10 @@ class PDFWorker {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let path = await attachment.getFilePathAsync();
|
let path = await attachment.getFilePathAsync();
|
||||||
|
let fileSize = (await OS.File.stat(path)).size;
|
||||||
|
if (fileSize > Math.pow(2, 31) - 1) {
|
||||||
|
throw new Error(`The file "${path}" is too large`);
|
||||||
|
}
|
||||||
let buf = await OS.File.read(path, {});
|
let buf = await OS.File.read(path, {});
|
||||||
buf = new Uint8Array(buf).buffer;
|
buf = new Uint8Array(buf).buffer;
|
||||||
|
|
||||||
|
|
@ -341,6 +345,10 @@ class PDFWorker {
|
||||||
|
|
||||||
async processCitaviAnnotations(pdfPath, citaviAnnotations, isPriority, password) {
|
async processCitaviAnnotations(pdfPath, citaviAnnotations, isPriority, password) {
|
||||||
return this._enqueue(async () => {
|
return this._enqueue(async () => {
|
||||||
|
let fileSize = (await OS.File.stat(pdfPath)).size;
|
||||||
|
if (fileSize > Math.pow(2, 31) - 1) {
|
||||||
|
throw new Error(`The file "${pdfPath}" is too large`);
|
||||||
|
}
|
||||||
let buf = await OS.File.read(pdfPath, {});
|
let buf = await OS.File.read(pdfPath, {});
|
||||||
buf = new Uint8Array(buf).buffer;
|
buf = new Uint8Array(buf).buffer;
|
||||||
try {
|
try {
|
||||||
|
|
@ -371,6 +379,10 @@ class PDFWorker {
|
||||||
*/
|
*/
|
||||||
async processMendeleyAnnotations(pdfPath, mendeleyAnnotations, isPriority, password) {
|
async processMendeleyAnnotations(pdfPath, mendeleyAnnotations, isPriority, password) {
|
||||||
return this._enqueue(async () => {
|
return this._enqueue(async () => {
|
||||||
|
let fileSize = (await OS.File.stat(pdfPath)).size;
|
||||||
|
if (fileSize > Math.pow(2, 31) - 1) {
|
||||||
|
throw new Error(`The file "${pdfPath}" is too large`);
|
||||||
|
}
|
||||||
let buf = await OS.File.read(pdfPath, {});
|
let buf = await OS.File.read(pdfPath, {});
|
||||||
buf = new Uint8Array(buf).buffer;
|
buf = new Uint8Array(buf).buffer;
|
||||||
try {
|
try {
|
||||||
|
|
@ -410,6 +422,162 @@ class PDFWorker {
|
||||||
}
|
}
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete pages from PDF attachment
|
||||||
|
*
|
||||||
|
* @param {Integer} itemID Attachment item id
|
||||||
|
* @param {Array} pageIndexes
|
||||||
|
* @param {Boolean} [isPriority]
|
||||||
|
* @param {String} [password]
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
async deletePages(itemID, pageIndexes, isPriority, password) {
|
||||||
|
return this._enqueue(async () => {
|
||||||
|
let attachment = await Zotero.Items.getAsync(itemID);
|
||||||
|
|
||||||
|
Zotero.debug(`Deleting [${pageIndexes.join(', ')}] pages for item ${attachment.libraryKey}`);
|
||||||
|
let t = new Date();
|
||||||
|
|
||||||
|
if (!attachment.isPDFAttachment()) {
|
||||||
|
throw new Error('Item must be a PDF attachment');
|
||||||
|
}
|
||||||
|
|
||||||
|
let annotations = attachment
|
||||||
|
.getAnnotations()
|
||||||
|
.map(annotation => ({
|
||||||
|
id: annotation.id,
|
||||||
|
position: JSON.parse(annotation.annotationPosition)
|
||||||
|
}));
|
||||||
|
|
||||||
|
let path = await attachment.getFilePathAsync();
|
||||||
|
let buf = await OS.File.read(path, {});
|
||||||
|
buf = new Uint8Array(buf).buffer;
|
||||||
|
|
||||||
|
try {
|
||||||
|
var { buf: modifiedBuf } = await this._query('deletePages', {
|
||||||
|
buf, pageIndexes, password
|
||||||
|
}, [buf]);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
let error = new Error(`Worker 'deletePages' failed: ${JSON.stringify({ error: e.message })}`);
|
||||||
|
try {
|
||||||
|
error.name = JSON.parse(e.message).name;
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
Zotero.logError(e);
|
||||||
|
}
|
||||||
|
Zotero.logError(error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete annotations from deleted pages
|
||||||
|
let ids = [];
|
||||||
|
for (let i = annotations.length - 1; i >= 0; i--) {
|
||||||
|
let { id, position } = annotations[i];
|
||||||
|
if (pageIndexes.includes(position.pageIndex)) {
|
||||||
|
ids.push(id);
|
||||||
|
annotations.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ids.length) {
|
||||||
|
await Zotero.Items.erase(ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift page index for other annotations
|
||||||
|
ids = [];
|
||||||
|
await Zotero.DB.executeTransaction(async function () {
|
||||||
|
let rows = await Zotero.DB.queryAsync('SELECT itemID, position FROM itemAnnotations WHERE parentItemID=?', itemID);
|
||||||
|
for (let { itemID, position } of rows) {
|
||||||
|
try {
|
||||||
|
position = JSON.parse(position);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
Zotero.logError(e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Find the count of deleted pages before the current annotation page
|
||||||
|
let shift = pageIndexes.reduce((prev, cur) => cur < position.pageIndex ? prev + 1 : prev, 0);
|
||||||
|
if (shift > 0) {
|
||||||
|
position.pageIndex -= shift;
|
||||||
|
position = JSON.stringify(position);
|
||||||
|
await Zotero.DB.queryAsync('UPDATE itemAnnotations SET position=? WHERE itemID=?', [position, itemID]);
|
||||||
|
ids.push(itemID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let objectsClass = Zotero.DataObjectUtilities.getObjectsClassForObjectType('item');
|
||||||
|
let loadedObjects = objectsClass.getLoaded();
|
||||||
|
for (let object of loadedObjects) {
|
||||||
|
if (ids.includes(object.id)) {
|
||||||
|
await object.reload(null, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Zotero.Notifier.trigger('modify', 'item', ids, {});
|
||||||
|
|
||||||
|
await OS.File.writeAtomic(path, new Uint8Array(modifiedBuf));
|
||||||
|
let mtime = Math.floor(await attachment.attachmentModificationTime / 1000);
|
||||||
|
attachment.attachmentLastProcessedModificationTime = mtime;
|
||||||
|
await attachment.saveTx({
|
||||||
|
skipAll: true
|
||||||
|
});
|
||||||
|
|
||||||
|
Zotero.debug(`Deleted pages for item ${attachment.libraryKey} in ${new Date() - t} ms`);
|
||||||
|
}, isPriority);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotate pages in PDF attachment
|
||||||
|
*
|
||||||
|
* @param {Integer} itemID Attachment item id
|
||||||
|
* @param {Array} pageIndexes
|
||||||
|
* @param {Integer} degrees 90, 180, 270
|
||||||
|
* @param {Boolean} [isPriority]
|
||||||
|
* @param {String} [password]
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
async rotatePages(itemID, pageIndexes, degrees, isPriority, password) {
|
||||||
|
return this._enqueue(async () => {
|
||||||
|
let attachment = await Zotero.Items.getAsync(itemID);
|
||||||
|
|
||||||
|
Zotero.debug(`Rotating [${pageIndexes.join(', ')}] pages for item ${attachment.libraryKey}`);
|
||||||
|
let t = new Date();
|
||||||
|
|
||||||
|
if (!attachment.isPDFAttachment()) {
|
||||||
|
throw new Error('Item must be a PDF attachment');
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = await attachment.getFilePathAsync();
|
||||||
|
let buf = await OS.File.read(path, {});
|
||||||
|
buf = new Uint8Array(buf).buffer;
|
||||||
|
|
||||||
|
try {
|
||||||
|
var { buf: modifiedBuf } = await this._query('rotatePages', {
|
||||||
|
buf, pageIndexes, degrees, password
|
||||||
|
}, [buf]);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
let error = new Error(`Worker 'rotatePages' failed: ${JSON.stringify({ error: e.message })}`);
|
||||||
|
try {
|
||||||
|
error.name = JSON.parse(e.message).name;
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
Zotero.logError(e);
|
||||||
|
}
|
||||||
|
Zotero.logError(error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
await OS.File.writeAtomic(path, new Uint8Array(modifiedBuf));
|
||||||
|
let mtime = Math.floor(await attachment.attachmentModificationTime / 1000);
|
||||||
|
attachment.attachmentLastProcessedModificationTime = mtime;
|
||||||
|
await attachment.saveTx({
|
||||||
|
skipAll: true
|
||||||
|
});
|
||||||
|
|
||||||
|
Zotero.debug(`Rotated pages for item ${attachment.libraryKey} in ${new Date() - t} ms`);
|
||||||
|
}, isPriority);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.PDFWorker = new PDFWorker();
|
Zotero.PDFWorker = new PDFWorker();
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
***** END LICENSE BLOCK *****
|
***** END LICENSE BLOCK *****
|
||||||
*/
|
*/
|
||||||
Zotero.Prefs = new function(){
|
Zotero.Prefs = new function () {
|
||||||
// Privileged methods
|
// Privileged methods
|
||||||
this.get = get;
|
this.get = get;
|
||||||
this.set = set;
|
this.set = set;
|
||||||
|
|
@ -49,7 +49,7 @@ Zotero.Prefs = new function(){
|
||||||
if (!fromVersion) {
|
if (!fromVersion) {
|
||||||
fromVersion = 0;
|
fromVersion = 0;
|
||||||
}
|
}
|
||||||
var toVersion = 5;
|
var toVersion = 6;
|
||||||
if (fromVersion < toVersion) {
|
if (fromVersion < toVersion) {
|
||||||
for (var i = fromVersion + 1; i <= toVersion; i++) {
|
for (var i = fromVersion + 1; i <= toVersion; i++) {
|
||||||
switch (i) {
|
switch (i) {
|
||||||
|
|
@ -85,6 +85,24 @@ Zotero.Prefs = new function(){
|
||||||
case 5:
|
case 5:
|
||||||
this.clear('extensions.spellcheck.inline.max-misspellings', true);
|
this.clear('extensions.spellcheck.inline.max-misspellings', true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// If the note Quick Copy setting was set to Markdown + Rich Text but in a
|
||||||
|
// non-default way (e.g., because of whitespace differences), clear the pref
|
||||||
|
// to pick up the new app-link options
|
||||||
|
case 6:
|
||||||
|
var o = this.get('export.noteQuickCopy.setting');
|
||||||
|
try {
|
||||||
|
o = JSON.parse(o);
|
||||||
|
if (o.mode == 'export'
|
||||||
|
&& o.id == Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) {
|
||||||
|
this.clear('export.noteQuickCopy.setting');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
Zotero.logError(e);
|
||||||
|
this.clear('export.noteQuickCopy.setting');
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.set('prefVersion', toVersion);
|
this.set('prefVersion', toVersion);
|
||||||
|
|
@ -230,6 +248,7 @@ Zotero.Prefs = new function(){
|
||||||
Zotero.setFontSize(
|
Zotero.setFontSize(
|
||||||
Zotero.getActiveZoteroPane().document.getElementById('zotero-pane')
|
Zotero.getActiveZoteroPane().document.getElementById('zotero-pane')
|
||||||
);
|
);
|
||||||
|
Zotero.setFontSize(Zotero.getActiveZoteroPane().document.getElementById('zotero-context-pane'));
|
||||||
Zotero.getActiveZoteroPane().collectionsView && Zotero.getActiveZoteroPane().collectionsView.updateFontSize();
|
Zotero.getActiveZoteroPane().collectionsView && Zotero.getActiveZoteroPane().collectionsView.updateFontSize();
|
||||||
Zotero.getActiveZoteroPane().itemsView && Zotero.getActiveZoteroPane().itemsView.updateFontSize();
|
Zotero.getActiveZoteroPane().itemsView && Zotero.getActiveZoteroPane().itemsView.updateFontSize();
|
||||||
}],
|
}],
|
||||||
|
|
@ -358,12 +377,13 @@ Zotero.Prefs = new function(){
|
||||||
delete _observersBySymbol[symbol];
|
delete _observersBySymbol[symbol];
|
||||||
|
|
||||||
var [name, handler] = obs;
|
var [name, handler] = obs;
|
||||||
var i = obs.indexOf(handler);
|
var handlers = _observers[name];
|
||||||
|
var i = handlers.indexOf(handler);
|
||||||
if (i == -1) {
|
if (i == -1) {
|
||||||
Zotero.debug("Handler was not registered for preference " + name, 2);
|
Zotero.debug("Handler was not registered for preference " + name, 2);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
obs.splice(i, 1);
|
handlers.splice(i, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
87
chrome/content/zotero/xpcom/prompt.js
Normal file
87
chrome/content/zotero/xpcom/prompt.js
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Copyright © 2022 Corporation for Digital Scholarship
|
||||||
|
Vienna, 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 *****
|
||||||
|
*/
|
||||||
|
|
||||||
|
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
|
Zotero.Prompt = {
|
||||||
|
BUTTON_TITLE_OK: Services.prompt.BUTTON_TITLE_OK,
|
||||||
|
BUTTON_TITLE_CANCEL: Services.prompt.BUTTON_TITLE_CANCEL,
|
||||||
|
BUTTON_TITLE_YES: Services.prompt.BUTTON_TITLE_YES,
|
||||||
|
BUTTON_TITLE_NO: Services.prompt.BUTTON_TITLE_NO,
|
||||||
|
BUTTON_TITLE_SAVE: Services.prompt.BUTTON_TITLE_SAVE,
|
||||||
|
BUTTON_TITLE_DONT_SAVE: Services.prompt.BUTTON_TITLE_DONT_SAVE,
|
||||||
|
BUTTON_TITLE_REVERT: Services.prompt.BUTTON_TITLE_REVERT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper around XPCOM's Services.prompt.confirmEx()
|
||||||
|
* but with a friendlier interface.
|
||||||
|
*
|
||||||
|
* Button text can use special static variables from
|
||||||
|
* Zotero.Prompt
|
||||||
|
*
|
||||||
|
* @param options
|
||||||
|
* - {mozIDOMWindowProxy} window - The parent window or null.
|
||||||
|
* - {String} title - Text to appear in the title of the dialog.
|
||||||
|
* - {String} text - Text to appear in the body of the dialog.
|
||||||
|
* - {String|Number} button0 - Button 0 text
|
||||||
|
* - {String|Number} button1 - Button 1 text
|
||||||
|
* - {String|Number} button2 - Button 2 text
|
||||||
|
* - {String} checkLabel - Text to appear with the checkbox.
|
||||||
|
* - {Object} checkbox - Contains the initial checked state of the
|
||||||
|
* checkbox when this method is called and the final checked
|
||||||
|
* state after this method returns. Either {} or { value: true/false }.
|
||||||
|
* - {Number} defaultButton - The index of default button. 0 by default
|
||||||
|
* - {Boolean} delayButtons - Make the buttons initially disabled and enable them after some period
|
||||||
|
* so that the user doesn't click through the dialog without reading it.
|
||||||
|
* @returns {Number} The index of the button pressed.
|
||||||
|
*/
|
||||||
|
confirm(options = {}) {
|
||||||
|
let { window: win, title, text, button0, button1, button2, checkLabel, checkbox, defaultButton, delayButtons } = options;
|
||||||
|
if (!win) win = null;
|
||||||
|
if (!title) throw new Error('`title` is required');
|
||||||
|
if (!text) throw new Error('`text` is required');
|
||||||
|
if (!button0 && !button1 && !button2) {
|
||||||
|
throw new Error('At least one button is required');
|
||||||
|
}
|
||||||
|
if (checkLabel && (!checkbox || typeof checkbox != 'object')) {
|
||||||
|
throw new Error('`checkLabel` provided without `checkbox` option');
|
||||||
|
}
|
||||||
|
let flags = delayButtons ? Services.prompt.BUTTON_DELAY_ENABLE : 0;
|
||||||
|
if (typeof button0 == 'number') flags += Services.prompt.BUTTON_POS_0 * button0;
|
||||||
|
else if (typeof button0 == 'string') flags += Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_IS_STRING;
|
||||||
|
if (typeof button1 == 'number') flags += Services.prompt.BUTTON_POS_1 * button1;
|
||||||
|
else if (typeof button1 == 'string') flags += Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_IS_STRING;
|
||||||
|
if (typeof button2 == 'number') flags += Services.prompt.BUTTON_POS_2 * button2;
|
||||||
|
else if (typeof button2 == 'string') flags += Services.prompt.BUTTON_POS_2 * Services.prompt.BUTTON_TITLE_IS_STRING;
|
||||||
|
if (defaultButton) flags += defaultButton == 1 ? Services.prompt.BUTTON_POS_1_DEFAULT : Services.prompt.BUTTON_POS_2_DEFAULT;
|
||||||
|
return Services.prompt.confirmEx(
|
||||||
|
win, title, text, flags,
|
||||||
|
typeof button0 == 'number' ? null : button0,
|
||||||
|
typeof button1 == 'number' ? null : button1,
|
||||||
|
typeof button2 == 'number' ? null : button2,
|
||||||
|
checkLabel, checkbox
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -23,6 +23,8 @@
|
||||||
***** END LICENSE BLOCK *****
|
***** END LICENSE BLOCK *****
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
Components.utils.importGlobalProperties(["URL"]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A singleton to handle URL rewriting proxies
|
* A singleton to handle URL rewriting proxies
|
||||||
* @namespace
|
* @namespace
|
||||||
|
|
@ -138,6 +140,8 @@ Zotero.Proxies = new function() {
|
||||||
* @type String
|
* @type String
|
||||||
*/
|
*/
|
||||||
this.proxyToProper = function(url, onlyReturnIfProxied) {
|
this.proxyToProper = function(url, onlyReturnIfProxied) {
|
||||||
|
// make sure url has a trailing slash
|
||||||
|
url = new URL(url).href;
|
||||||
for (let proxy of Zotero.Proxies.proxies) {
|
for (let proxy of Zotero.Proxies.proxies) {
|
||||||
if(proxy.regexp) {
|
if(proxy.regexp) {
|
||||||
var m = proxy.regexp.exec(url);
|
var m = proxy.regexp.exec(url);
|
||||||
|
|
@ -173,16 +177,18 @@ Zotero.Proxies = new function() {
|
||||||
* Check the url for potential proxies and deproxify, providing a scheme to build
|
* Check the url for potential proxies and deproxify, providing a scheme to build
|
||||||
* a proxy object.
|
* a proxy object.
|
||||||
*
|
*
|
||||||
* @param URL
|
* @param url
|
||||||
* @returns {Object} Unproxied url to proxy object
|
* @returns {Object} Unproxied url to proxy object
|
||||||
*/
|
*/
|
||||||
this.getPotentialProxies = function(URL) {
|
this.getPotentialProxies = function(url) {
|
||||||
|
// make sure url has a trailing slash
|
||||||
|
url = new URL(url).href;
|
||||||
var urlToProxy = {};
|
var urlToProxy = {};
|
||||||
// If it's a known proxied URL just return it
|
// If it's a known proxied URL just return it
|
||||||
if (Zotero.Proxies.transparent) {
|
if (Zotero.Proxies.transparent) {
|
||||||
for (var proxy of Zotero.Proxies.proxies) {
|
for (var proxy of Zotero.Proxies.proxies) {
|
||||||
if (proxy.regexp) {
|
if (proxy.regexp) {
|
||||||
var m = proxy.regexp.exec(URL);
|
var m = proxy.regexp.exec(url);
|
||||||
if (m) {
|
if (m) {
|
||||||
let proper = proxy.toProper(m);
|
let proper = proxy.toProper(m);
|
||||||
urlToProxy[proper] = proxy.toJSON();
|
urlToProxy[proper] = proxy.toJSON();
|
||||||
|
|
@ -191,12 +197,12 @@ Zotero.Proxies = new function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
urlToProxy[URL] = null;
|
urlToProxy[url] = null;
|
||||||
|
|
||||||
// if there is a subdomain that is also a TLD, also test against URI with the domain
|
// if there is a subdomain that is also a TLD, also test against URI with the domain
|
||||||
// dropped after the TLD
|
// dropped after the TLD
|
||||||
// (i.e., www.nature.com.mutex.gmu.edu => www.nature.com)
|
// (i.e., www.nature.com.mutex.gmu.edu => www.nature.com)
|
||||||
var m = /^(https?:\/\/)([^\/]+)/i.exec(URL);
|
var m = /^(https?:\/\/)([^\/]+)/i.exec(url);
|
||||||
if (m) {
|
if (m) {
|
||||||
// First, drop the 0- if it exists (this is an III invention)
|
// First, drop the 0- if it exists (this is an III invention)
|
||||||
var host = m[2];
|
var host = m[2];
|
||||||
|
|
@ -211,16 +217,15 @@ Zotero.Proxies = new function() {
|
||||||
for (let i=0; i < hostnameParts.length; i++) {
|
for (let i=0; i < hostnameParts.length; i++) {
|
||||||
let parts = hostnameParts[i];
|
let parts = hostnameParts[i];
|
||||||
// If hostnameParts has two entries, then the second one is with replaced hyphens
|
// If hostnameParts has two entries, then the second one is with replaced hyphens
|
||||||
let dotsToHyphens = i == 1;
|
|
||||||
// skip the lowest level subdomain, domain and TLD
|
// skip the lowest level subdomain, domain and TLD
|
||||||
for (let j=1; j<parts.length-2; j++) {
|
for (let j=1; j<parts.length-2; j++) {
|
||||||
// if a part matches a TLD, everything up to it is probably the true URL
|
// if a part matches a TLD, everything up to it is probably the true URL
|
||||||
if (TLDS[parts[j].toLowerCase()]) {
|
if (TLDS[parts[j].toLowerCase()]) {
|
||||||
var properHost = parts.slice(0, j+1).join(".");
|
var properHost = parts.slice(0, j+1).join(".");
|
||||||
// protocol + properHost + /path
|
// protocol + properHost + /path
|
||||||
var properURL = m[1]+properHost+URL.substr(m[0].length);
|
var properURL = m[1]+properHost+url.substr(m[0].length);
|
||||||
var proxyHost = parts.slice(j+1).join('.');
|
var proxyHost = parts.slice(j+1).join('.');
|
||||||
urlToProxy[properURL] = {scheme: m[1] + '%h.' + proxyHost + '/%p', dotsToHyphens};
|
urlToProxy[properURL] = {scheme: m[1] + '%h.' + proxyHost + '/%p'};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -249,10 +254,6 @@ Zotero.Proxy.prototype._loadFromRow = function (row) {
|
||||||
this.multiHost = row.scheme && row.scheme.indexOf('%h') != -1 || !!row.multiHost;
|
this.multiHost = row.scheme && row.scheme.indexOf('%h') != -1 || !!row.multiHost;
|
||||||
this.autoAssociate = !!row.autoAssociate;
|
this.autoAssociate = !!row.autoAssociate;
|
||||||
this.scheme = row.scheme;
|
this.scheme = row.scheme;
|
||||||
// Database query results will throw as this option is only present when the proxy comes along with the translator
|
|
||||||
if ('dotsToHyphens' in row) {
|
|
||||||
this.dotsToHyphens = !!row.dotsToHyphens;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.scheme) {
|
if (this.scheme) {
|
||||||
this.compileRegexp();
|
this.compileRegexp();
|
||||||
|
|
@ -263,7 +264,7 @@ Zotero.Proxy.prototype.toJSON = function() {
|
||||||
if (!this.scheme) {
|
if (!this.scheme) {
|
||||||
throw Error('Cannot convert proxy to JSON - no scheme');
|
throw Error('Cannot convert proxy to JSON - no scheme');
|
||||||
}
|
}
|
||||||
return {id: this.id, scheme: this.scheme, dotsToHyphens: this.dotsToHyphens};
|
return {id: this.id, scheme: this.scheme};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -456,6 +457,8 @@ Zotero.Proxy.prototype.erase = Zotero.Promise.coroutine(function* () {
|
||||||
*/
|
*/
|
||||||
Zotero.Proxy.prototype.toProper = function(m) {
|
Zotero.Proxy.prototype.toProper = function(m) {
|
||||||
if (!Array.isArray(m)) {
|
if (!Array.isArray(m)) {
|
||||||
|
// make sure url has a trailing slash
|
||||||
|
m = new URL(m).href;
|
||||||
let match = this.regexp.exec(m);
|
let match = this.regexp.exec(m);
|
||||||
if (!match) {
|
if (!match) {
|
||||||
return m
|
return m
|
||||||
|
|
@ -463,16 +466,16 @@ Zotero.Proxy.prototype.toProper = function(m) {
|
||||||
m = match;
|
m = match;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let scheme = this.scheme.indexOf('https') == -1 ? 'http://' : 'https://';
|
let protocol = m[0].indexOf('https') == -1 ? 'http://' : 'https://';
|
||||||
if(this.multiHost) {
|
if(this.multiHost) {
|
||||||
var properURL = scheme+m[this.parameters.indexOf("%h")+1]+"/";
|
var properURL = protocol+m[this.parameters.indexOf("%h")+1]+"/";
|
||||||
} else {
|
} else {
|
||||||
var properURL = scheme+this.hosts[0]+"/";
|
var properURL = protocol+this.hosts[0]+"/";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace `-` with `.` in https to support EZProxy HttpsHyphens.
|
// Replace `-` with `.` in https to support EZProxy HttpsHyphens.
|
||||||
// Potentially troublesome with domains that contain dashes
|
// Potentially troublesome with domains that contain dashes
|
||||||
if (this.dotsToHyphens) {
|
if (protocol.includes('https')) {
|
||||||
properURL = properURL.replace(/-/g, '.');
|
properURL = properURL.replace(/-/g, '.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -507,7 +510,7 @@ Zotero.Proxy.prototype.toProxy = function(uri) {
|
||||||
var param = this.parameters[i];
|
var param = this.parameters[i];
|
||||||
var value = "";
|
var value = "";
|
||||||
if(param == "%h") {
|
if(param == "%h") {
|
||||||
value = this.dotsToHyphens ? uri.hostPort.replace(/-/g, '.') : uri.hostPort;
|
value = uri.scheme === 'https' ? uri.hostPort.replace(/-/g, '.') : uri.hostPort;
|
||||||
} else if(param == "%p") {
|
} else if(param == "%p") {
|
||||||
value = uri.pathQueryRef.substr(1);
|
value = uri.pathQueryRef.substr(1);
|
||||||
} else if(param == "%d") {
|
} else if(param == "%d") {
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue