Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
dev:client_coding:javascript_api [2018/09/16 13:23]
zuphilip [Managing citations and bibliographies] fix and expand related items
dev:client_coding:javascript_api [2019/06/11 03:44] (current)
dstillman [Running Ad Hoc JavaScript in Zotero]
Line 1: Line 1:
-<​html><​p id="​zotero-5-update-warning"​ style="​color:​ red; font-weight:​ bold">​We’re 
-in the process of updating the documentation for 
-<a href="​https://​www.zotero.org/​blog/​zotero-5-0">​Zotero 5.0</​a>​. Some documentation 
-may be outdated in the meantime. Thanks for your understanding.</​p></​html>​ 
- 
- 
 ====== Zotero JavaScript API ====== ====== Zotero JavaScript API ======
  
-Whereas Zotero'​s [[dev:​web_api|Server ​API]] allows read and write access to online Zotero libraries, it is also possible to access the local Zotero client through the JavaScript API. (It is also possible to [[dev/​client_coding/​direct_sqlite_database_access|directly access the local SQLite database]], but that approach is much more fragile.)+Whereas Zotero'​s [[dev:​web_api|Web API]] allows read and write access to online Zotero libraries, it is also possible to access the local Zotero client through the local JavaScript API. (It is also possible to [[dev/​client_coding/​direct_sqlite_database_access|directly access the local SQLite database]], but that approach is much more fragile.)
  
 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 ​to run arbitrary ​privileged JavaScript:
-    * [[http://​developer.mozilla.org|The Mozilla Developer Center]] (including [[http://​developer.mozilla.org/​en/​docs/​Building_an_Extension|Building an Extension]]) +
-    * [[https://​developer.mozilla.org/​en/​Setting_up_extension_development_environment|Setting up an extension development environment]] +
-  * A decent grasp of object-oriented JavaScript +
-    * [[http://​javascript.crockford.com/​private.html|public,​ private and privileged ​methods and members]]; object literal notation; closures +
-    * [[http://​developer.mozilla.org/​en/​docs/​Core_JavaScript_1.5_Reference|MDC Core JavaScript ​1.5 Reference]] (also note [[http://​developer.mozilla.org/​en/​docs/​New_in_JavaScript_1.6|New in JavaScript 1.6]] and [[http://​developer.mozilla.org/​en/​docs/​New_in_JavaScript_1.7|New in JavaScript 1.7]])+
  
-===== An Easy Debugging Environment =====+  - In the Advanced pane of the Zotero preferences,​ select Config Editor, and then set ''​devtools.chrome.enabled''​ to ''​true''​ and restart Zotero. 
 +  - In the Tools menu, select Run JavaScript. Open the Error Console, which also appears in Tools, will also be helpful. 
 +  - In the window that opens, enter JavaScript in the Code textbox and click Run or press Cmd-R/​Ctrl-R.
  
-Various Firefox extensions create servers within Firefox that expose ​the internal Firefox environmentincluding ​Zotero ​internalsThese can be used as shells for easy experimentationThese include:+To run asynchronous code, check the "Run as async function"​ checkbox. This runs the entered code wrapped in an [[https://​developer.mozilla.org/​en-US/​docs/​Web/​JavaScript/​Reference/​Statements/​async_function|async function]]allowing you to use ''​await''​ to wait for the resolution of promises returned by functions. Most Zotero ​functions that access the database, disk, or network are asynchronousIn this mode, the value of a ''​return''​ statement will be displayed in the right-hand pane upon successful completion.
  
-  * [[https://​addons.mozilla.org/​en-US/​firefox/​addon/​execute-js/​|ExecuteJS]] +In synchronous mode, the value of the final line will appear ​in the right-hand pane. 
-  * [[http://​davidkellogg.com/​wiki/​Main_Page|Plain Old Webserver]] (POW) - [[http://​github.com/​singingfish/​zotero-browser|Zotero Browser]] is an example of getting POW to interact with Zotero to create rich annotated bibliographies. +===== Zotero Code Architecture =====
-  * [[http://​wiki.github.com/​bard/​mozrepl/​|MozRepl]] - interactive shellavailable by telnetting to localhost on port 4242 (by default). Linux and Mac OS X users can use the built in telnet client. Windows users should use [[http://​www.chiark.greenend.org.uk/​~sgtatham/​putty/​|Putty]]. There is also a Firefox plugin to provide MozRepl access [[http://​ubik.cc/​2009/​09/​mozrepl-in-a-panel.html|directly inside Firefox]]. +
-    * **Potential MozRepl Problems**: Longer scripts can time out non-deterministically. A solution to this is to either send the code to the terminal application ​in smaller sized chunks, or to keep pasting ​the code in until it works. ​ Use an external library (like CPAN's MozRepl module) for finer grained control. ​  ​Syntax errors can cause silent failure. Avoid this by avoiding excessively long blocks of procedural code factor everything into smaller functions wherever possible.+
  
-Perl programmers should be aware of the [[http://​search.cpan.org/​perldoc?​MozRepl|MozRepl]] CPAN module, and the closely related [[http://​search.cpan.org/​perldoc?​MozRepl::​RemoteObject|MozRepl::​RemoteObject]] which allows you to access JavaScript objects from inside Perl programs.+==== Window Scope vsNon-Window Scope ===
  
 +Zotero code exists in either window scope and non-window scope.
  
-===== The Zotero ​Object ====+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'​s DOM and can interact with the UI. The main window-scope object in Zotero is ''​ZoteroPane'',​ from zoteroPane.js,​ which controls most interactions triggered in the main Zotero ​window.
  
-Zotero exposes an object-oriented JavaScript API that can be used to access and modify Zotero dataWithin ​Zotero ​itselfmost SQL statements are limited to the data layer, with all other elements (including ​the entire UI) using the data API to access the data. Through the base Zotero ​XPCOM servicethis functionality ​is available to all privileged code in Firefox, including other loaded extensions.+Non-window scope applies to lower-level code that doesn'​t have access ​to the DOMThis includes the core ''​Zotero''​ objectwhich contains ​all other non-window code, including the data layer used for retrieving and modifying library ​data. In Zotero, ​non-window code is contained within the ''​xpcom''​ subdirectory.
  
-==== XPCOM ====+Overlays and windows in Zotero can import the core ''​Zotero''​ object via the include.js script. Zotero methods can then be called from anywhere within the window'​s scope simply by calling, for example, ''​var item Zotero.Items.get(1);''​.
  
-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 codeWhile there are various mechanisms to get around this, the [[http://​developer.mozilla.org/​en/​docs/​Working_with_windows_in_chrome_code#​Using_an_XPCOM_singleton_component|recommended method]]and the method used by Zotero, is to use an XPCOM singleton component to store common datawhich can then be accessed from any window that imports ​the component. (XPCOM is the //cross-platform component object model//---the framework---that forms the basis for the Mozilla application environment.)+To access Zotero functionality from your own extension, you will need access ​to the core ''​Zotero''​ objectIf your extension operates within ​the main browser overlayyou already have access to the ''​Zotero''​ object and don’t need to take further steps. Otherwiseyou can import ​the Zotero object into other windows by including ​the script %%chrome://zotero/content/include.js%% within an HTML or XUL file:
  
-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 ''​wrappedJSObject''​ property of the component. The component itself does not define any XPCOM interfaces. (This is vaguely a hack, but it addresses our needs sufficiently and is an approach used by several other extensions.) Chrome overlays and windows in Zotero import the core object via the script include.js, which calls ''​getService()''​ on the component and assigns the wrapped object to the variable ''​Zotero''​. Zotero methods can then be called from anywhere within the window’s scope simply by calling, for example, ''​var item = Zotero.Items.get(1)''​.+<code html> 
 +<script ​src="​chrome://​zotero/​content/​include.js"><​script>​ 
 +</​code>​
  
-Access to the Zotero service is not limited to Zotero itself, however. As a standard XPCOM component, the Zotero service---and,​ specifically,​ the wrapped JavaScript object---can be accessed from anywhere within privileged code, including other loaded extensions. +Once you have ''​Zotero'',​ you can get the current ​''​ZoteroPane''​ object:
- +
-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 ''​Zotero'' ​object and don’t need to take further steps. Otherwise, you can import ​the Zotero object into other windows either by including the script %%chrome://​zotero/​content/​include.js%% within a XUL file (recommended) or by manually calling ​''​getService()'' ​directly on the Zotero XPCOM service and assigning the wrapped JS object ​to a variable. +
- +
-%%chrome://​zotero/​content/​include.js:%%+
 <code javascript>​ <code javascript>​
-var Zotero ​Components.classes["​@zotero.org/​Zotero;1"] +var zp = Zotero.getActiveZoteroPane(); 
- .getService(Components.interfaces.nsISupports+var items = zp.getSelectedItems()
- .wrappedJSObject+</​code> ​ 
-</​code>​+(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 ​extensions can use the system to perform additional operations when specific events occur---say, to sync item metadata with a web-based ​tool.+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: Available events:
Line 74: Line 61:
  
 //The Zotero JavaScript API is under-documented,​ and at present requires a lot of looking around in the source code.  The most useful parts of the source code are in chrome/​content/​zotero/​xpcom and xpcom/data, and the chrome/​content/​zotero (particularly zoteroPane.js and fileInterface.js).//​ //The Zotero JavaScript API is under-documented,​ and at present requires a lot of looking around in the source code.  The most useful parts of the source code are in chrome/​content/​zotero/​xpcom and xpcom/data, and the chrome/​content/​zotero (particularly zoteroPane.js and fileInterface.js).//​
- 
-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. ''​Zotero.Item = function(){...}''​) and singletons (''​Zotero.Items = **new** function(){...}''​). The most important example of the former is ''​Zotero.Item'',​ which represents a single item in the database. Singletons are used mostly to group related static methods into a single namespace. 
  
 ==== Adding items and modifying data ==== ==== Adding items and modifying data ====
  
-A typical operation might include a call to ''​Items.get()''​ to retrieve ​an ''​Item''​ instance, calls to ''​Item''​ methods on the retrieved object to modify data, and finally a ''​save()''​ to save the modified data to the database.+A typical operation might include a call to ''​Zotero.Items.get()''​ to retrieve ​''​Zotero.Item''​ instance, calls to ''​Zotero.Item''​ methods on the retrieved object to modify data, and finally a ''​save()'' ​(within a transaction) or ''​saveTx()''​ (outside a transaction) ​to save the modified data to the database.
  
 <code javascript>​ <code javascript>​
-var creator = new Zotero.Creator;​ +var item = new Zotero.Item('​book'​);​
-creator.firstName = '​William';​ +
-creator.lastName = '​Shakespeare';​ +
-creator.save();​ +
- +
-var item = new Zotero.Item+
-item.setType(Zotero.ItemTypes.getID('​book'​));+
 item.setField('​title',​ 'Much Ado About Nothing'​);​ item.setField('​title',​ 'Much Ado About Nothing'​);​
-item.setCreator(0creator'author'); +item.setCreators( 
-var itemID = item.save();​+    [ 
 +        { 
 +            firstName: "​William"​, 
 +            lastName: "​Shakespeare"​, 
 +            creatorType:​ "author
 +        } 
 +    ] 
 +); 
 +var itemID = await item.save()
 +return itemID;
 </​code>​ </​code>​
  
Line 105: Line 91:
 alert(item.getCreator(0));​ // {'​firstName'​=>'​William',​ '​lastName'​=>'​Shakespeare',​ alert(item.getCreator(0));​ // {'​firstName'​=>'​William',​ '​lastName'​=>'​Shakespeare',​
                            // ​  '​creatorTypeID'​=>​1,​ '​fieldMode'​=>​0}                            // ​  '​creatorTypeID'​=>​1,​ '​fieldMode'​=>​0}
 +// Alternative format
 +alert(item.getCreatorJSON(0));​ // {'​firstName'​=>'​William',​ '​lastName'​=>'​Shakespeare',​
 +                           // ​  '​creatorType'​=>'​author'​}
 +
 item.setField('​place',​ '​England'​);​ item.setField('​place',​ '​England'​);​
 item.setField('​date',​ 1599); item.setField('​date',​ 1599);
-item.save(); // update database with new data+await item.saveTx(); // update database with new data
  
 </​code>​ </​code>​
  
-==== Create new Zotero ​object ​==== +==== Get the Zotero Pane to interact with the Zotero ​UI ====
- +
-This is the first thing that you need to do when interacting with Zotero'​s +
-internals. ​ The code to do so is:+
  
 <code javascript>​ <code javascript>​
-var Zotero ​Components.classes["​@zotero.org/​Zotero;1"].getService(Components.interfaces.nsISupports).wrappedJSObject;+var ZoteroPane ​= Zotero.getActiveZoteroPane();
 </​code>​ </​code>​
  
-==== Get the Zotero Pane to interact with the Zotero ​GUI ====+Then grab the currently selected items from the Zotero ​pane:
  
 <code javascript>​ <code javascript>​
-var ZoteroPane = Components.classes["​@mozilla.org/​appshell/​window-mediator;​1"​] .getService(Components.interfaces.nsIWindowMediator).getMostRecentWindow("​navigator:​browser"​).ZoteroPane;​ +// Get first selected item 
-</​code>​ +var selectedItems ​= ZoteroPane.getSelectedItems();​ 
- +var item = selectedItems[0];
-Then grab the currently selected items from the zotero pane: +
- +
-<code javascript>​ +
-//get first selected item +
-var selected_items ​= ZoteroPane.getSelectedItems();​ +
-var item = selected_items[0];+
  
-// proced ​if selected ​item is neither a collection nor a note +// Proceed ​if an item is selected and it isn'​t ​a note 
-if ( item.isCollection() ​& ! item.isNote()) {+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 ​= item.getAttachments(false); +        // let attachmentIDs ​= item.getAttachments();​ 
-        // if (att_ids.length>​1) exit(); // bailout +        // let attachment ​= Zotero.Items.get(attachmentIDs[0]);
-        // item_att=Zotero.Items.get(att_ids[0]);+
     }     }
     alert(item.id);​     alert(item.id);​
Line 148: Line 128:
 </​code>​ </​code>​
  
-==== 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 ​items from your Zotero. ​ Creating an in-memory search is a +
-good start.+
  
-<code javascript> ​   var search ​new z.Search(); </​code>​+<code javascript>​ 
 +var collection ​ZoteroPane.getSelectedCollection(); 
 +var items = collection.getChildItems();​ 
 +// or you can obtain an array of itemIDs instead: 
 +var itemIDs = collection.getChildItems(true);​ 
 +</​code>​
  
-====  Search for items containing ​specific tag ====+=== Create ​subcollection of the selected collection ​===
  
-Starting with the code from "Setup a Zotero ​search"​ we then use the following +<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> ​   search.addCondition('​tag',​ '​is',​ 'tag name here'​);</​code>​+==== Zotero Search Basics ====
  
-==== Zotero Collection Operations ==== +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 
-=== Get the collection tree and display as a series of nested ordered lists === +good start.
- +
-This code was developed ​in the Firefox extension Plain Old Webserver server side JavaScript. ​ Note that it'​s ​recursive function. With a bit of jQuery the nested ordered list can be easily transformed into a tree widget.+
  
 <code javascript>​ <code javascript>​
-var Zotero ​Components.classes["​@zotero.org/​Zotero;​1"​] ​.getService(Components.interfaces.nsISupports).wrappedJSObject+var new Zotero.Search(); 
- +s.libraryID ​= Zotero.Libraries.userLibraryID;
-var render_collection = function(coll) { +
-    if (!coll) {  +
-        coll = null;  +
-    } +
-    var collections ​= Zotero.getCollections(coll);​ +
-    document.writeln("<​ul>"​);​ +
-    for (c in collections) { +
-        document.writeln('<​li>'​ + '<a href="​view_collection.sjs?​name='​ + encodeURI(collections[c].name) + '&​id='​ + collections[c].id + '">'​ + collections[c].name ​ + '</​a></​li>'​);​ +
-        if (collections[c].hasChildCollections) { +
-    var childCol = render_collection(collections[c].id);​ +
-        } +
-    }    +
-    document.writeln("</​ul>"​);​ +
-+
- +
-render_collection();+
 </​code>​ </​code>​
  
-=== Get the items for a particular collection ​===+=== Search ​for items containing ​specific tag ===
  
-<code javascript>​ +Starting with the above code, we then use the following ​code to retrieve items in My Library with particular tag:
-var Zotero = Components.classes["​@zotero.org/​Zotero;​1"​] .getService(Components.interfaces.nsISupports).wrappedJSObject;​ +
-var collectionid = pow_server.GET.id;​ // or some other way of finding ​the collectionID here +
-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 ​in a function ===+
  
 <code javascript>​ <code javascript>​
-async function createCollection(name) { +s.addCondition('​tag',​ '​is',​ '​tag ​name here'); 
-    var currentCollection = ZoteroPane.getSelectedCollection(); +var itemIDs ​= await s.search();
-    var collection = new Zotero.Collection();​ +
-    collection.name = name; +
-    collection.parentID = currentCollection.id;​ +
-    var collectionID ​= await collection.saveTx(); +
-    return collectionID;​ +
-}+
 </​code>​ </​code>​
  
-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 ''​await''​ before the +
-function call and make sure that this is within an ''​async''​ function. +
- +
-==== 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('​joinMode',​ '​any'​);​ // joinMode defaults to '​all'​ as per the  s.addCondition('​joinMode',​ '​any'​);​ // joinMode defaults to '​all'​ as per the 
-                                        // advanced search ​GUI+                                        // advanced search ​UI
 </​code>​ </​code>​
  
-To add the other conditions available in the advanced search ​GUI, use the following:+To add the other conditions available in the advanced search ​UI, use the following:
  
 <code javascript>​ <code javascript>​
Line 247: Line 197:
 === 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('​collectionID',​ '​is',​ collectionID);​+s.addCondition('​collectionID',​ '​is',​ collectionID); ​// e.g., 52
 s.addCondition('​savedSearchID',​ '​is',​ savedSearchID);​ s.addCondition('​savedSearchID',​ '​is',​ savedSearchID);​
 +</​code>​
 +
 +<code javascript>​
 +s.addCondition('​collection',​ '​is',​ collectionKey);​ // e.g., '​C72FDAP2'​
 +s.addCondition('​savedSearch',​ '​is',​ savedSearchKey);​
 </​code>​ </​code>​
  
Line 259: Line 214:
  
 <code javascript>​ <code javascript>​
-    ​var tagname ​= '​something';​ +var tagName ​= '​something';​ 
-    ​search.addCondition('​tag',​ '​is', ​tagname);+s.addCondition('​tag',​ '​is', ​tagName);
 </​code>​ </​code>​
  
Line 272: Line 227:
 results: results:
  
-<code javascript>​var results = search.search();</​code>​+<code javascript>​var results = await s.search();</​code>​
  
-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>​var items = z.Items.get(results);</​code>​+<code javascript>​var items = await Zotero.Items.getAsync(results);</​code>​
  
 ===== Managing citations and bibliographies ===== ===== Managing citations and bibliographies =====
Line 290: Line 244:
 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> ​ var qc = z.QuickCopy;​ +<code javascript>​var qc = Zotero.QuickCopy;​ 
-  var biblio = qc.getContentFromItems(new Array(item), +var biblio = qc.getContentFromItems([item]Zotero.Prefs.get("​export.quickCopy.setting"​));​ 
-                                      z.Prefs.get("​export.quickCopy.setting"​));​ +var biblio_html_format = cite.html;​ 
-    var biblio_html_format = cite.html;​ +var biblio_txt ​ = cite.text; ​
-    var biblio_txt ​        ​= cite.text; ​+
 </​code>​ </​code>​
  
Line 300: Line 253:
  
 <code javascript> ​ <code javascript> ​
-      ​var styles = zotero.Styles.getVisible();​ +await Zotero.Schema.schemeUpdatePromise;​ 
-      var style_info ​= []; +var styles = Zotero.Styles.getVisible();​ 
-      for each var s in styles) { +var styleInfo ​= []; 
-           style_info.push( { "id" ​s.styleID, ​"name" ​s.title } ); +for (let style of styles) { 
-       ​+    ​styleInfo.push({ id: style.styleID, name: style.title }); 
-      ​JSON.stringify(style_info)// e.g. to return json from the mozrepl+
 +return styleInfo;
 </​code>​ </​code>​
  
Line 318: Line 272:
 To get an item's abstract, we get the '​abstractNote'​ field from the Zotero item: To get an item's abstract, we get the '​abstractNote'​ field from the Zotero item:
  
-<code javascript> ​   var abstract = item.getField('​abstractNote'​);​ </​code>​+<code javascript>​var abstract = item.getField('​abstractNote'​);</​code>​
  
 ==== Get child notes for an item ==== ==== Get child notes for an item ====
Line 324: Line 278:
 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> ​    ​var notes = item.getNotes();​ </​code>​+<code javascript>​var ​noteIDs ​= item.getNotes();</​code>​
  
-This returns an array of notes. ​ Each note is in HTML format To get each note +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 (var j=0;​j<​notes.length;​j++) { +for (let id of noteIDs) { 
-        ​var ​note = z.Items.get(notes[j]); +    ​let ​note = Zotero.Items.get(id); 
-        var note_html ​= note.getNote();​ +    let noteHTML ​= note.getNote();​ 
-    +
 </​code>​ </​code>​
  
 ==== 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>​var ​relatedItems ​= item.relatedItems;</​code>​
-the Zotero database. ​ This includes items and notes. +
- +
-<code javascript> ​ var related_items ​= item.relatedItems</​code>​+
  
 ==== Set two items as related to each other  ==== ==== Set two items as related to each other  ====
Line 354: Line 304:
  
  
-==== Get an Item'​s ​Attachments ​===+==== Get an item'​s ​attachments ​===
  
-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' ​// some Zotero Item obtained previously +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
-    ​var attachments ​selected_items[item].getAttachments(false); +    ​let attachmentIDs ​= item.getAttachments();​ 
-    for (a in attachments) { +    for (let id of attachmentIDs) { 
-        ​var a_item ​= Zotero.Items.get(attachments[a]); +        ​let attachment ​= Zotero.Items.get(id); 
-        if (a_item.attachmentMIMEType ​== '​application/​pdf'​ +        if (attachment.attachmentContentType ​== '​application/​pdf'​ 
-            || a_item.attachmentMIMEType ​== '​text/​html'​) { +                || attachment.attachmentContentType ​== '​text/​html'​) { 
-            fulltext.push(a_item.attachmentText);​+            fulltext.push(await attachment.attachmentText);​
         }         }
     }     }
 } }
 +return fulltext;
 </​code>​ </​code>​
  
Line 387: Line 338:
 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.
  
-Firstinstall the [[https://​addons.mozilla.org/​en-US/​firefox/​addon/​execute-js/​|Execute JS]] Firefox extension to interact with Zotero via JavaScript. (Advanced users can also use the Firefox Browser Console or Scratchpad in Browser mode after enable chrome/​add-on debugging in the Web Console settings.) Back up your database first, ​and temporarily disable auto-sync in the Sync pane of the Zotero preferences+Before proceedingback 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 "​JS-Code to execute"​ box, make any necessary changes, and click "​Execute"​.+
  
 ==== Example: Item Field Changes ==== ==== Example: Item Field Changes ====
Line 400: Line 349:
  
 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,​ '​is',​ oldValue); s.addCondition(fieldName,​ '​is',​ oldValue);
-var ids = s.search();​ +var ids = await s.search();​ 
-if (ids) { +if (!ids.length) { 
- for(var i in ids) { +    ​return ​"No items found";
- var item = Zotero.Items.get(ids[i]);​ +
- var mappedFieldID = Zotero.ItemFields.getFieldIDFromTypeAndBase(item.itemTypeID,​ fieldName);​ +
- item.setField(mappedFieldID ? mappedFieldID : fieldID, newValue);​ +
- item.save();​ +
-+
- alert(ids.length + " items updated");+
 } }
-else +await Zotero.DB.executeTransaction(async function () 
- alert("No items found"); +    ​for ​(let id of ids) { 
-}</​code>​+        let item = await Zotero.Items.getAsync(id); 
 +        let mappedFieldID = Zotero.ItemFields.getFieldIDFromTypeAndBase(item.itemTypeID,​ fieldName);​ 
 +        item.setField(mappedFieldID ? mappedFieldID : fieldID, newValue);​ 
 +        await item.save();​ 
 +    ​} 
 +}); 
 +return ids.length + " item(s) updated";​</​code>​
  
-The list of field names to use can be retrieved via the server ​API: https://​api.zotero.org/​itemFields?​pprint=1.+The list of field names to use can be retrieved via the web API:
  
-==== ExampleDelete All Automatic Tags ==== +https://api.zotero.org/​itemFields?​pprint=1.
- +
-<code javascript>​var tagType = 1; // automatic +
-Zotero.Tags.erase(Object.keys(Zotero.Tags.getAll([tagType])))</​code>​+
  
 ==== Example: Delete Tags By Name ==== ==== Example: Delete Tags By Name ====
 +
 +<​html><​p id="​zotero-5-update-warning"​ style="​color:​ red; font-weight:​ bold">​This example has not been updated for Zotero 5 and should not currently be used.</​p></​html>​
  
 <code javascript>​var tags = ["​foo",​ "​bar",​ "​baz"​];​ <code javascript>​var tags = ["​foo",​ "​bar",​ "​baz"​];​
Line 438: Line 387:
  
 ==== Example: Delete Tags By Part of Name ==== ==== Example: Delete Tags By Part of Name ====
 +
 +<​html><​p id="​zotero-5-update-warning"​ style="​color:​ red; font-weight:​ bold">​This example has not been updated for Zotero 5 and should not currently be used.</​p></​html>​
  
 <code javascript>​var tags = ["​foo",​ "​bar",​ "​baz"​];​ <code javascript>​var tags = ["​foo",​ "​bar",​ "​baz"​];​
dev/client_coding/javascript_api.1537118611.txt.gz · Last modified: 2018/09/16 13:23 by zuphilip