Data Unbound

Data Architect, Consultant, Trainer, and Author Raymond Yee on data and software in research and education

January 2nd, 2009

copying to clipboard Zotero citations as HTML w/ chickenfoot

Here's a first pass at a Chickenfoot script that copies selected Zotero items as HTML to the clipboard.  (I couldn't find anything in the Zotero interface to do exactly this function.  There is a  Create Bibliography from Selected Item(s)->Copy to Clipboard but that seems to copy citations as RTF for me.)

var Zotero = chromeWindow.Zotero;
var ZoteroPane = chromeWindow.ZoteroPane;
var zfi = chromeWindow.Zotero_File_Interface;

// zfi.bibliographyFromItems();

// create the bibliography out of the default style 
// and copy directly to clipboard w/o any popup windows.

// get the default style
var style=Zotero.Prefs.get("export.lastStyle");

// e.g., http://www.zotero.org/styles/chicago-note-bibliographystyle

var items = ZoteroPane.getSelectedItems();
zfi.copyItemsToClipboard(items, style);

For more details, you can look at how Zotero_File_Interface is defined in v 1.0.9 to study copyItemsToClipboard.

December 16th, 2008

Range of creator types in Zotero

One thing that I didn't know about Zotero was the range of creator types in Zotero. Here's a Chickenfoot script that you can run to list them:

// to explore the range of creatorTypes
// to do:  figure out how to get localized string for different creator types

var Zotero = chromeWindow.Zotero;
var creatorTypes = Zotero.CreatorTypes.getTypes()
for (var i=0; i<creatorTypes.length; i++) {
  output(creatorTypes[i].id + ": " + Zotero.CreatorTypes.getName(creatorTypes[i].id));
}

The results (in Zotero 1.0.7):

22: artist
15: attorneyAgent
1: author
20: cartographer
11: castMember
23: commenter
18: composer
2: contributor
13: counsel
8: director
3: editor
25: guest
6: interviewee
7: interviewer
14: inventor
17: performer
26: podcaster
24: presenter
10: producer
21: programmer
16: recipient
27: reviewedAuthor
9: scriptwriter
5: seriesEditor
12: sponsor
4: translator
19: wordsBy

December 16th, 2008

Accessing attributes of Zotero Items

The following Chickenfoot script demonstrates how to access attributes related to Zotero items. (I've run this script on both Zotero 1.0.7 and the latest named release of  Zotero 1.5 Sync Preview (1.5-sync3.5))):

// zotero_item_show_pieces.js
// code to extract elements of the first selected item
// Dec 16, 2008

var Zotero = chromeWindow.Zotero;
var ZoteroPane = chromeWindow.ZoteroPane;
var items = ZoteroPane.getSelectedItems();
var item = items[0];

// get item ID

output("item ID: " + item.id);

// get named type

var iType = item.getType();
output("type: " + Zotero.ItemTypes.getName(iType));

// figure out whether this is a top level item, note, etc.
// if not, is it a  note or attachment
output("isRegularItem: " + item.isRegularItem());
output("isAttachment: " + item.isAttachment());
output("isNote: " + item.isNote()); // or just check type == note

// get all possible fields for the type and their value
var fields = Zotero.ItemFields.getItemTypeFields(iType);
for (var i=0; i<fields.length; i++) {
  fieldName = Zotero.ItemFields.getName(fields[i]);
  fieldVal = item.getField(fieldName);
  fieldLocalName = Zotero.ItemFields.getLocalizedString(iType,fields[i])
  output(fieldLocalName + ": " + fieldVal);
}
//fields;

// get creators

var creators = item.getCreators();

for (var i=0; i<creators.length; i++) {
  // Zoteo 1.5 +
  if (creators[i].ref) {
    output (Zotero.CreatorTypes.getName(creators[i].creatorTypeID) + ": " +creators[i].ref.lastName + ", " + creators[i].ref.firstName);  }
  // Zotero 1.0.x
  else {
    output (Zotero.CreatorTypes.getName(creators[i].creatorTypeID) + ": " +creators[i].lastName + ", " + creators[i].firstName);
  }
}

// date added and date modified
output("Date Added: " + item.getField('dateAdded'));
output("Date Modified: " + item.getField('dateModified'));

// Notes -- return ids for the notes

var notes = item.getNotes();
output("notes: " + notes);

// Attachments -- there are more details to take care of
// https://www.zotero.org/trac/browser/extension/tags/1.5-sync3.5/chrome/content/zotero/xpcom/data/item.js#L3469

var attachments = item.getAttachments();
output("attachments: " + attachments);

// Tags -- Z 1.5.x has more complex structures than Z 1.0.7

var tags = item.getTags();
output("tags:");
for (var i=0; i<tags.length; i++) {
  if (tags[i].name) {
    output("|" + tags[i].name + "|");
  }
  else {
  output("|" + tags[i]['tag'] + "|");
  }
}

// Related

// https://www.zotero.org/trac/browser/extension/tags/1.0.7/chrome/content/zotero/xpcom/data_access.js#L2180

if (item.getSeeAlso) {
  var related = item.getSeeAlso();
} else if (item.relatedItems) {
  var related = item.relatedItems;
}

output("related: " + related);

// Collections -- get which collections the item belongs in.
output("collections: " + item.getCollections());

A few notes:

  • Some of the conditionals in the script reflect differences in how Zotero 1.0.x and the upcoming Zotero 1.5+ Zotero object model work.
  • For Zotero 1.5+,  you can learn a lot about how to access attributes related to a Zotero item by studying Zotero.Item.serialize (v 1.5-sync3.5)
December 11th, 2008

Adding all tabs to Zotero Version 2 — scraping translatable sites

In a previous post adding all Firefox tabs to Zotero using Chickenfoot, I showed how to write a Chickenfoot script to loop through all Firefox  tabs and add each of them as an item into Zotero.  A limitation of the script was that it used only ZoteroPane.addItemFromPage for every tab, even if a given tab had a translator that could be used to save the item. As explained in Adding items to Zotero with Chickenfoot, you can use Zotero_Browser.scrapeThisPage to invoke the appropriate translator for tab. The reason I didn't use Zotero_Browser.scrapeThisPage in my Chickenfoot script to add items is that I didn't know how to write a function to determine suitable translator exists.

Now, I think I've come up with a way of determining whether a translator exists — though I'm not highly confident that the solution is fullproof.    I'll share my  Chickenfoot script here, explain the logic behind it, and write about its possible limitations.  First the script:

// add_each_tab_to_Zotero_2.js
// R. Yee

var Zotero = chromeWindow.Zotero;
var ZoteroPane = chromeWindow.ZoteroPane;
var Zotero_Browser = chromeWindow.Zotero_Browser;
var tabBrowser = getTabBrowser(chromeWindow);

// getIcon returns a link to the translator icon for the current
// tab and false if there is no suitable
// translator and 'chrome://zotero/skin/treesource-collection.png'
// if there are multiple savable elements on a page

function getIcon() {

  var browser = Zotero_Browser.tabbrowser.selectedBrowser;
  var tab0 = new Zotero_Browser.Tab(browser);

  // need to figure out whether doc is HTMLDocument
  // doc instanceof HTMLDocument doesn't seem to work here

  var doc = browser.contentWindow.document;

  // in emulation of
  // https://www.zotero.org/trac/browser/extension/tags/1.0.7/chrome/content/zotero/browser.js#L311

  var rootDoc = doc;
  if (rootDoc.defaultView) {
    while(rootDoc.defaultView.frameElement) {
      rootDoc = rootDoc.defaultView.frameElement.ownerDocument;
    }
  }

  // detect possible translators and return the corresponding icon
  tab0.detectTranslators(rootDoc,doc);
  return tab0.getCaptureIcon();

} // getIcon()

// create a new collection with current date
var new_Collection = Zotero.Collections.add("_Saved " + (new Date()).toLocaleString());

// output # tabs
output(tabBrowser.browsers.length);

// loop through tabs, selecting each one in turn
for (var i=0; i < tabBrowser.browsers.length; i++) {
  tabBrowser.mTabContainer.advanceSelectedTab(1, true);
  output(tabBrowser.selectedBrowser.contentWindow.location);
  var icon = getIcon();
  // if icon is not false and not representing multiple items -- scrape page
  if (icon && icon != 'chrome://zotero/skin/treesource-collection.png') {
    Zotero_Browser.scrapeThisPage(new_Collection.id);
  // otherwise add item as a generic web page
  } else {
   ZoteroPane.addItemFromPage(new_Collection.id);
  }
}

A few points about the script:

When I ran the script with 3 tabs, it seemed to work fine. When I had 20+ tabs, all the tabs were saved — but only the first one ended up in the right collection. (I don't know why….) Also, the code is not terribly elegant — for example, it depends on creating a new Zotero_Browser.Tab for each tab; I figure there should be some way to read off whether an icon exists in the interface already without having to recalculate possible translators.

December 2nd, 2008

Getting primary and secondary item types in Zotero

A Chickenfoot script for calculating the primary and secondary Zotero items (which draws upon the code in overlay.js->onLoad():


// based on https://www.zotero.org/trac/browser/extension/tags/1.0.7/chrome/content/zotero/overlay.js#L153

var Zotero = chromeWindow.Zotero;

var itemTypes = Zotero.ItemTypes.getPrimaryTypes();

for(var i = 0; i<itemTypes.length; i++) {
  output (itemTypes[i]['name'] + ":" + itemTypes[i]['id']);
}

var itemTypes = Zotero.ItemTypes.getSecondaryTypes();

for(var i = 0; i<itemTypes.length; i++) {
  output (itemTypes[i]['name'] + ":" + itemTypes[i]['id']);
}

The output of the script is (for Zotero 1.0.7):

book:2
bookSection:3
document:34
journalArticle:4
magazineArticle:5
newspaperArticle:6
artwork:12
audioRecording:26
bill:16
blogPost:23
case:17
computerProgram:32
conferencePaper:33
dictionaryEntry:36
email:21
encyclopediaArticle:35
film:11
forumPost:25
hearing:18
instantMessage:24
interview:10
letter:8
manuscript:9
map:22
patent:19
podcast:31
presentation:27
radioBroadcast:30
report:15
statute:20
thesis:7
tvBroadcast:29
videoRecording:28

When you hit "New Item" button in Zotero, you are given the option of creating a new item of one of a given type:

primary and secondary Zotero item types

Furthermore, you can use Zotero.ItemTypes to map between item types and IDs:

Zotero.ItemTypes.getID('book'); yields 2

and

Zotero.ItemTypes.getName(2); returns book

November 21st, 2008

A screencast to show Chickenfoot scripting of Zotero

Several weeks ago, I wrote (a blog post and a post on the zotero-dev group) on how to use Chickenfoot to script Zotero. I'm starting to experiment with creating little videos that I hope will make my technical posts easier to understand. Here's one I just made to illustrate what I wrote about in the previous post:

Please forgive my video-newbie level of craft — I'm still learning to make my way around Camtasia Studio 6. (One fault is that I produced the video for 640×480, which is not the optimal setting for YouTube.)

Enjoy!

November 12th, 2008

adding all Firefox tabs to Zotero using Chickenfoot

We can now build on what was presented in the previous blog post: Adding items to Zotero with Chickenfoot.

The end goal is to loop through all the tabs and add Each of them as items into Zotero. So we need to figure out how to access the tabs. I would do so here by presenting a series of code fragments that you can try out using Chickenfoot and Zotero.

But let's first work through some code to manipulate tabs in Firefox, drawn from two reference documents:

  • tabbrowser - MDC: a reference to the tabbrowser object
  • Tabbed browser - MDC has some nice sample code, which seems to work best in Firefox 3.x, — something seemed to fail in Firefox 2.x (I should put an example here)

For example, one example of code doesn't work in 2.x — using addTab:

var tabBrowser = getTabBrowser(chromeWindow);
tabBrowser.addTab("http://yahoo.com");

Since I've had some problems using Firefox 2.x, I recommend running the following code fragments in Firefox 3.x.

First, let's get the tabbrowser object:

var tabBrowser = getTabBrowser(chromeWindow);
tabBrowser;

You can show the URL of the currently selected tab:

var tabBrowser = getTabBrowser(chromeWindow);
tabBrowser.selectedBrowser.contentWindow.location;

The following code advances selects the tab to the right of the currently tab — and loops around if needed:

var tabBrowser = getTabBrowser(chromeWindow);
tabBrowser.mTabContainer.advanceSelectedTab(1, true);

We can write out the current number of open tabs:

var tabBrowser = getTabBrowser(chromeWindow);
output(tabBrowser.browsers.length);

Here's code to select each tab in turn and output its location:

var tabBrowser = getTabBrowser(chromeWindow);
output(tabBrowser.browsers.length);
for (var i=0; i < tabBrowser.browsers.length; i++) {
  tabBrowser.mTabContainer.advanceSelectedTab(1, true);
  output(tabBrowser.selectedBrowser.contentWindow.location);
}

how to create a new collection in Zotero:

var Zotero = chromeWindow.Zotero;
var new_Collection = Zotero.Collections.add("testing");
new_Collection;

Here's code to create a new collection and add an item to it. (In my tries, the code crashes in FF 2.0.x but seems to work in FF 3.0.3):

var Zotero = chromeWindow.Zotero;
var ZoteroPane = chromeWindow.ZoteroPane;

var new_Collection = Zotero.Collections.add("testing");
var new_item = ZoteroPane.addItemFromPage(new_Collection.id);

Here's code that pulls it altogether — it loops through the tabs and adds the document of each tab to Zotero in a new collection:

var Zotero = chromeWindow.Zotero;
var ZoteroPane = chromeWindow.ZoteroPane;
var tabBrowser = getTabBrowser(chromeWindow);

var new_Collection = Zotero.Collections.add("Saved from tabs");
output(tabBrowser.browsers.length);

for (var i=0; i < tabBrowser.browsers.length; i++) {
  tabBrowser.mTabContainer.advanceSelectedTab(1, true);
  output(tabBrowser.selectedBrowser.contentWindow.location);
  var new_item = ZoteroPane.addItemFromPage(new_Collection.id);
}
November 1st, 2008

Adding items to Zotero with Chickenfoot

Here I report a few code fragments that you can try in Chickenfoot to create a Zotero item from the contents in the selected tab of your browser. (For a bit of context for what I'm talking about here, see my previous post Data Unbound » Accessing Zotero via Chickenfoot: a warm up exercise.)

There's a difference between the Zotero function that is invoked if an appropriate translator is found verses when there is no translator (and you have to then "create a new item from current page". (It would be helpful to show you how I deduced what these code fragments should be — I hope to do so later.)

When there is an appropriate translator, you invoke Zotero_Browser.scrapeThisPage:

var Zotero_Browser = chromeWindow.Zotero_Browser;
Zotero_Browser.scrapeThisPage();

When there isn't — and you add the page to Zotero as a web page using ZoteroPane.addItemFromPage:

var ZoteroPane = chromeWindow.ZoteroPane;
ZoteroPane.addItemFromPage();

Yet to figure out: How to ask Zotero whether for the selected tab there is an appropriate translator to use.

October 28th, 2008

Accessing Zotero via Chickenfoot: a warm up exercise

I'm currently learning how to program Zotero, specifically how to integrate Zotero with other applications. I document my learning experience to make it easier for others to learn what I've learned. Note that I'm still learning (I'm far from an expert) — so I think my advice will improve over time. But it decided not to wait until I am an expert before I share my experiences.

You can write Zotero plugins (a Firefox extension that accesses the Zotero extension) as a way of extending Zotero — see plugins [Zotero Documentation].

In a series of posts, I would like to explore how to use Chickenfoot as a scripting framework for Zotero and as a way to explore a working Zotero instance. Why Chickenfoot? Chickenfoot is appealing because it's the closest thing we have to a Web automation framework running within a web browser — and hence something that can take advantage of all the scriptability and context of the Web browser. (Besides, much of the work we do with Chickenfoot will be transferable to writing a Zotero plug-in.)

A goal I've set for myself to focus my learning — and to provide a narrative for the series: create a Chickenfoot script to grab all the references I added on a given date and format those items to be sent out as HTML, wiki formatting, and to be uplaoded to some social bookmarking systems, including delicious and Connotea.

Note: I'm using v 1.0.7 of Zotero, running on Firefox 2.0.0.17 on Windows XP. I'm also using Chickenfoot v 1.0.4. I assume that you will have some basic knowledge of JavaScript and Firefox extensions. (I plan to provide more background later.) The overall approach we take here is a combination of experimenting with bits and pieces of source code, combined with reading the original Zotero source code.

In this first post, we warm up by studying how to access with Chickenfoot Zotero and ZoteroPane, two important Zotero objects, and perform some basic tasks with them.

First, the Zotero object is arguably the main JavaScript object, one that gives you access to the underlying functionality of Zotero. You can access it in this way (see include.js):

var Zotero = Components.classes["@zotero.org/Zotero;1"].
getService(Components.interfaces.nsISupports).wrappedJSObject;

or, in the context of Chickenfoot script:

var Zotero = chromeWindow.Zotero;

(See Rewrite the Web with Chickenfoot [JavaScript & Ajax Tutorials] for a description of the chromeWindow, which is the top-level object of the Firefox browser.)

From a thread on the Zotero forums, I learned an incantation (courtesy of Dan Stillman) to access the ZoteroPane:

var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
var browserWindow = wm.getMostRecentWindow("navigator:browser");
var ZoteroPane = browserWindow.ZoteroPane;

It turns out but you can also use chromeWindow to get at ZoteroPane:

var ZoteroPane = chromeWindow.ZoteroPane;

What can we do with Zotero and ZoteroPane?

Zotero object in Chickenfoot
The first thing to do is to examine the two objects. You can examine them the output panel by toggling the output variable to see the children of the output object. For example, if you run the following code, you can get a list of all the children of Zotero and ZoteroPane:


function list_props(obj) {
var name, props;
var props;
props = "";
for (name in obj) {
props = props + " " + name;
}
return props;
}
var Zotero = chromeWindow.Zotero;
var ZoteroPane = chromeWindow.ZoteroPane;
list_props(Zotero);

to generate the following list:


init stateCheck getProfileDirectory getZoteroDirectory getStorageDirectory getZoteroDatabase chooseZoteroDirectory debug log getErrors getSystemInfo varDump safeDebug getString localeJoin getLocaleCollation setFontSize flattenArguments getAncestorByTagName join inArray arraySearch arrayToHash hasValues randomString getRandomID moveToUnique initialized skipLoading startupError startupErrorHandler Prefs Keys Hash Text Date Browser UnresponsiveScriptIndicator WebProgressFinishListener JSON DBConnection DB Schema Item Items Notes Collection Collections Creators Tags CachedTypes CreatorTypes ItemTypes FileTypes CharacterSets ItemFields getCollections Attachments Notifier History Search Searches SearchConditions Ingester OpenURL Translate Cite CSL QuickCopy Report Timeline Utilities Integration File Fulltext MIME ItemTreeView ItemTreeCommandController CollectionTreeView CollectionTreeCommandController ItemGroup ProgressWindowSet ProgressWindow Annotate Annotations Annotation Highlight version isFx2 isFx3 platform isMac isWin isLinux locale dir Maps IconFactory

Calculating the number of top level items

The following simple Chickenfoot script returns the number of top-level items in my Zotero collection:


var Zotero = chromeWindow.Zotero;
var items = Zotero.Items.getAll(true);
items.length + 1;

Toggle the Zotero Panel

The following two-liner shows how to turn the Zotero display on and off (equivalent to hitting Ctrl-Alt-Z)

var ZoteroPane = chromeWindow.ZoteroPane;
ZoteroPane.toggleDisplay();

printing the title of the first selected item

Finally, a three-liner to print out the title of the first selected item:


var ZoteroPane = chromeWindow.ZoteroPane;
var selectedItems = ZoteroPane.getSelectedItems();
var title = selectedItems[0].getField("title");
title;

Conclusion

This post is meant only to lay the foundation for the ultimate goal which is to integrate Zotero and other systems using Chickenfoot. I hope it also whet your appetite for more — and if you're a Zotero user, that you would go right away to install Chickenfoot to explore Zotero in a deeper way.

|