33 | ¬found-tag-desc;
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/bootstrap.js:
--------------------------------------------------------------------------------
1 | if (typeof Zotero == 'undefined') {
2 | var Zotero;
3 | }
4 | var ShortDOI;
5 | var chromeHandle;
6 |
7 | let mainWindowListener;
8 |
9 | function log(msg) {
10 | Zotero.debug("DOI Manager: " + msg);
11 | }
12 |
13 | // In Zotero 6, bootstrap methods are called before Zotero is initialized, and using include.js
14 | // to get the Zotero XPCOM service would risk breaking Zotero startup. Instead, wait for the main
15 | // Zotero window to open and get the Zotero object from there.
16 | //
17 | // In Zotero 7, bootstrap methods are not called until Zotero is initialized, and the 'Zotero' is
18 | // automatically made available.
19 | async function waitForZotero() {
20 | if (typeof Zotero != 'undefined') {
21 | await Zotero.initializationPromise;
22 | return;
23 | }
24 |
25 | var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
26 | var windows = Services.wm.getEnumerator('navigator:browser');
27 | var found = false;
28 | while (windows.hasMoreElements()) {
29 | let win = windows.getNext();
30 | if (win.Zotero) {
31 | Zotero = win.Zotero;
32 | found = true;
33 | break;
34 | }
35 | }
36 | if (!found) {
37 | await new Promise((resolve) => {
38 | var listener = {
39 | onOpenWindow: function (aWindow) {
40 | // Wait for the window to finish loading
41 | let domWindow = aWindow
42 | .QueryInterface(Ci.nsIInterfaceRequestor)
43 | .getInterface(
44 | Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow
45 | );
46 | domWindow.addEventListener(
47 | "load",
48 | function () {
49 | domWindow.removeEventListener(
50 | "load",
51 | arguments.callee,
52 | false
53 | );
54 | if (domWindow.Zotero) {
55 | Services.wm.removeListener(listener);
56 | Zotero = domWindow.Zotero;
57 | resolve();
58 | }
59 | },
60 | false
61 | );
62 | },
63 | };
64 | Services.wm.addListener(listener);
65 | });
66 | }
67 | await Zotero.initializationPromise;
68 | }
69 |
70 | // Adds main window open/close listeners in Zotero 6
71 | function listenForMainWindowEvents() {
72 | mainWindowListener = {
73 | onOpenWindow: function (aWindow) {
74 | let domWindow = aWindow
75 | .QueryInterface(Ci.nsIInterfaceRequestor)
76 | .getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
77 | async function onload() {
78 | domWindow.removeEventListener("load", onload, false);
79 | if (
80 | domWindow.location.href !==
81 | "chrome://zotero/content/standalone/standalone.xul"
82 | ) {
83 | return;
84 | }
85 | onMainWindowLoad({ window: domWindow });
86 | }
87 | domWindow.addEventListener("load", onload, false);
88 | },
89 | onCloseWindow: async function (aWindow) {
90 | let domWindow = aWindow
91 | .QueryInterface(Ci.nsIInterfaceRequestor)
92 | .getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
93 | if (
94 | domWindow.location.href !==
95 | "chrome://zotero/content/standalone/standalone.xul"
96 | ) {
97 | return;
98 | }
99 | onMainWindowUnload({ window: domWindow });
100 | },
101 | };
102 | Services.wm.addListener(mainWindowListener);
103 | }
104 |
105 | function removeMainWindowListener() {
106 | if (mainWindowListener) {
107 | Services.wm.removeListener(mainWindowListener);
108 | }
109 | }
110 |
111 | // Loads default preferences from prefs.js in Zotero 6
112 | function setDefaultPrefs(rootURI) {
113 | var branch = Services.prefs.getDefaultBranch("");
114 | var obj = {
115 | pref(pref, value) {
116 | switch (typeof value) {
117 | case 'boolean':
118 | branch.setBoolPref(pref, value);
119 | break;
120 | case 'string':
121 | branch.setStringPref(pref, value);
122 | break;
123 | case 'number':
124 | branch.setIntPref(pref, value);
125 | break;
126 | default:
127 | Zotero.logError(`Invalid type '${typeof (value)}' for pref '${pref}'`);
128 | }
129 | },
130 | };
131 | Services.scriptloader.loadSubScript(rootURI + "prefs.js", obj);
132 | }
133 |
134 | async function install() {
135 | await waitForZotero();
136 |
137 | log("Installed");
138 | }
139 |
140 | async function startup({ id, version, resourceURI, rootURI = resourceURI.spec }) {
141 | await waitForZotero();
142 |
143 | log("Starting");
144 |
145 | // 'Services' may not be available in Zotero 6
146 | if (typeof Services == 'undefined') {
147 | var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
148 | }
149 |
150 | if (Zotero.platformMajorVersion < 102) {
151 | // Listen for window load/unload events in Zotero 6, since onMainWindowLoad/Unload don't
152 | // get called
153 | listenForMainWindowEvents();
154 | // Read prefs from prefs.js in Zotero 6
155 | setDefaultPrefs(rootURI);
156 | }
157 |
158 | var aomStartup = Cc[
159 | "@mozilla.org/addons/addon-manager-startup;1"
160 | ].getService(Ci.amIAddonManagerStartup);
161 | var manifestURI = Services.io.newURI(rootURI + "manifest.json");
162 | chromeHandle = aomStartup.registerChrome(manifestURI, [
163 | ["locale", "zoteroshortdoi", "en-US", "locale/en-US/"],
164 | ["locale", "zoteroshortdoi", "de", "locale/de/"],
165 | ]);
166 |
167 | Services.scriptloader.loadSubScript(rootURI + "zoteroshortdoi.js");
168 |
169 | ShortDOI.init({ id, version, rootURI });
170 |
171 | if (Zotero.platformMajorVersion >= 102) {
172 | Zotero.PreferencePanes.register({
173 | pluginID: 'zoteroshortdoi@wiernik.org',
174 | src: rootURI + 'content/options.xhtml',
175 | //scripts: ['prefs.js'],
176 | //stylesheets: ['prefs.css'],
177 | });
178 | }
179 |
180 | ShortDOI.addToAllWindows();
181 | }
182 |
183 | function onMainWindowLoad({ window }) {
184 | ShortDOI.addToWindow(window);
185 | }
186 |
187 | function onMainWindowUnload({ window }) {
188 | ShortDOI.removeFromWindow(window);
189 |
190 | window.addEventListener(
191 | "unload",
192 | function (e) {
193 | Zotero.Notifier.unregisterObserver(ShortDOI.notifierID);
194 | },
195 | false
196 | );
197 | }
198 |
199 | function shutdown() {
200 | log("Shutting down");
201 |
202 | if (Zotero.platformMajorVersion < 102) {
203 | removeMainWindowListener();
204 | }
205 |
206 | chromeHandle.destruct();
207 | chromeHandle = null;
208 |
209 | ShortDOI.removeFromAllWindows();
210 | ShortDOI = undefined;
211 | }
212 |
213 | function uninstall() {
214 | // `Zotero` object isn't available in `uninstall()` in Zotero 6, so log manually
215 | if (typeof Zotero == 'undefined') {
216 | dump("DOI Manager: Uninstalled\n\n");
217 | return;
218 | }
219 |
220 | log("Uninstalled");
221 | }
222 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Mozilla Public License Version 2.0
2 | ==================================
3 |
4 | 1. Definitions
5 | --------------
6 |
7 | 1.1. "Contributor"
8 | means each individual or legal entity that creates, contributes to
9 | the creation of, or owns Covered Software.
10 |
11 | 1.2. "Contributor Version"
12 | means the combination of the Contributions of others (if any) used
13 | by a Contributor and that particular Contributor's Contribution.
14 |
15 | 1.3. "Contribution"
16 | means Covered Software of a particular Contributor.
17 |
18 | 1.4. "Covered Software"
19 | means Source Code Form to which the initial Contributor has attached
20 | the notice in Exhibit A, the Executable Form of such Source Code
21 | Form, and Modifications of such Source Code Form, in each case
22 | including portions thereof.
23 |
24 | 1.5. "Incompatible With Secondary Licenses"
25 | means
26 |
27 | (a) that the initial Contributor has attached the notice described
28 | in Exhibit B to the Covered Software; or
29 |
30 | (b) that the Covered Software was made available under the terms of
31 | version 1.1 or earlier of the License, but not also under the
32 | terms of a Secondary License.
33 |
34 | 1.6. "Executable Form"
35 | means any form of the work other than Source Code Form.
36 |
37 | 1.7. "Larger Work"
38 | means a work that combines Covered Software with other material, in
39 | a separate file or files, that is not Covered Software.
40 |
41 | 1.8. "License"
42 | means this document.
43 |
44 | 1.9. "Licensable"
45 | means having the right to grant, to the maximum extent possible,
46 | whether at the time of the initial grant or subsequently, any and
47 | all of the rights conveyed by this License.
48 |
49 | 1.10. "Modifications"
50 | means any of the following:
51 |
52 | (a) any file in Source Code Form that results from an addition to,
53 | deletion from, or modification of the contents of Covered
54 | Software; or
55 |
56 | (b) any new file in Source Code Form that contains any Covered
57 | Software.
58 |
59 | 1.11. "Patent Claims" of a Contributor
60 | means any patent claim(s), including without limitation, method,
61 | process, and apparatus claims, in any patent Licensable by such
62 | Contributor that would be infringed, but for the grant of the
63 | License, by the making, using, selling, offering for sale, having
64 | made, import, or transfer of either its Contributions or its
65 | Contributor Version.
66 |
67 | 1.12. "Secondary License"
68 | means either the GNU General Public License, Version 2.0, the GNU
69 | Lesser General Public License, Version 2.1, the GNU Affero General
70 | Public License, Version 3.0, or any later versions of those
71 | licenses.
72 |
73 | 1.13. "Source Code Form"
74 | means the form of the work preferred for making modifications.
75 |
76 | 1.14. "You" (or "Your")
77 | means an individual or a legal entity exercising rights under this
78 | License. For legal entities, "You" includes any entity that
79 | controls, is controlled by, or is under common control with You. For
80 | purposes of this definition, "control" means (a) the power, direct
81 | or indirect, to cause the direction or management of such entity,
82 | whether by contract or otherwise, or (b) ownership of more than
83 | fifty percent (50%) of the outstanding shares or beneficial
84 | ownership of such entity.
85 |
86 | 2. License Grants and Conditions
87 | --------------------------------
88 |
89 | 2.1. Grants
90 |
91 | Each Contributor hereby grants You a world-wide, royalty-free,
92 | non-exclusive license:
93 |
94 | (a) under intellectual property rights (other than patent or trademark)
95 | Licensable by such Contributor to use, reproduce, make available,
96 | modify, display, perform, distribute, and otherwise exploit its
97 | Contributions, either on an unmodified basis, with Modifications, or
98 | as part of a Larger Work; and
99 |
100 | (b) under Patent Claims of such Contributor to make, use, sell, offer
101 | for sale, have made, import, and otherwise transfer either its
102 | Contributions or its Contributor Version.
103 |
104 | 2.2. Effective Date
105 |
106 | The licenses granted in Section 2.1 with respect to any Contribution
107 | become effective for each Contribution on the date the Contributor first
108 | distributes such Contribution.
109 |
110 | 2.3. Limitations on Grant Scope
111 |
112 | The licenses granted in this Section 2 are the only rights granted under
113 | this License. No additional rights or licenses will be implied from the
114 | distribution or licensing of Covered Software under this License.
115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a
116 | Contributor:
117 |
118 | (a) for any code that a Contributor has removed from Covered Software;
119 | or
120 |
121 | (b) for infringements caused by: (i) Your and any other third party's
122 | modifications of Covered Software, or (ii) the combination of its
123 | Contributions with other software (except as part of its Contributor
124 | Version); or
125 |
126 | (c) under Patent Claims infringed by Covered Software in the absence of
127 | its Contributions.
128 |
129 | This License does not grant any rights in the trademarks, service marks,
130 | or logos of any Contributor (except as may be necessary to comply with
131 | the notice requirements in Section 3.4).
132 |
133 | 2.4. Subsequent Licenses
134 |
135 | No Contributor makes additional grants as a result of Your choice to
136 | distribute the Covered Software under a subsequent version of this
137 | License (see Section 10.2) or under the terms of a Secondary License (if
138 | permitted under the terms of Section 3.3).
139 |
140 | 2.5. Representation
141 |
142 | Each Contributor represents that the Contributor believes its
143 | Contributions are its original creation(s) or it has sufficient rights
144 | to grant the rights to its Contributions conveyed by this License.
145 |
146 | 2.6. Fair Use
147 |
148 | This License is not intended to limit any rights You have under
149 | applicable copyright doctrines of fair use, fair dealing, or other
150 | equivalents.
151 |
152 | 2.7. Conditions
153 |
154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
155 | in Section 2.1.
156 |
157 | 3. Responsibilities
158 | -------------------
159 |
160 | 3.1. Distribution of Source Form
161 |
162 | All distribution of Covered Software in Source Code Form, including any
163 | Modifications that You create or to which You contribute, must be under
164 | the terms of this License. You must inform recipients that the Source
165 | Code Form of the Covered Software is governed by the terms of this
166 | License, and how they can obtain a copy of this License. You may not
167 | attempt to alter or restrict the recipients' rights in the Source Code
168 | Form.
169 |
170 | 3.2. Distribution of Executable Form
171 |
172 | If You distribute Covered Software in Executable Form then:
173 |
174 | (a) such Covered Software must also be made available in Source Code
175 | Form, as described in Section 3.1, and You must inform recipients of
176 | the Executable Form how they can obtain a copy of such Source Code
177 | Form by reasonable means in a timely manner, at a charge no more
178 | than the cost of distribution to the recipient; and
179 |
180 | (b) You may distribute such Executable Form under the terms of this
181 | License, or sublicense it under different terms, provided that the
182 | license for the Executable Form does not attempt to limit or alter
183 | the recipients' rights in the Source Code Form under this License.
184 |
185 | 3.3. Distribution of a Larger Work
186 |
187 | You may create and distribute a Larger Work under terms of Your choice,
188 | provided that You also comply with the requirements of this License for
189 | the Covered Software. If the Larger Work is a combination of Covered
190 | Software with a work governed by one or more Secondary Licenses, and the
191 | Covered Software is not Incompatible With Secondary Licenses, this
192 | License permits You to additionally distribute such Covered Software
193 | under the terms of such Secondary License(s), so that the recipient of
194 | the Larger Work may, at their option, further distribute the Covered
195 | Software under the terms of either this License or such Secondary
196 | License(s).
197 |
198 | 3.4. Notices
199 |
200 | You may not remove or alter the substance of any license notices
201 | (including copyright notices, patent notices, disclaimers of warranty,
202 | or limitations of liability) contained within the Source Code Form of
203 | the Covered Software, except that You may alter any license notices to
204 | the extent required to remedy known factual inaccuracies.
205 |
206 | 3.5. Application of Additional Terms
207 |
208 | You may choose to offer, and to charge a fee for, warranty, support,
209 | indemnity or liability obligations to one or more recipients of Covered
210 | Software. However, You may do so only on Your own behalf, and not on
211 | behalf of any Contributor. You must make it absolutely clear that any
212 | such warranty, support, indemnity, or liability obligation is offered by
213 | You alone, and You hereby agree to indemnify every Contributor for any
214 | liability incurred by such Contributor as a result of warranty, support,
215 | indemnity or liability terms You offer. You may include additional
216 | disclaimers of warranty and limitations of liability specific to any
217 | jurisdiction.
218 |
219 | 4. Inability to Comply Due to Statute or Regulation
220 | ---------------------------------------------------
221 |
222 | If it is impossible for You to comply with any of the terms of this
223 | License with respect to some or all of the Covered Software due to
224 | statute, judicial order, or regulation then You must: (a) comply with
225 | the terms of this License to the maximum extent possible; and (b)
226 | describe the limitations and the code they affect. Such description must
227 | be placed in a text file included with all distributions of the Covered
228 | Software under this License. Except to the extent prohibited by statute
229 | or regulation, such description must be sufficiently detailed for a
230 | recipient of ordinary skill to be able to understand it.
231 |
232 | 5. Termination
233 | --------------
234 |
235 | 5.1. The rights granted under this License will terminate automatically
236 | if You fail to comply with any of its terms. However, if You become
237 | compliant, then the rights granted under this License from a particular
238 | Contributor are reinstated (a) provisionally, unless and until such
239 | Contributor explicitly and finally terminates Your grants, and (b) on an
240 | ongoing basis, if such Contributor fails to notify You of the
241 | non-compliance by some reasonable means prior to 60 days after You have
242 | come back into compliance. Moreover, Your grants from a particular
243 | Contributor are reinstated on an ongoing basis if such Contributor
244 | notifies You of the non-compliance by some reasonable means, this is the
245 | first time You have received notice of non-compliance with this License
246 | from such Contributor, and You become compliant prior to 30 days after
247 | Your receipt of the notice.
248 |
249 | 5.2. If You initiate litigation against any entity by asserting a patent
250 | infringement claim (excluding declaratory judgment actions,
251 | counter-claims, and cross-claims) alleging that a Contributor Version
252 | directly or indirectly infringes any patent, then the rights granted to
253 | You by any and all Contributors for the Covered Software under Section
254 | 2.1 of this License shall terminate.
255 |
256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all
257 | end user license agreements (excluding distributors and resellers) which
258 | have been validly granted by You or Your distributors under this License
259 | prior to termination shall survive termination.
260 |
261 | ************************************************************************
262 | * *
263 | * 6. Disclaimer of Warranty *
264 | * ------------------------- *
265 | * *
266 | * Covered Software is provided under this License on an "as is" *
267 | * basis, without warranty of any kind, either expressed, implied, or *
268 | * statutory, including, without limitation, warranties that the *
269 | * Covered Software is free of defects, merchantable, fit for a *
270 | * particular purpose or non-infringing. The entire risk as to the *
271 | * quality and performance of the Covered Software is with You. *
272 | * Should any Covered Software prove defective in any respect, You *
273 | * (not any Contributor) assume the cost of any necessary servicing, *
274 | * repair, or correction. This disclaimer of warranty constitutes an *
275 | * essential part of this License. No use of any Covered Software is *
276 | * authorized under this License except under this disclaimer. *
277 | * *
278 | ************************************************************************
279 |
280 | ************************************************************************
281 | * *
282 | * 7. Limitation of Liability *
283 | * -------------------------- *
284 | * *
285 | * Under no circumstances and under no legal theory, whether tort *
286 | * (including negligence), contract, or otherwise, shall any *
287 | * Contributor, or anyone who distributes Covered Software as *
288 | * permitted above, be liable to You for any direct, indirect, *
289 | * special, incidental, or consequential damages of any character *
290 | * including, without limitation, damages for lost profits, loss of *
291 | * goodwill, work stoppage, computer failure or malfunction, or any *
292 | * and all other commercial damages or losses, even if such party *
293 | * shall have been informed of the possibility of such damages. This *
294 | * limitation of liability shall not apply to liability for death or *
295 | * personal injury resulting from such party's negligence to the *
296 | * extent applicable law prohibits such limitation. Some *
297 | * jurisdictions do not allow the exclusion or limitation of *
298 | * incidental or consequential damages, so this exclusion and *
299 | * limitation may not apply to You. *
300 | * *
301 | ************************************************************************
302 |
303 | 8. Litigation
304 | -------------
305 |
306 | Any litigation relating to this License may be brought only in the
307 | courts of a jurisdiction where the defendant maintains its principal
308 | place of business and such litigation shall be governed by laws of that
309 | jurisdiction, without reference to its conflict-of-law provisions.
310 | Nothing in this Section shall prevent a party's ability to bring
311 | cross-claims or counter-claims.
312 |
313 | 9. Miscellaneous
314 | ----------------
315 |
316 | This License represents the complete agreement concerning the subject
317 | matter hereof. If any provision of this License is held to be
318 | unenforceable, such provision shall be reformed only to the extent
319 | necessary to make it enforceable. Any law or regulation which provides
320 | that the language of a contract shall be construed against the drafter
321 | shall not be used to construe this License against a Contributor.
322 |
323 | 10. Versions of the License
324 | ---------------------------
325 |
326 | 10.1. New Versions
327 |
328 | Mozilla Foundation is the license steward. Except as provided in Section
329 | 10.3, no one other than the license steward has the right to modify or
330 | publish new versions of this License. Each version will be given a
331 | distinguishing version number.
332 |
333 | 10.2. Effect of New Versions
334 |
335 | You may distribute the Covered Software under the terms of the version
336 | of the License under which You originally received the Covered Software,
337 | or under the terms of any subsequent version published by the license
338 | steward.
339 |
340 | 10.3. Modified Versions
341 |
342 | If you create software not governed by this License, and you want to
343 | create a new license for such software, you may create and use a
344 | modified version of this License if you rename the license and remove
345 | any references to the name of the license steward (except to note that
346 | such modified license differs from this License).
347 |
348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary
349 | Licenses
350 |
351 | If You choose to distribute Source Code Form that is Incompatible With
352 | Secondary Licenses under the terms of this version of the License, the
353 | notice described in Exhibit B of this License must be attached.
354 |
355 | Exhibit A - Source Code Form License Notice
356 | -------------------------------------------
357 |
358 | This Source Code Form is subject to the terms of the Mozilla Public
359 | License, v. 2.0. If a copy of the MPL was not distributed with this
360 | file, You can obtain one at http://mozilla.org/MPL/2.0/.
361 |
362 | If it is not possible or desirable to put the notice in a particular
363 | file, then You may include the notice in a location (such as a LICENSE
364 | file in a relevant directory) where a recipient would be likely to look
365 | for such a notice.
366 |
367 | You may add additional accurate notices of copyright ownership.
368 |
369 | Exhibit B - "Incompatible With Secondary Licenses" Notice
370 | ---------------------------------------------------------
371 |
372 | This Source Code Form is "Incompatible With Secondary Licenses", as
373 | defined by the Mozilla Public License, v. 2.0.
--------------------------------------------------------------------------------
/zoteroshortdoi.js:
--------------------------------------------------------------------------------
1 | // Startup -- load Zotero and constants
2 | if (typeof Zotero === 'undefined') {
3 | Zotero = {};
4 | }
5 |
6 | //const zotDOI_is7 = Zotero.platformMajorVersion >= 102;
7 |
8 | function _create(doc, name) {
9 | const elt =
10 | Zotero.platformMajorVersion >= 102
11 | ? doc.createXULElement(name)
12 | : doc.createElementNS(
13 | "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
14 | name
15 | );
16 | return elt;
17 | }
18 |
19 | ShortDOI = {
20 | id: null,
21 | version: null,
22 | rootURI: null,
23 | addedElementIDs: [],
24 | notifierID: null,
25 |
26 | log(msg) {
27 | Zotero.debug("DOI Manager: " + msg);
28 | },
29 |
30 | // Preference managers
31 |
32 | getPref(pref) {
33 | return Zotero.Prefs.get("extensions.shortdoi." + pref, true);
34 | },
35 |
36 | setPref(pref, value) {
37 | return Zotero.Prefs.set("extensions.shortdoi." + pref, value, true);
38 | },
39 |
40 | // Startup - initialize plugin
41 |
42 | init({ id, version, rootURI } = {}) {
43 | ShortDOI.resetState("initial");
44 |
45 | this.id = id;
46 | this.version = version;
47 | this.rootURI = rootURI;
48 |
49 | // Register the callback in Zotero as an item observer
50 | notifierID = Zotero.Notifier.registerObserver(
51 | ShortDOI.notifierCallback,
52 | ["item"]
53 | );
54 | },
55 |
56 | // Overlay management
57 |
58 | addToWindow(window) {
59 | log("Updating window")
60 |
61 | let doc = window.document;
62 |
63 | // createElementNS() necessary in Zotero 6; createElement() defaults to HTML in Zotero 7
64 | //let HTML_NS = "http://www.w3.org/1999/xhtml";
65 | //let XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
66 |
67 | let stringBundle = Services.strings.createBundle(
68 | "chrome://zoteroshortdoi/locale/zoteroshortdoi.properties"
69 | );
70 |
71 | // Item menu
72 | let itemmenu = _create(doc, "menu");
73 | itemmenu.id = "zotero-itemmenu-shortdoi-menu";
74 | itemmenu.setAttribute("class", "menu-iconic");
75 | //itemmenu.setAttribute('image', 'xxx');
76 | itemmenu.setAttribute(
77 | "label",
78 | stringBundle.GetStringFromName("shortdoi-menu-label")
79 | );
80 |
81 | let itemmenupopup = _create(doc, "menupopup");
82 | itemmenupopup.id = "zotero-itemmenu-shortdoi-menupopup";
83 |
84 | let updateShort = _create(doc, "menuitem");
85 | updateShort.id = "zotero-itemmenu-shortdoi-short";
86 | updateShort.setAttribute(
87 | "label",
88 | stringBundle.GetStringFromName("shortdoi-menu-short-label")
89 | );
90 | updateShort.addEventListener("command", () => {
91 | ShortDOI.updateSelectedItems("short");
92 | });
93 |
94 | let updateLong = _create(doc, "menuitem");
95 | updateLong.id = "zotero-itemmenu-shortdoi-long";
96 | updateLong.setAttribute(
97 | "label",
98 | stringBundle.GetStringFromName("shortdoi-menu-long-label")
99 | );
100 | updateLong.addEventListener("command", () => {
101 | ShortDOI.updateSelectedItems("long");
102 | });
103 |
104 | let updateCheck = _create(doc, "menuitem");
105 | updateCheck.id = "zotero-itemmenu-shortdoi-check";
106 | updateCheck.setAttribute(
107 | "label",
108 | stringBundle.GetStringFromName("shortdoi-menu-check-label")
109 | );
110 | updateCheck.addEventListener("command", () => {
111 | ShortDOI.updateSelectedItems("check");
112 | });
113 |
114 | itemmenupopup.appendChild(updateShort);
115 | itemmenupopup.appendChild(updateLong);
116 | itemmenupopup.appendChild(updateCheck);
117 | itemmenu.appendChild(itemmenupopup);
118 | doc.getElementById("zotero-itemmenu").appendChild(itemmenu);
119 | this.storeAddedElement(itemmenu);
120 |
121 | // Tools menu
122 | // Preferences
123 | // As they are now in the main Zotero preferences in Zotero 7, this is only for Zotero 6
124 | if (!(Zotero.platformMajorVersion >= 102)) {
125 | let menuitem = _create(doc, "menuitem");
126 | menuitem.id = "menu_Tools-shortdoi-preferences";
127 | menuitem.setAttribute(
128 | "label",
129 | stringBundle.GetStringFromName("shortdoi-preferences-label")
130 | );
131 | menuitem.addEventListener("command", () => {
132 | ShortDOI.openPreferenceWindow();
133 | });
134 | doc.getElementById("menu_ToolsPopup").appendChild(menuitem);
135 | this.storeAddedElement(menuitem);
136 | }
137 |
138 | // Auto-retrieve settings
139 | let submenu = _create(doc, "menu");
140 | submenu.id = "menu_Tools-shortdoi-menu";
141 | submenu.setAttribute(
142 | "label",
143 | stringBundle.GetStringFromName("shortdoi-autoretrieve-label")
144 | );
145 | let submenupopup = _create(doc, "menupopup");
146 | submenupopup.id = "menu_Tools-shortdoi-menu-popup";
147 | submenupopup.addEventListener("popupshowing", () => {
148 | ShortDOI.setCheck();
149 | });
150 |
151 | let itemShort = _create(doc, "menuitem");
152 | itemShort.id = "menu_Tools-shortdoi-menu-popup-short";
153 | itemShort.setAttribute("type", "checkbox");
154 | itemShort.setAttribute(
155 | "label",
156 | stringBundle.GetStringFromName("shortdoi-autoretrieve-short-label")
157 | );
158 | itemShort.addEventListener("command", () => {
159 | ShortDOI.changePref("short");
160 | });
161 |
162 | let itemLong = _create(doc, "menuitem");
163 | itemLong.id = "menu_Tools-shortdoi-menu-popup-long";
164 | itemLong.setAttribute("type", "checkbox");
165 | itemLong.setAttribute(
166 | "label",
167 | stringBundle.GetStringFromName("shortdoi-autoretrieve-long-label")
168 | );
169 | itemLong.addEventListener("command", () => {
170 | ShortDOI.changePref("long");
171 | });
172 |
173 | let itemCheck = _create(doc, "menuitem");
174 | itemCheck.id = "menu_Tools-shortdoi-menu-popup-check";
175 | itemCheck.setAttribute("type", "checkbox");
176 | itemCheck.setAttribute(
177 | "label",
178 | stringBundle.GetStringFromName("shortdoi-autoretrieve-check-label")
179 | );
180 | itemCheck.addEventListener("command", () => {
181 | ShortDOI.changePref("check");
182 | });
183 |
184 | let itemNone = _create(doc, "menuitem");
185 | itemNone.id = "menu_Tools-shortdoi-menu-popup-none";
186 | itemNone.setAttribute("type", "checkbox");
187 | itemNone.setAttribute(
188 | "label",
189 | stringBundle.GetStringFromName("shortdoi-autoretrieve-no-label")
190 | );
191 | itemNone.addEventListener("command", () => {
192 | ShortDOI.changePref("none");
193 | });
194 |
195 | submenupopup.appendChild(itemShort);
196 | submenupopup.appendChild(itemLong);
197 | submenupopup.appendChild(itemCheck);
198 | submenupopup.appendChild(itemNone);
199 | submenu.appendChild(submenupopup);
200 | doc.getElementById("menu_ToolsPopup").appendChild(submenu);
201 | this.storeAddedElement(submenu);
202 |
203 | // Use strings from make-it-red.ftl (Fluent) in Zotero 7
204 | /*if (is7) {
205 | window.MozXULElement.insertFTLIfNeeded("zoteroshortdoi.ftl");
206 | }
207 | // Use strings from make-it-red.properties (legacy properties format) in Zotero 6
208 | else {
209 | let stringBundle = Services.strings.createBundle(
210 | 'chrome://zotero-shortdoi/locale/zoteroshortdoi.properties'
211 | );
212 | doc.getElementById('menu_Tools-shortdoi-preferences')
213 | .setAttribute('label', stringBundle.GetStringFromName('makeItGreenInstead.label'));
214 | }*/
215 | },
216 |
217 | addToAllWindows() {
218 | var windows = Zotero.getMainWindows();
219 | for (let win of windows) {
220 | if (!win.ZoteroPane) continue;
221 | this.addToWindow(win);
222 | }
223 | },
224 |
225 | storeAddedElement(elem) {
226 | if (!elem.id) {
227 | throw new Error("Element must have an id");
228 | }
229 | this.addedElementIDs.push(elem.id);
230 | },
231 |
232 | removeFromWindow(window) {
233 | var doc = window.document;
234 | // Remove all elements added to DOM
235 | for (let id of this.addedElementIDs) {
236 | // ?. (null coalescing operator) not available in Zotero 6
237 | let elem = doc.getElementById(id);
238 | if (elem) elem.remove();
239 | }
240 | //doc.querySelector('[href="zoteroshortdoi.ftl"]').remove();
241 | },
242 |
243 | removeFromAllWindows() {
244 | var windows = Zotero.getMainWindows();
245 | for (let win of windows) {
246 | if (!win.ZoteroPane) continue;
247 | this.removeFromWindow(win);
248 | }
249 | },
250 |
251 | // Notifications
252 |
253 | notifierCallback: {
254 | notify: function (event, type, ids, extraData) {
255 | if (event == "add") {
256 | switch (ShortDOI.getPref("autoretrieve")) {
257 | case "short":
258 | ShortDOI.updateItems(Zotero.Items.get(ids), "short");
259 | break;
260 | case "long":
261 | ShortDOI.updateItems(Zotero.Items.get(ids), "long");
262 | break;
263 | case "check":
264 | ShortDOI.updateItems(Zotero.Items.get(ids), "check");
265 | break;
266 | default:
267 | break;
268 | }
269 | }
270 | },
271 | },
272 |
273 | // Controls for Tools menu
274 |
275 | // *********** Set the checkbox checks, frompref
276 | setCheck() {
277 | let document = Zotero.getMainWindow().document;
278 |
279 | var tools_short = document.getElementById(
280 | "menu_Tools-shortdoi-menu-popup-short"
281 | );
282 | var tools_long = document.getElementById(
283 | "menu_Tools-shortdoi-menu-popup-long"
284 | );
285 | var tools_check = document.getElementById(
286 | "menu_Tools-shortdoi-menu-popup-check"
287 | );
288 | var tools_none = document.getElementById(
289 | "menu_Tools-shortdoi-menu-popup-none"
290 | );
291 | var pref = ShortDOI.getPref("autoretrieve");
292 | tools_short.setAttribute("checked", Boolean(pref === "short"));
293 | tools_long.setAttribute("checked", Boolean(pref === "long"));
294 | tools_check.setAttribute("checked", Boolean(pref === "check"));
295 | tools_none.setAttribute("checked", Boolean(pref === "none"));
296 | },
297 |
298 | // *********** Change the checkbox, topref
299 | changePref(option) {
300 | ShortDOI.setPref("autoretrieve", option);
301 | },
302 |
303 | /**
304 | * Open shortdoi preference window
305 | */
306 | openPreferenceWindow(paneID, action) {
307 | log("Opening pref window for DOI");
308 |
309 | let window = Zotero.getMainWindow();
310 | var io = { pane: paneID, action: action };
311 | window.openDialog(
312 | "chrome://zoteroshortdoi/content/options.xul",
313 | "shortdoi-pref",
314 | "chrome,titlebar,toolbar,centerscreen" +
315 | Zotero.Prefs.get("browser.preferences.instantApply", true)
316 | ? "dialog=no"
317 | : "modal",
318 | io
319 | );
320 | },
321 |
322 | resetState(operation) {
323 | if (operation == "initial") {
324 | if (ShortDOI.progressWindow) {
325 | ShortDOI.progressWindow.close();
326 | }
327 | ShortDOI.current = -1;
328 | ShortDOI.toUpdate = 0;
329 | ShortDOI.itemsToUpdate = null;
330 | ShortDOI.numberOfUpdatedItems = 0;
331 | ShortDOI.counter = 0;
332 | error_invalid = null;
333 | error_nodoi = null;
334 | error_multiple = null;
335 | error_invalid_shown = false;
336 | error_nodoi_shown = false;
337 | error_multiple_shown = false;
338 | final_count_shown = false;
339 | return;
340 | } else {
341 | if (error_invalid || error_nodoi || error_multiple) {
342 | ShortDOI.progressWindow.close();
343 | var icon = "chrome://zotero/skin/cross.png";
344 | if (error_invalid && !error_invalid_shown) {
345 | var progressWindowInvalid = new Zotero.ProgressWindow({
346 | closeOnClick: true,
347 | });
348 | progressWindowInvalid.changeHeadline("Invalid DOI");
349 | if (ShortDOI.getPref("tag_invalid") !== "") {
350 | progressWindowInvalid.progress = new progressWindowInvalid.ItemProgress(
351 | icon,
352 | "Invalid DOIs were found. These have been tagged with '" +
353 | ShortDOI.getPref("tag_invalid") +
354 | "'."
355 | );
356 | } else {
357 | progressWindowInvalid.progress = new progressWindowInvalid.ItemProgress(
358 | icon,
359 | "Invalid DOIs were found."
360 | );
361 | }
362 | progressWindowInvalid.progress.setError();
363 | progressWindowInvalid.show();
364 | progressWindowInvalid.startCloseTimer(8000);
365 | error_invalid_shown = true;
366 | }
367 | if (error_nodoi && !error_nodoi_shown) {
368 | var progressWindowNoDOI = new Zotero.ProgressWindow({
369 | closeOnClick: true,
370 | });
371 | progressWindowNoDOI.changeHeadline("DOI not found");
372 | if (ShortDOI.getPref("tag_nodoi") !== "") {
373 | progressWindowNoDOI.progress = new progressWindowNoDOI.ItemProgress(
374 | icon,
375 | "No DOI was found for some items. These have been tagged with '" +
376 | ShortDOI.getPref("tag_nodoi") +
377 | "'."
378 | );
379 | } else {
380 | progressWindowNoDOI.progress = new progressWindowNoDOI.ItemProgress(
381 | icon,
382 | "No DOI was found for some items."
383 | );
384 | }
385 | progressWindowNoDOI.progress.setError();
386 | progressWindowNoDOI.show();
387 | progressWindowNoDOI.startCloseTimer(8000);
388 | error_nodoi_shown = true;
389 | }
390 | if (error_multiple && !error_multiple_shown) {
391 | var progressWindowMulti = new Zotero.ProgressWindow({
392 | closeOnClick: true,
393 | });
394 | progressWindowMulti.changeHeadline(
395 | "Multiple possible DOIs"
396 | );
397 | if (ShortDOI.getPref("tag_multiple") !== "") {
398 | progressWindowMulti.progress = new progressWindowMulti.ItemProgress(
399 | icon,
400 | "Some items had multiple possible DOIs. Links to lists of DOIs have been added and tagged with '" +
401 | ShortDOI.getPref("tag_multiple") +
402 | "'."
403 | );
404 | } else {
405 | progressWindowMulti.progress = new progressWindowMulti.ItemProgress(
406 | icon,
407 | "Some items had multiple possible DOIs."
408 | );
409 | }
410 | progressWindow.progress.setError();
411 | progressWindowMulti.show();
412 | progressWindowMulti.startCloseTimer(8000);
413 | error_multiple_shown = true;
414 | }
415 | } else {
416 | if (!final_count_shown) {
417 | var icon = "chrome://zotero/skin/tick.png";
418 | ShortDOI.progressWindow = new Zotero.ProgressWindow({
419 | closeOnClick: true,
420 | });
421 | ShortDOI.progressWindow.changeHeadline("Finished");
422 | ShortDOI.progressWindow.progress = new ShortDOI.progressWindow.ItemProgress(
423 | icon
424 | );
425 | ShortDOI.progressWindow.progress.setProgress(100);
426 | if (operation == "short") {
427 | ShortDOI.progressWindow.progress.setText(
428 | "shortDOIs updated for " +
429 | ShortDOI.counter +
430 | " items."
431 | );
432 | } else if (operation == "long") {
433 | ShortDOI.progressWindow.progress.setText(
434 | "Long DOIs updated for " +
435 | ShortDOI.counter +
436 | " items."
437 | );
438 | } else {
439 | ShortDOI.progressWindow.progress.setText(
440 | "DOIs verified for " + ShortDOI.counter + " items."
441 | );
442 | }
443 | ShortDOI.progressWindow.show();
444 | ShortDOI.progressWindow.startCloseTimer(4000);
445 | final_count_shown = true;
446 | }
447 | }
448 | return;
449 | }
450 | },
451 | };
452 |
453 | ShortDOI.generateItemUrl = function (item, operation) {
454 | var doi = item.getField("DOI");
455 | if (!doi) {
456 | doi = ShortDOI.crossrefLookup(item, operation);
457 | } else {
458 | if (typeof doi === "string") {
459 | doi = Zotero.Utilities.cleanDOI(doi);
460 | if (doi) {
461 | if (operation === "short" && !doi.match(/10\/[^\s]*[^\s\.,]/)) {
462 | var url =
463 | "https://shortdoi.org/" +
464 | encodeURIComponent(doi) +
465 | "?format=json";
466 | return url;
467 | } else {
468 | var url =
469 | "https://doi.org/api/handles/" +
470 | encodeURIComponent(doi);
471 | return url;
472 | }
473 | } else {
474 | return "invalid";
475 | }
476 | } else {
477 | return "invalid";
478 | }
479 | }
480 |
481 | return false;
482 | };
483 |
484 | ShortDOI.updateSelectedItems = function (operation) {
485 | ShortDOI.updateItems(
486 | Zotero.getActiveZoteroPane().getSelectedItems(),
487 | operation
488 | );
489 | };
490 |
491 | ShortDOI.updateItems = function (items, operation) {
492 | // For now, filter out non-journal article and conference paper items
493 | var items = items.filter(
494 | (item) =>
495 | item.itemTypeID == Zotero.ItemTypes.getID("journalArticle") ||
496 | item.itemTypeID == Zotero.ItemTypes.getID("conferencePaper")
497 | );
498 | var items = items.filter((item) => !item.isFeedItem);
499 |
500 | if (
501 | items.length === 0 ||
502 | ShortDOI.numberOfUpdatedItems < ShortDOI.toUpdate
503 | ) {
504 | return;
505 | }
506 |
507 | ShortDOI.resetState("initial");
508 | ShortDOI.toUpdate = items.length;
509 | ShortDOI.itemsToUpdate = items;
510 |
511 | // Progress Windows
512 | ShortDOI.progressWindow = new Zotero.ProgressWindow({
513 | closeOnClick: false,
514 | });
515 | var icon =
516 | "chrome://zotero/skin/toolbar-advanced-search" +
517 | (Zotero.hiDPI ? "@2x" : "") +
518 | ".png";
519 | if (operation == "short") {
520 | ShortDOI.progressWindow.changeHeadline("Getting shortDOIs", icon);
521 | } else if (operation == "long") {
522 | ShortDOI.progressWindow.changeHeadline("Getting long DOIs", icon);
523 | } else {
524 | ShortDOI.progressWindow.changeHeadline(
525 | "Validating DOIs and removing extra text",
526 | icon
527 | );
528 | }
529 | let iconPath =
530 | Zotero.platformMajorVersion >= 102
531 | ? this.rootURI + "skin/doi"
532 | : "chrome://zoteroshortdoi/skin/doi";
533 | var doiIcon = iconPath + (Zotero.hiDPI ? "@2x" : "") + ".png";
534 | ShortDOI.progressWindow.progress = new ShortDOI.progressWindow.ItemProgress(
535 | doiIcon,
536 | "Checking DOIs."
537 | );
538 |
539 | ShortDOI.updateNextItem(operation);
540 | };
541 |
542 | ShortDOI.updateNextItem = function (operation) {
543 | ShortDOI.numberOfUpdatedItems++;
544 |
545 | if (ShortDOI.current == ShortDOI.toUpdate - 1) {
546 | ShortDOI.progressWindow.close();
547 | ShortDOI.resetState(operation);
548 | return;
549 | }
550 |
551 | ShortDOI.current++;
552 |
553 | // Progress Windows
554 | var percent = Math.round(
555 | (ShortDOI.numberOfUpdatedItems / ShortDOI.toUpdate) * 100
556 | );
557 | ShortDOI.progressWindow.progress.setProgress(percent);
558 | ShortDOI.progressWindow.progress.setText(
559 | "Item " + ShortDOI.current + " of " + ShortDOI.toUpdate
560 | );
561 | ShortDOI.progressWindow.show();
562 |
563 | ShortDOI.updateItem(ShortDOI.itemsToUpdate[ShortDOI.current], operation);
564 | };
565 |
566 | ShortDOI.updateItem = function (item, operation) {
567 | var url = ShortDOI.generateItemUrl(item, operation);
568 |
569 | if (!url) {
570 | if (item.hasTag(ShortDOI.getPref("tag_invalid"))) {
571 | item.removeTag(ShortDOI.getPref("tag_invalid"));
572 | item.saveTx();
573 | }
574 | ShortDOI.updateNextItem(operation);
575 | } else if (url == "invalid") {
576 | ShortDOI.invalidate(item, operation);
577 | } else {
578 | var oldDOI = item.getField("DOI");
579 | var req = new XMLHttpRequest();
580 | req.open("GET", url, true);
581 | req.responseType = "json";
582 |
583 | if (operation == "short") {
584 | req.onreadystatechange = function () {
585 | if (req.readyState == 4) {
586 | if (req.status == 200) {
587 | if (item.isRegularItem()) {
588 | if (oldDOI.match(/10\/[^\s]*[^\s\.,]/)) {
589 | if (req.response.responseCode == 1) {
590 | if (req.response.handle != oldDOI) {
591 | var shortDOI = req.response.handle.toLowerCase();
592 | item.setField("DOI", shortDOI);
593 | item.removeTag(
594 | ShortDOI.getPref("tag_invalid")
595 | );
596 | item.removeTag(
597 | ShortDOI.getPref("tag_multiple")
598 | );
599 | item.removeTag(
600 | ShortDOI.getPref("tag_nodoi")
601 | );
602 | item.saveTx();
603 | ShortDOI.counter++;
604 | } else if (
605 | item.hasTag(
606 | ShortDOI.getPref("tag_invalid")
607 | ) ||
608 | item.hasTag(
609 | ShortDOI.getPref("tag_multiple")
610 | ) ||
611 | item.hasTag(
612 | ShortDOI.getPref("tag_nodoi")
613 | )
614 | ) {
615 | item.removeTag(
616 | ShortDOI.getPref("tag_invalid")
617 | );
618 | item.removeTag(
619 | ShortDOI.getPref("tag_multiple")
620 | );
621 | item.removeTag(
622 | ShortDOI.getPref("tag_nodoi")
623 | );
624 | item.saveTx();
625 | }
626 | } else {
627 | ShortDOI.invalidate(item, operation);
628 | }
629 | } else {
630 | var shortDOI = req.response.ShortDOI.toLowerCase();
631 | item.setField("DOI", shortDOI);
632 | item.removeTag(ShortDOI.getPref("tag_invalid"));
633 | item.removeTag(
634 | ShortDOI.getPref("tag_multiple")
635 | );
636 | item.removeTag(ShortDOI.getPref("tag_nodoi"));
637 | item.saveTx();
638 | ShortDOI.counter++;
639 | }
640 | }
641 | ShortDOI.updateNextItem(operation);
642 | } else if (req.status == 400 || req.status == 404) {
643 | ShortDOI.invalidate(item, operation);
644 | } else {
645 | ShortDOI.updateNextItem(operation);
646 | }
647 | }
648 | };
649 |
650 | req.send(null);
651 | } else if (operation == "long") {
652 | req.onreadystatechange = function () {
653 | if (req.readyState == 4) {
654 | if (req.status == 200) {
655 | if (req.response.responseCode == 1) {
656 | if (oldDOI.match(/10\/[^\s]*[^\s\.,]/)) {
657 | if (item.isRegularItem()) {
658 | var longDOI = req.response.values[
659 | "1"
660 | ].data.value.toLowerCase();
661 | item.setField("DOI", longDOI);
662 | item.removeTag(
663 | ShortDOI.getPref("tag_invalid")
664 | );
665 | item.removeTag(
666 | ShortDOI.getPref("tag_multiple")
667 | );
668 | item.removeTag(
669 | ShortDOI.getPref("tag_nodoi")
670 | );
671 | item.saveTx();
672 | ShortDOI.counter++;
673 | }
674 | } else {
675 | if (req.response.handle != oldDOI) {
676 | var longDOI = req.response.handle.toLowerCase();
677 | item.setField("DOI", longDOI);
678 | item.removeTag(
679 | ShortDOI.getPref("tag_invalid")
680 | );
681 | item.removeTag(
682 | ShortDOI.getPref("tag_multiple")
683 | );
684 | item.removeTag(
685 | ShortDOI.getPref("tag_nodoi")
686 | );
687 | item.saveTx();
688 | ShortDOI.counter++;
689 | } else if (
690 | item.hasTag(
691 | ShortDOI.getPref("tag_invalid")
692 | ) ||
693 | item.hasTag(
694 | ShortDOI.getPref("tag_multiple")
695 | ) ||
696 | item.hasTag(ShortDOI.getPref("tag_nodoi"))
697 | ) {
698 | item.removeTag(
699 | ShortDOI.getPref("tag_invalid")
700 | );
701 | item.removeTag(
702 | ShortDOI.getPref("tag_multiple")
703 | );
704 | item.removeTag(
705 | ShortDOI.getPref("tag_nodoi")
706 | );
707 | item.saveTx();
708 | }
709 | }
710 | ShortDOI.updateNextItem(operation);
711 | } else {
712 | ShortDOI.invalidate(item, operation);
713 | }
714 | } else if (req.status == 404) {
715 | ShortDOI.invalidate(item, operation);
716 | } else {
717 | ShortDOI.updateNextItem(operation);
718 | }
719 | }
720 | };
721 |
722 | req.send(null);
723 | } else {
724 | //operation == "check"
725 |
726 | req.onreadystatechange = function () {
727 | if (req.readyState == 4) {
728 | if (req.status == 404) {
729 | ShortDOI.invalidate(item, operation);
730 | } else if (req.status == 200) {
731 | if (req.response.responseCode == 200) {
732 | ShortDOI.invalidate(item, operation);
733 | } else {
734 | if (req.response.handle != oldDOI) {
735 | var newDOI = req.response.handle.toLowerCase();
736 | item.setField("DOI", newDOI);
737 | item.removeTag(ShortDOI.getPref("tag_invalid"));
738 | item.removeTag(
739 | ShortDOI.getPref("tag_multiple")
740 | );
741 | item.removeTag(ShortDOI.getPref("tag_nodoi"));
742 | item.saveTx();
743 | } else if (
744 | item.hasTag(ShortDOI.getPref("tag_invalid")) ||
745 | item.hasTag(ShortDOI.getPref("tag_multiple")) ||
746 | item.hasTag(ShortDOI.getPref("tag_nodoi"))
747 | ) {
748 | item.removeTag(ShortDOI.getPref("tag_invalid"));
749 | item.removeTag(
750 | ShortDOI.getPref("tag_multiple")
751 | );
752 | item.removeTag(ShortDOI.getPref("tag_nodoi"));
753 | item.saveTx();
754 | }
755 | ShortDOI.counter++;
756 | ShortDOI.updateNextItem(operation);
757 | }
758 | } else {
759 | ShortDOI.updateNextItem(operation);
760 | }
761 | }
762 | };
763 |
764 | req.send(null);
765 | }
766 | }
767 | };
768 |
769 | ShortDOI.invalidate = function (item, operation) {
770 | if (item.isRegularItem()) {
771 | error_invalid = true;
772 | if (ShortDOI.getPref("tag_invalid") !== "")
773 | item.addTag(ShortDOI.getPref("tag_invalid"), 1);
774 | item.saveTx();
775 | }
776 | ShortDOI.updateNextItem(operation);
777 | };
778 |
779 | if (typeof window !== "undefined") {
780 | window.addEventListener(
781 | "load",
782 | function (e) {
783 | ShortDOI.init();
784 | },
785 | false
786 | );
787 | }
788 |
789 | ShortDOI.crossrefLookup = function (item, operation) {
790 | var crossrefOpenURL =
791 | "https://www.crossref.org/openurl?pid=zoteroDOI@wiernik.org&";
792 | var ctx = Zotero.OpenURL.createContextObject(item, "1.0");
793 | if (ctx) {
794 | var url = crossrefOpenURL + ctx + "&multihit=true";
795 | var req = new XMLHttpRequest();
796 | req.open("GET", url, true);
797 |
798 | req.onreadystatechange = function () {
799 | if (req.readyState == 4) {
800 | if (req.status == 200) {
801 | var response = req.responseXML.getElementsByTagName(
802 | "query"
803 | )[0];
804 | var status = response.getAttribute("status");
805 | if (status === "resolved") {
806 | var doi = response.getElementsByTagName("doi")[0]
807 | .childNodes[0].nodeValue;
808 | if (operation === "short") {
809 | item.setField("DOI", doi);
810 | ShortDOI.updateItem(item, operation);
811 | } else {
812 | item.setField("DOI", doi);
813 | item.removeTag(ShortDOI.getPref("tag_invalid"));
814 | item.removeTag(ShortDOI.getPref("tag_multiple"));
815 | item.removeTag(ShortDOI.getPref("tag_nodoi"));
816 | item.saveTx();
817 | ShortDOI.counter++;
818 | ShortDOI.updateNextItem(operation);
819 | }
820 | } else if (status === "unresolved") {
821 | error_nodoi = true;
822 | item.removeTag(ShortDOI.getPref("tag_invalid"));
823 | item.removeTag(ShortDOI.getPref("tag_multiple"));
824 | item.removeTag(ShortDOI.getPref("tag_nodoi"));
825 | if (ShortDOI.getPref("tag_nodoi") !== "")
826 | item.addTag(ShortDOI.getPref("tag_nodoi"), 1);
827 | item.saveTx();
828 | ShortDOI.updateNextItem(operation);
829 | } else if (status === "multiresolved") {
830 | error_multiple = true;
831 | Zotero.Attachments.linkFromURL({
832 | url: crossrefOpenURL + ctx,
833 | parentItemID: item.id,
834 | contentType: "text/html",
835 | title: "Multiple DOIs found",
836 | });
837 | if (
838 | item.hasTag(ShortDOI.getPref("tag_invalid")) ||
839 | item.hasTag(ShortDOI.getPref("tag_nodoi"))
840 | ) {
841 | item.removeTag(ShortDOI.getPref("tag_invalid"));
842 | item.removeTag(ShortDOI.getPref("tag_nodoi"));
843 | }
844 | // TODO: Move this tag to the attachment link
845 | if (ShortDOI.getPref("tag_multiple") !== "")
846 | item.addTag(ShortDOI.getPref("tag_multiple"), 1);
847 | item.saveTx();
848 | ShortDOI.updateNextItem(operation);
849 | } else {
850 | Zotero.debug(
851 | "Zotero DOI Manager: CrossRef lookup: Unknown status code: " +
852 | status
853 | );
854 | ShortDOI.updateNextItem(operation);
855 | }
856 | } else {
857 | ShortDOI.updateNextItem(operation);
858 | }
859 | }
860 | };
861 |
862 | req.send(null);
863 | }
864 |
865 | return false;
866 | };
867 |
--------------------------------------------------------------------------------