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 [2019/01/22 00:42] – [Helpful Resources] dstillman | 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 ====== | ||
Line 14: | Line 8: | ||
===== Running Ad Hoc JavaScript in Zotero ===== | ===== Running Ad Hoc JavaScript in Zotero ===== | ||
- | Zotero | + | Zotero |
- | | + | - In the Tools → Developer |
- | | + | - In the window that opens, enter JavaScript in the Code textbox and click Run or press Cmd-R/ |
- | - In the window that opens, enter JavaScript in the Code textbox and click Run or press Cmd-R/ | + | |
- | (Before Zotero 5.0.61 is released, you'll need to install | + | When running asynchronous code containing |
- | ===== The Zotero | + | |
- | Zotero exposes an object-oriented JavaScript API that can be used to access and modify Zotero data. Within Zotero itself, most SQL statements are limited to the data layer, with all other elements (including | + | In synchronous mode, the value of the final line will appear in the right-hand pane. |
+ | ===== Zotero | ||
- | ==== XPCOM ==== | + | ==== Window Scope vs. Non-Window Scope === |
- | A common problem | + | Zotero code exists |
- | The base Zotero | + | Window scope applies to code that runs within either the main Zotero |
- | Access | + | Non-window scope applies to lower-level code that doesn' |
- | 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, you already have access to the '' | + | Overlays and windows |
- | %%chrome:// | + | To access Zotero functionality from your own extension, you will need access to the core '' |
- | < | + | |
- | var Zotero | + | < |
- | .getService(Components.interfaces.nsISupports) | + | <script src="chrome://zotero/content/ |
- | .wrappedJSObject; | + | |
</ | </ | ||
- | ===== Notification System | + | Once you have '' |
+ | <code javascript> | ||
+ | var zp = Zotero.getActiveZoteroPane(); | ||
+ | var items = zp.getSelectedItems(); | ||
+ | </ | ||
+ | (The Zotero pane will always be available unless the main window is closed, as is possible on macOS.) | ||
+ | |||
+ | ==== 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 61: | 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 92: | 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 135: | 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 234: | 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 246: | Line 213: | ||
<code javascript> | <code javascript> | ||
- | | + | var tagName |
- | | + | s.addCondition(' |
</ | </ | ||
Line 259: | 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 277: | 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 287: | 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 305: | 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 311: | 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 341: | 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 374: | 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 387: | 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, | |
- | The list of field names to use can be retrieved via the server API: https://api.zotero.org/itemFields? | + | item.setField(mappedFieldID ? mappedFieldID : fieldID, newValue); |
+ | await item.save(); | ||
+ | | ||
+ | }); | ||
+ | return ids.length + " item(s) updated";< | ||
- | ==== Example: Delete All Automatic Tags ==== | + | The list of field names to use can be retrieved via the web API: |
- | <code javascript> | + | https://api.zotero.org/ |
- | Zotero.Tags.erase(Object.keys(Zotero.Tags.getAll([tagType])))</ | + | |
==== Example: Delete Tags By Name ==== | ==== Example: Delete Tags By Name ==== | ||
+ | |||
+ | < | ||
<code javascript> | <code javascript> | ||
Line 425: | Line 386: | ||
==== Example: Delete Tags By Part of Name ==== | ==== Example: Delete Tags By Part of Name ==== | ||
+ | |||
+ | < | ||
<code javascript> | <code javascript> |