Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision | ||
dev:client_coding:javascript_api [2018/09/16 13:23] – [Managing citations and bibliographies] fix and expand related items zuphilip | dev:client_coding:javascript_api [2019/08/05 05:54] – [Running Ad Hoc JavaScript in Zotero] dstillman | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | < | ||
- | in the process of updating the documentation for | ||
- | <a href=" | ||
- | may be outdated in the meantime. Thanks for your understanding.</ | ||
- | |||
- | |||
====== Zotero JavaScript API ====== | ====== Zotero JavaScript API ====== | ||
- | Whereas Zotero' | + | Whereas Zotero' |
Note that the (mostly user-contributed) documentation of the JavaScript API is not comprehensive. If you use the JavaScript API in ways beyond what's described here, please consider expanding this wiki page. | Note that the (mostly user-contributed) documentation of the JavaScript API is not comprehensive. If you use the JavaScript API in ways beyond what's described here, please consider expanding this wiki page. | ||
- | ===== Helpful Resources ===== | ||
- | For this section, you’ll likely find the following helpful: | + | ===== Running Ad Hoc JavaScript in Zotero ===== |
- | * A basic understanding of how to create a Firefox extension | + | Zotero includes an option |
- | * [[http:// | + | |
- | * [[https:// | + | |
- | * A decent grasp of object-oriented JavaScript | + | |
- | * [[http:// | + | |
- | * [[http:// | + | |
- | ===== An Easy Debugging Environment ===== | + | - In the Tools → Developer menu, select Run JavaScript. Opening the Error Console, which appears in the same menu, will also be helpful. |
+ | - In the window that opens, enter JavaScript in the Code textbox and click Run or press Cmd-R/ | ||
- | Various Firefox extensions create servers within Firefox that expose | + | When running asynchronous code containing '' |
- | * [[https:// | + | In synchronous mode, the value of the final line will appear |
- | * [[http:// | + | ===== Zotero Code Architecture ===== |
- | * [[http:// | + | |
- | * **Potential MozRepl Problems**: Longer scripts can time out non-deterministically. A solution to this is to either send the code to the terminal application | + | |
- | Perl programmers should be aware of the [[http:// | + | ==== Window Scope vs. Non-Window Scope === |
+ | Zotero code exists in either window scope and non-window scope. | ||
- | ===== The Zotero | + | Window scope applies to code that runs within either the main Zotero window or a secondary window (e.g., the Advanced Search window). It has access to the window' |
- | Zotero exposes an object-oriented JavaScript API that can be used to access and modify Zotero data. Within | + | Non-window scope applies to lower-level code that doesn' |
- | ==== XPCOM ==== | + | Overlays and windows in Zotero can import the core '' |
- | A common problem in developing Mozilla extensions is figuring out how to pass data between different windows, which have their own scopes and do not by default share the same variables and code. While there are various mechanisms to get around this, the [[http:// | + | To access Zotero functionality from your own extension, you will need access |
- | The base Zotero service is an XPCOM component written in JavaScript. While normally XPCOM components implement various predefined interfaces themselves, in Zotero the data layer and most of the core functionality are stored within a JavaScript object that is then stuffed into the special '' | + | <code html> |
+ | <script | ||
+ | </ | ||
- | Access to the Zotero service is not limited to Zotero itself, however. As a standard XPCOM component, the Zotero service---and, | + | Once you have '' |
- | + | ||
- | To access the data API in your own extension, you will need access to the core Zotero JavaScript object. If your extension operates within the main browser overlay, | + | |
- | + | ||
- | %%chrome:// | + | |
<code javascript> | <code javascript> | ||
- | var Zotero | + | var zp = Zotero.getActiveZoteroPane(); |
- | .getService(Components.interfaces.nsISupports) | + | var items = zp.getSelectedItems(); |
- | .wrappedJSObject; | + | </ |
- | </ | + | (The Zotero pane will always be available unless the main window is closed, as is possible on macOS.) |
- | ===== Notification System | + | ==== Notification System ==== |
- | Zotero has a built-in notification system that allows other privileged code to be notified when a change is made via the data layer---for example, when an item is added to the library. Within Zotero itself, this is used mostly to update the UI when items change, but external | + | Zotero has a built-in notification system that allows other privileged code to be notified when a change is made via the data layer — for example, when an item is added to the library. Within Zotero itself, this is used mostly to update the UI when items change, but extensions can use the system to perform additional operations when specific events occur — say, to sync item metadata with a web-based |
Available events: | Available events: | ||
Line 74: | Line 60: | ||
//The Zotero JavaScript API is under-documented, | //The Zotero JavaScript API is under-documented, | ||
- | |||
- | Once you have access to the core Zotero object, you can use the objects and methods provided by the Zotero JavaScript API. | ||
- | |||
- | Zotero uses a combination of instantiatable objects (e.g. '' | ||
==== Adding items and modifying data ==== | ==== Adding items and modifying data ==== | ||
- | A typical operation might include a call to '' | + | A typical operation might include a call to '' |
<code javascript> | <code javascript> | ||
- | var creator = new Zotero.Creator; | + | var item = new Zotero.Item(' |
- | creator.firstName = ' | + | |
- | creator.lastName = ' | + | |
- | creator.save(); | + | |
- | + | ||
- | var item = new Zotero.Item; | + | |
- | item.setType(Zotero.ItemTypes.getID(' | + | |
item.setField(' | item.setField(' | ||
- | item.setCreator(0, creator, 'author'); | + | item.setCreators( |
- | var itemID = item.save(); | + | [ |
+ | { | ||
+ | firstName: " | ||
+ | lastName: " | ||
+ | creatorType: | ||
+ | } | ||
+ | ] | ||
+ | ); | ||
+ | var itemID = await item.save(); | ||
+ | return itemID; | ||
</ | </ | ||
Line 105: | Line 90: | ||
alert(item.getCreator(0)); | alert(item.getCreator(0)); | ||
// | // | ||
+ | // Alternative format | ||
+ | alert(item.getCreatorJSON(0)); | ||
+ | // | ||
+ | |||
item.setField(' | item.setField(' | ||
item.setField(' | item.setField(' | ||
- | item.save(); // update database with new data | + | await item.saveTx(); // update database with new data |
</ | </ | ||
- | ==== Create new Zotero | + | ==== Get the Zotero Pane to interact with the Zotero |
- | + | ||
- | This is the first thing that you need to do when interacting with Zotero' | + | |
- | internals. | + | |
<code javascript> | <code javascript> | ||
- | var Zotero | + | var ZoteroPane |
</ | </ | ||
- | ==== Get the Zotero Pane to interact with the Zotero | + | Then grab the currently selected items from the Zotero |
<code javascript> | <code javascript> | ||
- | var ZoteroPane = Components.classes[" | + | // Get first selected item |
- | </ | + | var selectedItems |
- | + | var item = selectedItems[0]; | |
- | Then grab the currently selected items from the zotero pane: | + | |
- | + | ||
- | <code javascript> | + | |
- | //get first selected item | + | |
- | var selected_items | + | |
- | var item = selected_items[0]; | + | |
- | // proced | + | // Proceed |
- | if ( ! item.isCollection() | + | if (item && !item.isNote()) { |
if (item.isAttachment()) { | if (item.isAttachment()) { | ||
// find out about attachment | // find out about attachment | ||
} | } | ||
if (item.isRegularItem()) { | if (item.isRegularItem()) { | ||
- | // we could grab attachments: | + | // We could grab attachments: |
- | // var att_ids | + | // let attachmentIDs |
- | // if (att_ids.length> | + | // let attachment |
- | // item_att=Zotero.Items.get(att_ids[0]); | + | |
} | } | ||
alert(item.id); | alert(item.id); | ||
Line 148: | Line 127: | ||
</ | </ | ||
- | ==== Setup a Zotero search | + | ==== Collection Operations |
- | If you are focused on data access, then the first thing you will want to do | + | === Get the items in the selected collection === |
- | will be to retrieve | + | |
- | good start. | + | |
- | <code javascript> | + | <code javascript> |
+ | var collection | ||
+ | var items = collection.getChildItems(); | ||
+ | // or you can obtain an array of itemIDs instead: | ||
+ | var itemIDs = collection.getChildItems(true); | ||
+ | </ | ||
- | ==== Search for items containing | + | === Create |
- | Starting with the code from "Setup a Zotero | + | <code javascript> |
- | code to retrieve items with a particular tag: | + | var currentCollection = ZoteroPane.getSelectedCollection(); |
+ | var collection = new Zotero.Collection(); | ||
+ | collection.name = name; | ||
+ | collection.parentID = currentCollection.id; | ||
+ | var collectionID = await collection.saveTx(); | ||
+ | return collectionID; | ||
+ | </code> | ||
- | <code javascript> | + | ==== Zotero Search Basics ==== |
- | ==== Zotero Collection Operations ==== | + | If you are focused on data access, |
- | + | will be to retrieve items from your Zotero library. Creating an in-memory search is a | |
- | === Get the collection tree and display as a series of nested ordered lists === | + | good start. |
- | + | ||
- | This code was developed | + | |
<code javascript> | <code javascript> | ||
- | var Zotero | + | var s = new Zotero.Search(); |
- | + | s.libraryID | |
- | var render_collection = function(coll) { | + | |
- | if (!coll) { | + | |
- | coll = null; | + | |
- | } | + | |
- | var collections | + | |
- | document.writeln("< | + | |
- | for (c in collections) { | + | |
- | document.writeln('< | + | |
- | if (collections[c].hasChildCollections) { | + | |
- | var childCol = render_collection(collections[c].id); | + | |
- | } | + | |
- | } | + | |
- | document.writeln("</ | + | |
- | } | + | |
- | + | ||
- | render_collection(); | + | |
</ | </ | ||
- | === Get the items for a particular collection | + | === Search |
- | <code javascript> | + | Starting with the above code, we then use the following |
- | var Zotero = Components.classes[" | + | |
- | var collectionid = pow_server.GET.id; | + | |
- | var collection = z.Collections.get(collectionid); | + | |
- | var items = collection.getChildItems(); | + | |
- | // or you can obtain an array of itemIDs instead: | + | |
- | var itemids = collection.getChildItems(true); | + | |
- | </code> | + | |
- | + | ||
- | === Create a New Collection | + | |
<code javascript> | <code javascript> | ||
- | async function createCollection(name) { | + | s.addCondition(' |
- | var currentCollection = ZoteroPane.getSelectedCollection(); | + | var itemIDs |
- | var collection = new Zotero.Collection(); | + | |
- | collection.name = name; | + | |
- | collection.parentID = currentCollection.id; | + | |
- | var collectionID | + | |
- | return collectionID; | + | |
- | } | + | |
</ | </ | ||
- | This function creates a new collection with a given name and makes | + | === Advanced searches |
- | it a subcollection of the currently selected one. Since the saving | + | |
- | will result in a Promise object, we add the '' | + | |
- | function call and make sure that this is within an '' | + | |
- | + | ||
- | ==== Zotero Search Basics ==== | + | |
- | + | ||
- | === Set up the search | + | |
<code javascript> | <code javascript> | ||
var s = new Zotero.Search(); | var s = new Zotero.Search(); | ||
+ | s.libraryID = Zotero.Libraries.userLibraryID; | ||
s.addCondition(' | s.addCondition(' | ||
- | // advanced search | + | // advanced search |
</ | </ | ||
- | To add the other conditions available in the advanced search | + | To add the other conditions available in the advanced search |
<code javascript> | <code javascript> | ||
Line 247: | Line 196: | ||
=== Search by collection === | === Search by collection === | ||
- | To search for a collection or a saved search you need to know the ID: | + | To search for a collection or a saved search you need to know the ID or key: |
<code javascript> | <code javascript> | ||
- | s.addCondition(' | + | s.addCondition(' |
s.addCondition(' | s.addCondition(' | ||
+ | </ | ||
+ | |||
+ | <code javascript> | ||
+ | s.addCondition(' | ||
+ | s.addCondition(' | ||
</ | </ | ||
Line 259: | Line 213: | ||
<code javascript> | <code javascript> | ||
- | | + | var tagName |
- | | + | s.addCondition(' |
</ | </ | ||
Line 272: | Line 226: | ||
results: | results: | ||
- | <code javascript> | + | <code javascript> |
- | This returns the item ids in the search as an array [I could be wrong ... ]. | + | This returns the item ids in the search as an array. The next thing to do is to get the Zotero items for the array of IDs: |
- | The next thing to do is to get the Zotero items for the array of IDs: | + | |
- | <code javascript> | + | <code javascript> |
===== Managing citations and bibliographies ===== | ===== Managing citations and bibliographies ===== | ||
Line 290: | Line 243: | ||
First we start with a list of as in the previous entry. | First we start with a list of as in the previous entry. | ||
- | <code javascript> | + | <code javascript> |
- | var biblio = qc.getContentFromItems(new Array(item), | + | var biblio = qc.getContentFromItems([item], Zotero.Prefs.get(" |
- | z.Prefs.get(" | + | var biblio_html_format = cite.html; |
- | var biblio_html_format = cite.html; | + | var biblio_txt |
- | var biblio_txt | + | |
</ | </ | ||
Line 300: | Line 252: | ||
<code javascript> | <code javascript> | ||
- | | + | await Zotero.Schema.schemeUpdatePromise; |
- | var style_info | + | var styles = Zotero.Styles.getVisible(); |
- | for each ( var s in styles) { | + | var styleInfo |
- | style_info.push( { "id" | + | for (let style of styles) { |
- | | + | |
- | | + | } |
+ | return styleInfo; | ||
</ | </ | ||
Line 318: | Line 271: | ||
To get an item's abstract, we get the ' | To get an item's abstract, we get the ' | ||
- | <code javascript> | + | <code javascript> |
==== Get child notes for an item ==== | ==== Get child notes for an item ==== | ||
Line 324: | Line 277: | ||
To get the child notes for an item, we use the following code: | To get the child notes for an item, we use the following code: | ||
- | <code javascript> | + | <code javascript> |
- | This returns an array of notes. | + | This returns an array of ids of note items. To get each note in turn we just iterate through the array: |
- | in turn we just iterate through the array: | + | |
<code javascript> | <code javascript> | ||
- | | + | for (let id of noteIDs) { |
- | | + | |
- | var note_html | + | let noteHTML |
- | } | + | } |
</ | </ | ||
==== Get an item's related items ==== | ==== Get an item's related items ==== | ||
- | This technique works for anything that can have related items attached within | + | <code javascript> |
- | the Zotero database. | + | |
- | + | ||
- | <code javascript> | + | |
==== Set two items as related to each other ==== | ==== Set two items as related to each other ==== | ||
Line 354: | Line 303: | ||
- | ==== Get an Item' | + | ==== Get an item' |
- | Here's some example code to get the full text of HTML and PDF items in storage and puts the data in an array: | + | Here's some example code to get the full text of HTML and PDF items in storage and put the data in an array: |
<code javascript> | <code javascript> | ||
- | var item = 'some item' | + | var item = ZoteroPane.getSelectedItems()[0]; |
- | var fulltext = new Array; | + | var fulltext = []; |
if (item.isRegularItem()) { // not an attachment already | if (item.isRegularItem()) { // not an attachment already | ||
- | | + | |
- | for (a in attachments) { | + | for (let id of attachmentIDs) { |
- | | + | |
- | if (a_item.attachmentMIMEType | + | if (attachment.attachmentContentType |
- | || a_item.attachmentMIMEType | + | || attachment.attachmentContentType |
- | fulltext.push(a_item.attachmentText); | + | fulltext.push(await attachment.attachmentText); |
} | } | ||
} | } | ||
} | } | ||
+ | return fulltext; | ||
</ | </ | ||
Line 387: | Line 337: | ||
The JavaScript API can provide a powerful way to script changes to your Zotero library. The common case of search-and-replace is accomplished easily using a basic script. | The JavaScript API can provide a powerful way to script changes to your Zotero library. The common case of search-and-replace is accomplished easily using a basic script. | ||
- | First, install the [[https:// | + | Before proceeding, back up your [[:zotero_data|Zotero data directory]] and temporarily disable auto-sync in the Sync pane of the Zotero preferences. |
- | + | ||
- | In Execute JS, switch the target window to an open browser window, paste the relevant code into the " | + | |
==== Example: Item Field Changes ==== | ==== Example: Item Field Changes ==== | ||
Line 400: | Line 348: | ||
var fieldID = Zotero.ItemFields.getID(fieldName); | var fieldID = Zotero.ItemFields.getID(fieldName); | ||
- | var s = new Zotero.Search; | + | var s = new Zotero.Search(); |
+ | s.libraryID = Zotero.Libraries.userLibraryID; | ||
s.addCondition(fieldName, | s.addCondition(fieldName, | ||
- | var ids = s.search(); | + | var ids = await s.search(); |
- | if (ids) { | + | if (!ids.length) { |
- | for(var i in ids) { | + | |
- | var item = Zotero.Items.get(ids[i]); | + | |
- | var mappedFieldID = Zotero.ItemFields.getFieldIDFromTypeAndBase(item.itemTypeID, | + | |
- | item.setField(mappedFieldID ? mappedFieldID : fieldID, newValue); | + | |
- | item.save(); | + | |
- | } | + | |
- | alert(ids.length + " items updated"); | + | |
} | } | ||
- | else { | + | await Zotero.DB.executeTransaction(async function () { |
- | alert("No items found"); | + | |
- | }</ | + | let item = await Zotero.Items.getAsync(id); |
+ | let mappedFieldID = Zotero.ItemFields.getFieldIDFromTypeAndBase(item.itemTypeID, | ||
+ | item.setField(mappedFieldID ? mappedFieldID : fieldID, newValue); | ||
+ | await item.save(); | ||
+ | | ||
+ | }); | ||
+ | return ids.length + " item(s) updated"; | ||
- | The list of field names to use can be retrieved via the server | + | The list of field names to use can be retrieved via the web API: |
- | ==== Example: Delete All Automatic Tags ==== | + | https://api.zotero.org/ |
- | + | ||
- | <code javascript> | + | |
- | Zotero.Tags.erase(Object.keys(Zotero.Tags.getAll([tagType])))</ | + | |
==== Example: Delete Tags By Name ==== | ==== Example: Delete Tags By Name ==== | ||
+ | |||
+ | < | ||
<code javascript> | <code javascript> | ||
Line 438: | Line 386: | ||
==== Example: Delete Tags By Part of Name ==== | ==== Example: Delete Tags By Part of Name ==== | ||
+ | |||
+ | < | ||
<code javascript> | <code javascript> |