Differences
This shows you the differences between two versions of the page.
Previous revisionNext revision | |||
— | dev:client_coding:javascript_api [2019/08/18 05:41] – [Running Ad Hoc JavaScript in Zotero] Add examples zuphilip | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Zotero JavaScript API ====== | ||
+ | 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. | ||
+ | |||
+ | |||
+ | ===== Running Ad Hoc JavaScript in Zotero ===== | ||
+ | |||
+ | Zotero includes an option to run arbitrary privileged JavaScript: | ||
+ | |||
+ | - 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/ | ||
+ | |||
+ | When running **asynchronous** code containing '' | ||
+ | |||
+ | <code javascript> | ||
+ | var items = Zotero.getActiveZoteroPane().getSelectedItems(); | ||
+ | return items; | ||
+ | </ | ||
+ | |||
+ | In **synchronous** mode, the value of the final line will appear in the right-hand pane. The same result as above could be achieved in synchonous mode with | ||
+ | |||
+ | <code javascript> | ||
+ | var items = Zotero.getActiveZoteroPane().getSelectedItems(); | ||
+ | items; | ||
+ | </ | ||
+ | ===== Zotero Code Architecture ===== | ||
+ | |||
+ | ==== Window Scope vs. Non-Window Scope === | ||
+ | |||
+ | Zotero code exists in either window scope and non-window scope. | ||
+ | |||
+ | 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' | ||
+ | |||
+ | Non-window scope applies to lower-level code that doesn' | ||
+ | |||
+ | Overlays and windows in Zotero can import the core '' | ||
+ | |||
+ | To access Zotero functionality from your own extension, you will need access to the core '' | ||
+ | |||
+ | <code html> | ||
+ | <script src=" | ||
+ | </ | ||
+ | |||
+ | 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 extensions can use the system to perform additional operations when specific events occur — say, to sync item metadata with a web-based API. | ||
+ | |||
+ | Available events: | ||
+ | |||
+ | * add (c, s, i, t, ci, it) | ||
+ | * modify (c, s, i, t) | ||
+ | * delete (c, s, i, t) | ||
+ | * remove (ci, it) | ||
+ | * move (c, for changing collection parent) | ||
+ | |||
+ | c = collection, s = search (saved search), i = item, t = tag, ci = collection-item, | ||
+ | |||
+ | See the Zotero [[dev/ | ||
+ | |||
+ | ===== API Methods ===== | ||
+ | |||
+ | //The Zotero JavaScript API is under-documented, | ||
+ | |||
+ | ==== Adding items and modifying data ==== | ||
+ | |||
+ | A typical operation might include a call to '' | ||
+ | |||
+ | <code javascript> | ||
+ | var item = new Zotero.Item(' | ||
+ | item.setField(' | ||
+ | item.setCreators( | ||
+ | [ | ||
+ | { | ||
+ | firstName: " | ||
+ | lastName: " | ||
+ | creatorType: | ||
+ | } | ||
+ | ] | ||
+ | ); | ||
+ | var itemID = await item.save(); | ||
+ | return itemID; | ||
+ | </ | ||
+ | |||
+ | <code javascript> | ||
+ | |||
+ | // Fetch saved items with Items.get(itemID) | ||
+ | var item = Zotero.Items.get(itemID); | ||
+ | |||
+ | alert(item.getField(' | ||
+ | |||
+ | alert(item.getCreator(0)); | ||
+ | // | ||
+ | // Alternative format | ||
+ | alert(item.getCreatorJSON(0)); | ||
+ | // | ||
+ | |||
+ | item.setField(' | ||
+ | item.setField(' | ||
+ | await item.saveTx(); | ||
+ | |||
+ | </ | ||
+ | |||
+ | ==== Get the Zotero Pane to interact with the Zotero UI ==== | ||
+ | |||
+ | <code javascript> | ||
+ | var ZoteroPane = Zotero.getActiveZoteroPane(); | ||
+ | </ | ||
+ | |||
+ | Then grab the currently selected items from the Zotero pane: | ||
+ | |||
+ | <code javascript> | ||
+ | // Get first selected item | ||
+ | var selectedItems = ZoteroPane.getSelectedItems(); | ||
+ | var item = selectedItems[0]; | ||
+ | |||
+ | // Proceed if an item is selected and it isn't a note | ||
+ | if (item && !item.isNote()) { | ||
+ | if (item.isAttachment()) { | ||
+ | // find out about attachment | ||
+ | } | ||
+ | if (item.isRegularItem()) { | ||
+ | // We could grab attachments: | ||
+ | // let attachmentIDs = item.getAttachments(); | ||
+ | // let attachment = Zotero.Items.get(attachmentIDs[0]); | ||
+ | } | ||
+ | alert(item.id); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Collection Operations ==== | ||
+ | |||
+ | === Get the items in the selected collection === | ||
+ | |||
+ | <code javascript> | ||
+ | var collection = ZoteroPane.getSelectedCollection(); | ||
+ | var items = collection.getChildItems(); | ||
+ | // or you can obtain an array of itemIDs instead: | ||
+ | var itemIDs = collection.getChildItems(true); | ||
+ | </ | ||
+ | |||
+ | === Create a subcollection of the selected collection === | ||
+ | |||
+ | <code javascript> | ||
+ | var currentCollection = ZoteroPane.getSelectedCollection(); | ||
+ | var collection = new Zotero.Collection(); | ||
+ | collection.name = name; | ||
+ | collection.parentID = currentCollection.id; | ||
+ | var collectionID = await collection.saveTx(); | ||
+ | return collectionID; | ||
+ | </ | ||
+ | |||
+ | ==== Zotero Search Basics ==== | ||
+ | |||
+ | If you are focused on data access, the first thing you will want to do | ||
+ | will be to retrieve items from your Zotero library. Creating an in-memory search is a | ||
+ | good start. | ||
+ | |||
+ | <code javascript> | ||
+ | var s = new Zotero.Search(); | ||
+ | s.libraryID = Zotero.Libraries.userLibraryID; | ||
+ | </ | ||
+ | |||
+ | === Search for items containing a specific tag === | ||
+ | |||
+ | Starting with the above code, we then use the following code to retrieve items in My Library with a particular tag: | ||
+ | |||
+ | <code javascript> | ||
+ | s.addCondition(' | ||
+ | var itemIDs = await s.search(); | ||
+ | </ | ||
+ | |||
+ | === Advanced searches === | ||
+ | |||
+ | <code javascript> | ||
+ | var s = new Zotero.Search(); | ||
+ | s.libraryID = Zotero.Libraries.userLibraryID; | ||
+ | s.addCondition(' | ||
+ | // advanced search UI | ||
+ | </ | ||
+ | |||
+ | To add the other conditions available in the advanced search UI, use the following: | ||
+ | |||
+ | <code javascript> | ||
+ | s.addCondition(' | ||
+ | s.addCondition(' | ||
+ | s.addCondition(' | ||
+ | </ | ||
+ | |||
+ | There are also some " | ||
+ | |||
+ | One example is: | ||
+ | |||
+ | <code javascript> | ||
+ | s.addCondition(' | ||
+ | </ | ||
+ | |||
+ | === Search by collection === | ||
+ | |||
+ | To search for a collection or a saved search you need to know the ID or key: | ||
+ | |||
+ | <code javascript> | ||
+ | s.addCondition(' | ||
+ | s.addCondition(' | ||
+ | </ | ||
+ | |||
+ | <code javascript> | ||
+ | s.addCondition(' | ||
+ | s.addCondition(' | ||
+ | </ | ||
+ | |||
+ | === Search by tag === | ||
+ | |||
+ | To search by tag, you use the tag text: | ||
+ | |||
+ | <code javascript> | ||
+ | var tagName = ' | ||
+ | s.addCondition(' | ||
+ | </ | ||
+ | |||
+ | === Search by other fields === | ||
+ | |||
+ | The complete list of other fields available to search on is on the [[dev/ | ||
+ | |||
+ | ==== Executing the search ==== | ||
+ | |||
+ | Once the search conditions have been set up, then it's time to execute the | ||
+ | results: | ||
+ | |||
+ | <code javascript> | ||
+ | |||
+ | 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: | ||
+ | |||
+ | <code javascript> | ||
+ | |||
+ | ===== Managing citations and bibliographies ===== | ||
+ | |||
+ | TODO: this is pretty sparse. | ||
+ | |||
+ | ==== Getting a bibliography for an array of items: ==== | ||
+ | |||
+ | Here we use Zotero' | ||
+ | specified in Zotero' | ||
+ | |||
+ | First we start with a list of as in the previous entry. | ||
+ | |||
+ | <code javascript> | ||
+ | var biblio = qc.getContentFromItems([item], | ||
+ | var biblio_html_format = cite.html; | ||
+ | var biblio_txt | ||
+ | </ | ||
+ | |||
+ | ==== Get a list of available styles ==== | ||
+ | |||
+ | <code javascript> | ||
+ | await Zotero.Schema.schemeUpdatePromise; | ||
+ | var styles = Zotero.Styles.getVisible(); | ||
+ | var styleInfo = []; | ||
+ | for (let style of styles) { | ||
+ | styleInfo.push({ id: style.styleID, | ||
+ | } | ||
+ | return styleInfo; | ||
+ | </ | ||
+ | |||
+ | TODO: get citations. | ||
+ | especially RTF | ||
+ | |||
+ | ==== Get information about an item. ==== | ||
+ | |||
+ | TODO: need to list all the possible fields here, and what kind of entry they | ||
+ | belong to. | ||
+ | |||
+ | To get an item's abstract, we get the ' | ||
+ | |||
+ | <code javascript> | ||
+ | |||
+ | ==== Get child notes for an item ==== | ||
+ | |||
+ | To get the child notes for an item, we use the following code: | ||
+ | |||
+ | <code javascript> | ||
+ | |||
+ | This returns an array of ids of note items. To get each note in turn we just iterate through the array: | ||
+ | |||
+ | <code javascript> | ||
+ | for (let id of noteIDs) { | ||
+ | let note = Zotero.Items.get(id); | ||
+ | let noteHTML = note.getNote(); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Get an item's related items ==== | ||
+ | |||
+ | <code javascript> | ||
+ | |||
+ | ==== Set two items as related to each other ==== | ||
+ | |||
+ | Given two items '' | ||
+ | to each other by using the '' | ||
+ | |||
+ | <code javascript> | ||
+ | await itemA.saveTx(); | ||
+ | itemB.addRelatedItem(itemA); | ||
+ | await itemB.saveTx();</ | ||
+ | |||
+ | |||
+ | ==== Get an item's attachments === | ||
+ | |||
+ | 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> | ||
+ | var item = ZoteroPane.getSelectedItems()[0]; | ||
+ | var fulltext = []; | ||
+ | if (item.isRegularItem()) { // not an attachment already | ||
+ | let attachmentIDs = item.getAttachments(); | ||
+ | for (let id of attachmentIDs) { | ||
+ | let attachment = Zotero.Items.get(id); | ||
+ | if (attachment.attachmentContentType == ' | ||
+ | || attachment.attachmentContentType == ' | ||
+ | fulltext.push(await attachment.attachmentText); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | return fulltext; | ||
+ | </ | ||
+ | |||
+ | ==== To Do === | ||
+ | |||
+ | * Select a Zotero saved search | ||
+ | * Combining search terms | ||
+ | * Complete list of search operators | ||
+ | * Complete list of search fields - with description of what the more obscure fields mean - e.g. abstractNote for abstract, and how do we search the fulltext archive? | ||
+ | * fulltext for an item | ||
+ | * Get stored attachments for an item | ||
+ | |||
+ | |||
+ | ===== Batch Editing ===== | ||
+ | |||
+ | 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. | ||
+ | |||
+ | Before proceeding, back up your [[: | ||
+ | |||
+ | ==== Example: Item Field Changes ==== | ||
+ | |||
+ | Edit the first three lines as necessary: | ||
+ | |||
+ | <code javascript> | ||
+ | var oldValue = " | ||
+ | var newValue = " | ||
+ | |||
+ | var fieldID = Zotero.ItemFields.getID(fieldName); | ||
+ | var s = new Zotero.Search(); | ||
+ | s.libraryID = Zotero.Libraries.userLibraryID; | ||
+ | s.addCondition(fieldName, | ||
+ | var ids = await s.search(); | ||
+ | if (!ids.length) { | ||
+ | return "No items found"; | ||
+ | } | ||
+ | await Zotero.DB.executeTransaction(async function () { | ||
+ | for (let id of ids) { | ||
+ | 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 web API: | ||
+ | |||
+ | https:// | ||
+ | |||
+ | ==== Example: Delete Tags By Name ==== | ||
+ | |||
+ | < | ||
+ | |||
+ | <code javascript> | ||
+ | var ids = []; | ||
+ | var allTags = Zotero.Tags.search(); | ||
+ | tags = tags.map(tag => tag.toLowerCase()); | ||
+ | for (var id in allTags) { | ||
+ | if (tags.indexOf(allTags[id].name.toLowerCase()) != -1) { | ||
+ | ids.push(id); | ||
+ | } | ||
+ | } | ||
+ | Zotero.Tags.erase(ids); | ||
+ | </ | ||
+ | |||
+ | ==== Example: Delete Tags By Part of Name ==== | ||
+ | |||
+ | < | ||
+ | |||
+ | <code javascript> | ||
+ | var ids = []; | ||
+ | tags.forEach(function (tag) { | ||
+ | ids = ids.concat(Object.keys(Zotero.Tags.search(tag))); | ||
+ | }); | ||
+ | Zotero.Tags.erase(ids);</ |