Add flexible locate menu, based on a (very heavily modified) copy of the pubget patch. Documentation is forthcoming.

This commit is contained in:
Simon Kornblith 2011-02-09 03:22:06 +00:00
parent b6d2898a03
commit 1486132626
15 changed files with 1430 additions and 370 deletions

View file

@ -44,7 +44,6 @@
<field name="clickable">false</field>
<field name="editable">false</field>
<field name="saveOnEdit">false</field>
<field name="displayGoButtons">false</field>
<field name="showTypeMenu">false</field>
<field name="hideEmptyFields">false</field>
<field name="clickByRow">false</field> <!-- Click entire row rather than just field value -->
@ -61,7 +60,6 @@
this.clickable = false;
this.editable = false;
this.saveOnEdit = false;
this.displayGoButtons = false;
this.showTypeMenu = false;
this.hideEmptyFields = false;
this.clickByRow = false;
@ -75,7 +73,6 @@
this.clickable = true;
this.editable = true;
this.saveOnEdit = true
this.displayGoButtons = true;
this.showTypeMenu = true;
this.clickHandler = this.showEditor;
this.blurHandler = this.hideEditor;
@ -90,7 +87,6 @@
this.clickable = true;
this.editable = true;
this.saveOnEdit = false;
this.displayGoButtons = false;
this.showTypeMenu = true;
this.clickHandler = this.showEditor;
this.blurHandler = this.hideEditor;
@ -230,111 +226,6 @@
'document.getBindingParent(this).clickHandler(this)');
}
if (this.displayGoButtons) {
// Enable/disable "View =>" button
testView: try {
var viewButton = document.getElementById('view-button');
viewButton.removeAttribute('viewSnapshot');
viewButton.removeAttribute('viewURL');
viewButton.setAttribute('label',
Zotero.getString('pane.item.goToURL.online.label'));
viewButton.setAttribute('tooltiptext',
Zotero.getString('pane.item.goToURL.online.tooltip'));
var spec = false, validURI = false;
var uri = Components.classes["@mozilla.org/network/standard-url;1"].
createInstance(Components.interfaces.nsIURI);
// First try to find a snapshot matching the item's URL field
var snapID = this.item.getBestAttachment();
if (snapID) {
spec = Zotero.Items.get(snapID).getLocalFileURL();
uri.spec = spec;
if (!uri.scheme || uri.scheme != 'file') {
snapID = false;
spec = false;
}
}
// If that fails, try the URL field itself
if (!spec) {
spec = this.item.getField('url');
uri.spec = spec;
if (!(uri.scheme && (uri.host || uri.scheme == 'file'))) {
spec = false;
}
}
// If that fails, try the DOI field
if (!spec) {
var doi = this.item.getField('DOI');
if (doi && typeof val == 'String') {
// Pull out DOI, in case there's a prefix
doi = Zotero.Utilities.cleanDOI(doi);;
if (doi) {
spec = "http://dx.doi.org/" + encodeURIComponent(doi);
}
}
}
if (!spec) {
break testView;
}
validURI = true;
if (snapID) {
viewButton.setAttribute('label',
Zotero.getString('pane.item.goToURL.snapshot.label'));
viewButton.setAttribute('tooltiptext',
Zotero.getString('pane.item.goToURL.snapshot.tooltip'));
viewButton.setAttribute('viewSnapshot', snapID);
}
else {
viewButton.setAttribute('viewURL', spec);
}
}
catch (e) {
Zotero.debug(e);
}
viewButton.setAttribute('disabled', !validURI);
// Enable/disable "Locate =>" button
switch (this.item.itemTypeID) {
// DEBUG: handle descendents of these types as well?
case Zotero.ItemTypes.getID('book'):
case Zotero.ItemTypes.getID('bookSection'):
case Zotero.ItemTypes.getID('journalArticle'):
case Zotero.ItemTypes.getID('thesis'):
var openURL = true;
break;
default:
var openURL = false;
}
var locateButton = document.getElementById('locate-button');
// TODO: move Locate service logic to separate interface
var wayback = this._itemHasURL();
if (wayback) {
locateButton.setAttribute('type', 'menu');
locateButton.setAttribute('disabled', false);
document.getElementById('locate-service-openurl').disabled = !openURL;
}
else {
locateButton.removeAttribute('type');
locateButton.disabled = !openURL;
}
this._id('go-buttons').hidden = false;
}
else {
this._id('go-buttons').hidden = true;
}
// Item type menu
if (this.showTypeMenu) {
// Build item type menu if it hasn't been built yet
@ -452,7 +343,7 @@
label.setAttribute("isButton", true);
// TODO: make getFieldValue non-private and use below instead
label.setAttribute("onclick", "ZoteroPane.loadURI(this.nextSibling.firstChild ? this.nextSibling.firstChild.nodeValue : this.nextSibling.value, event)");
label.setAttribute("tooltiptext", Zotero.getString('pane.item.goToURL.online.tooltip'));
label.setAttribute("tooltiptext", Zotero.getString('locate.online.tooltip'));
}
else if (fieldName == 'DOI' && val && typeof val == 'string') {
// Pull out DOI, in case there's a prefix
@ -461,7 +352,7 @@
doi = "http://dx.doi.org/" + encodeURIComponent(doi);
label.setAttribute("isButton", true);
label.setAttribute("onclick", "ZoteroPane.loadURI('" + doi + "', event)");
label.setAttribute("tooltiptext", Zotero.getString('pane.item.goToURL.online.tooltip'));
label.setAttribute("tooltiptext", Zotero.getString('locate.online.tooltip'));
}
}
else if (fieldName == 'abstractNote') {
@ -567,16 +458,6 @@
</method>
<method name="_itemHasURL">
<body>
<![CDATA[
var url = this.item.getField('url');
return url && url.match && !url.match(/^file:|^zotero:/);
]]>
</body>
</method>
<method name="buildItemTypeMenu">
<body>
<![CDATA[
@ -1079,56 +960,6 @@
</method>
<method name="onViewClick">
<parameter name="button"/>
<parameter name="event"/>
<body>
<![CDATA[
if (button.getAttribute('viewURL')) {
ZoteroPane.loadURI(button.getAttribute('viewURL'), event);
}
else if (button.getAttribute('viewSnapshot')) {
ZoteroPane.viewAttachment(button.getAttribute('viewSnapshot'), event);
}
]]>
</body>
</method>
<method name="onLocateClick">
<parameter name="event"/>
<body>
<![CDATA[
// Default OpenURL button
if (event.originalTarget.id == 'locate-button') {
var url = Zotero.OpenURL.resolve(this.item);
if (url) {
ZoteroPane.loadURI(url, event);
}
return;
}
// Menu options
switch (event.originalTarget.id) {
case 'locate-service-openurl':
var url = Zotero.OpenURL.resolve(this.item);
if (url) {
ZoteroPane.loadURI(url, event);
}
break;
case 'locate-service-wayback':
var prefix = "http://web.archive.org/web/*/";
var url = this.item.getField('url');
url = prefix + url;
ZoteroPane.loadURI(url, event);
break;
}
]]>
</body>
</method>
<method name="toggleAbstractExpand">
<parameter name="label"/>
<body>
@ -2395,29 +2226,13 @@
</menu>
</menupopup>
</popupset>
<hbox id="go-buttons" align="center" hidden="true">
<button id="view-button"
onfocus="document.getBindingParent(this).ensureElementIsVisible(this)"
oncommand="document.getBindingParent(this).onViewClick(this, event)" disabled="false"/>
<button id="locate-button" label="&zotero.toolbar.openURL.label;"
onfocus="document.getBindingParent(this).ensureElementIsVisible(this)"
type="menu"
oncommand="document.getBindingParent(this).onLocateClick(event)">
<menupopup>
<menuitem id="locate-service-openurl" label="Library Lookup" tooltiptext="&zotero.toolbar.openURL.tooltip;"/>
<menuitem id="locate-service-wayback" label="Wayback Machine"/>
</menupopup>
</button>
</hbox>
<hbox align="center" hidden="true">
</hbox>
<grid flex="1">
<columns>
<column/>
<column flex="1"/>
</columns>
<rows id="dynamic-fields" flex="1">
<row>
<row class="zotero-item-first-row">
<label value="&zotero.items.itemType;:"/>
<menulist class="zotero-clicky" id="item-type-menu" oncommand="document.getBindingParent(this).changeTypeTo(this.value, this)" flex="1"
onfocus="document.getBindingParent(this).ensureElementIsVisible(this)"

View file

@ -0,0 +1,277 @@
<?xml version="1.0"?>
<!--
***** BEGIN LICENSE BLOCK *****
Copyright © 2009 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://zotero.org
This file is part of Zotero.
Zotero is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zotero is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
***** END LICENSE BLOCK *****
-->
<!DOCTYPE prefwindow SYSTEM "chrome://zotero/locale/preferences.dtd">
<?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>
<?xml-stylesheet href="chrome://zotero/skin/preferences.css"?>
<!--
To add a new preference:
1) Add a new <preference> to <preferences>
2) Add a XUL control with a 'preference' attribute
3) (Optional) Add additional setup/change logic to preferences.js
4) (Optional) To add an observer for a preference change,
add an appropriate case in the switch statement
in Zotero.Prefs.observe()
-->
<prefwindow id="zotero-locate-manager-prefs" title="&zotero.preferences.title;" onload="refreshLocateEnginesList()"
windowtype="zotero:pref" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<prefpane id="zotero-prefpane-locate"
label="&zotero.preferences.prefpane.locate;"
image="chrome://zotero/skin/prefs-styles.png">
<!-- TODO: pic for locate pane -->
<!-- TODO: create labels in dtd -->
<preferences>
<preference id="pref-locate-activeEngines" name="extensions.zotero.locate.activeEngines" type="string"/>
</preferences>
<groupbox flex="1">
<caption label="&zotero.preferences.locate.locateEngineManager;"/>
<label id="locateEngineDescription" width="45em" style="font-size: 10px">
&zotero.preferences.locate.locateEnginedescription;
</label>
<separator class="thin"/>
<hbox>
<tree flex="1" id="locateManager" hidecolumnpicker="true" rows="10"
onselect="document.getElementById('locateManager-delete').disabled = undefined"
editable="false">
<treecols>
<treecol type="checkbox" id="locateManager-checkbox" editable="true" flex="0.5"/>
<treecol id="locateManager-name" label="&zotero.preferences.locate.name;" flex="1"/>
<treecol id="locateManager-description" label="&zotero.preferences.locate.description;" flex="2"/>
</treecols>
<treechildren id="locateManager-rows"/>
</tree>
</hbox>
<separator class="thin"/>
<hbox align="center">
<hbox pack="start" flex="1">
<button label="Toggle" onclick="toggleLocateEngines()" flex="1"/>
<button id="locateManager-restoreDefaults" label="&zotero.preferences.locate.restoreDefaults;" onclick="restoreDefaultLocateEngines()" flex="1"/>
</hbox>
<hbox pack="end" flex="2">
<button disabled="true" id="locateManager-delete" label="-" onclick="deleteLocateEngine()" flex="0.5"/>
</hbox>
</hbox>
<separator class="thin"/>
<label id="addLocateEngineDescription" style="font-size: 10px; width: 45em; height: 6em">
&zotero.preferences.locate.addDescription;
</label>
</groupbox>
</prefpane>
<script src="chrome://zotero/content/include.js"></script>
<script type="application/javascript">
<![CDATA[
function treeClick(event) {
// We only care about primary button double and triple clicks
if (!event || (event.detail != 2 && event.detail != 3) || event.button != 0) {
return;
}
var t = event.originalTarget;
if (t.localName != 'treechildren') {
return;
}
var tree = t.parentNode;
var row = {}, col = {}, obj = {};
var cell = tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, obj);
var treechildren = document.getElementById('locateManager-rows');
var treeitem = treechildren.children[row.value];
treeitem.engine.hidden = !treeitem.engine.hidden;
refreshLocateEnginesList()
}
/**
* Refreshes the list of locate engines in the locate pane
* @param {String} name of locate engine to select
*/
function refreshLocateEnginesList() {
var tree = document.getElementById('locateManager');
var treechildren = document.getElementById('locateManager-rows');
// add click listener
tree.addEventListener("click", treeClick, false);
// store ranges
var nRanges = tree.view.selection.getRangeCount();
var start = {};
var end = {};
var ranges = [];
for(var i=0; i<nRanges; i++) {
tree.view.selection.getRangeAt(i, start, end);
ranges.push([start.value, end.value]);
}
// clear tree
while (treechildren.hasChildNodes()) {
treechildren.removeChild(treechildren.firstChild);
}
// repopulate tree with available engines
var engines = Zotero.LocateManager.getEngines();
var i = 0;
for each(var engine in engines) {
var treeitem = document.createElement('treeitem');
var treerow = document.createElement('treerow');
var checkboxCell = document.createElement('treecell');
var nameCell = document.createElement('treecell');
var descriptionCell = document.createElement('treecell');
treeitem.engine = engine;
nameCell.setAttribute('label', engine.name);
descriptionCell.setAttribute('label', engine.description);
if( !engine.hidden ) {
checkboxCell.setAttribute('value', 'true');
}
treerow.appendChild(checkboxCell);
treerow.appendChild(nameCell);
treerow.appendChild(descriptionCell);
treeitem.appendChild(treerow);
treechildren.appendChild(treeitem);
i++;
}
// restore ranges
for each(var range in ranges) {
if(range[1] <= engines.length-1) {
tree.view.selection.rangedSelect(range[0], range[1], true);
}
}
}
/**
* Adds a new Locate Engine to the locate pane
**/
/*
function addLocateEngine() {
// alert(Zotero.LocateManager.activeLocateEngines.join(" || "));
var textbox = document.getElementById('locate-add-textbox');
Zotero.LocateManager.addLocateEngine(textbox.value);
refreshLocateEnginesList();
}
*/
function toggleLocateEngines() {
// get selected engines names
var tree = document.getElementById('locateManager');
var treeItems = tree.lastChild.childNodes;
var engineNames = [];
var start = {};
var end = {};
var nRanges = tree.view.selection.getRangeCount();
var numStatuses = 0;
var engineStatusesSum = 0;
for(var i=0; i<nRanges; i++) {
tree.view.selection.getRangeAt(i, start, end);
for(var j=start.value; j<=end.value; j++) {
var engineStatus = treeItems[j].engine.hidden ? 0 : 1;
numStatuses += 1;
engineStatusesSum += engineStatus;
}
}
var hidden;
switch( engineStatusesSum ) {
case 0:
// all off, turn all on
hidden = false;
break;
case numStatuses:
// all on, turn all off
hidden = true;
break;
default:
// some on, some off. turn all on
hidden = false;
}
[engine.hidden = hidden for each(engine in Zotero.LocateManager.getEngines())];
refreshLocateEnginesList();
}
/**
* Deletes selected Locate Engines from the locate pane
**/
function deleteLocateEngine() {
// get selected engines names
var tree = document.getElementById('locateManager');
var treeItems = tree.lastChild.childNodes;
var engineNames = [];
var start = {};
var end = {};
var nRanges = tree.view.selection.getRangeCount();
for(var i=0; i<nRanges; i++) {
tree.view.selection.getRangeAt(i, start, end);
for(var j=start.value; j<=end.value; j++) {
Zotero.LocateManager.removeEngine(treeItems[j].engine);
}
}
for(var i=0; i<engineNames.length; i++) {
Zotero.LocateManager.removeLocateEngine(engineNames[i]);
}
tree.view.selection.clearSelection();
refreshLocateEnginesList();
}
/**
* Restores Default Locate Engines
**/
function restoreDefaultLocateEngines() {
Zotero.LocateManager.restoreDefaultLocateEngines();
refreshLocateEnginesList();
}
]]>
</script>
</prefwindow>

View file

@ -0,0 +1,317 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright © 2009 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://zotero.org
This file is part of Zotero.
Zotero is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zotero is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
***** END LICENSE BLOCK *****
*/
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
/*
* This object contains the various functions for the interface
*/
var Zotero_LocateMenu = new function() {
XPCOMUtils.defineLazyServiceGetter(this, "ios", "@mozilla.org/network/io-service;1", "nsIIOService");
/**
* Create a new menuitem XUL element
*/
function _createMenuItem( label, id, tooltiptext ) {
var menuitem = document.createElement("menuitem");
menuitem.setAttribute("label", label);
if(id) menuitem.setAttribute("id", id);
if(tooltiptext) menuitem.setAttribute("tooltiptext", tooltiptext);
return menuitem;
}
/**
* Get snapshot IDs for items
*/
function _getSnapshotIDs(items) {
var ids = [];
for each(var item in items) {
if(item.isNote()) continue;
var snapID = (item.isAttachment() ? item.id : item.getBestAttachment());
if(!snapID) continue;
var spec = Zotero.Items.get(snapID).getLocalFileURL();
if(!spec) continue;
var uri = Zotero_LocateMenu.ios.newURI(spec, null, null);
if(!uri || uri.scheme != 'file') continue;
ids.push(snapID);
}
return ids;
}
/**
* Get URLs for items
*/
function _getURLs(items, includeDOIs) {
var urls = [];
for each(var item in items) {
if(item.isNote()) continue;
var url = null;
// try url field
var urlField = item.getField('url');
if(urlField) {
var uri = Zotero_LocateMenu.ios.newURI(urlField, null, null);
if(uri && (uri.host || !uri.scheme !== 'file')) {
url = urlField;
}
}
if(includeDOIs) {
if(!url) {
// try DOI field
var doi = item.getField('DOI');
if(doi && typeof doi === "string") {
doi = Zotero.Utilities.cleanDOI(doi);
if(doi) url = "http://dx.doi.org/" + encodeURIComponent(doi);
}
}
}
if(url) urls.push(url);
}
return urls;
}
/**
* Get any locate engines that can be installed from the current page
*/
function _getInstallableLocateEngines() {
var locateEngines = [];
if(!Zotero_Browser) return locateEngines;
var links = Zotero_Browser.tabbrowser.selectedBrowser.contentDocument.getElementsByTagName("link");
for each(var link in links) {
if(!link.getAttribute) continue;
var rel = link.getAttribute("rel");
if(rel && rel === "search") {
var type = link.getAttribute("type");
if(type && type === "application/x-openurl-opensearchdescription+xml") {
var label = link.getAttribute("title");
if(label) {
if(Zotero.LocateManager.getEngineByName(label)) {
label = 'Update "'+label+'"';
} else {
label = 'Add "'+label+'"';
}
} else {
label = 'Add Locate Engine';
}
locateEngines.push({'label':label,
'href':link.getAttribute("href"),
'image':Zotero_Browser.tabbrowser.selectedTab.image});
}
}
}
return locateEngines;
}
/**
* Clear and build the locate menu
*/
this.buildLocateMenu = function() {
var locateMenu = document.getElementById('zotero-tb-locate-menu');
// clear menu
while(locateMenu.childElementCount > 0) {
locateMenu.removeChild(locateMenu.firstChild);
}
var selectedItems = [item for each(item in ZoteroPane.getSelectedItems()) if(!item.isNote())];
if(selectedItems.length) {
// get snapshot IDs and URLs
var allURLs = _getURLs(selectedItems, true);
var realURLs = _getURLs(selectedItems);
if(selectedItems.length == 1 && _getSnapshotIDs(selectedItems).length) {
// add view snapshot
var menuitem = _createMenuItem(Zotero.getString("locate.snapshot.label"),
"zotero-locate-snapshot", Zotero.getString("locate.snapshot.tooltip"));
locateMenu.appendChild(menuitem);
menuitem.addEventListener("command", this.openItemSnapshot, false);
}
if(allURLs.length) {
// add view online
var menuitem = _createMenuItem(Zotero.getString("locate.online.label"),
"zotero-locate-online", Zotero.getString("locate.online.tooltip"));
locateMenu.appendChild(menuitem);
menuitem.addEventListener("command", this.openItemURL, false);
}
// add library lookup to any item
var menuitem = _createMenuItem(Zotero.getString("locate.libraryLookup.label"),
"zotero-locate-service-openurl", Zotero.getString("locate.libraryLookup.tooltip"));
locateMenu.appendChild(menuitem);
menuitem.addEventListener("command", this.lookupItem, false);
// add wayback if there are real URLs
if(realURLs.length) {
var menuitem = _createMenuItem(Zotero.getString("locate.waybackMachine.label"),
"zotero-locate-service-wayback", Zotero.getString("locate.waybackMachine.tooltip"));
locateMenu.appendChild(menuitem);
menuitem.addEventListener("command", this.waybackItem, false);
}
var customEngines = Zotero.LocateManager.getVisibleEngines();
if(customEngines.length) {
locateMenu.appendChild(document.createElement("menuseparator"));
// add engines to menu
for each(var engine in customEngines) {
// require a submission for at least one selected item
var canSubmit = false;
for each(var item in selectedItems) {
if(engine.getItemSubmission(item)) {
canSubmit = true;
break;
}
}
if(canSubmit) {
var menuitem = _createMenuItem(engine.name, null, engine.description);
menuitem.setAttribute("class", "menuitem-iconic");
menuitem.setAttribute("image", engine.icon);
locateMenu.appendChild(menuitem);
menuitem.addEventListener("command", this.locateItem, false);
}
}
}
} else {
// add "no items selected"
menuitem = _createMenuItem(Zotero.getString("pane.item.selected.zero"), "no-items-selected");
locateMenu.appendChild(menuitem);
menuitem.disabled = true;
}
// add installable locate menus, if there are any
if(window.Zotero_Browser) {
var installableLocateEngines = _getInstallableLocateEngines();
} else {
var installableLocateEngines = [];
}
if(installableLocateEngines.length) {
locateMenu.appendChild(document.createElement("menuseparator"));
for each(var locateEngine in installableLocateEngines) {
var menuitem = document.createElement("menuitem");
menuitem.setAttribute("label", locateEngine.label);
menuitem.setAttribute("image", locateEngine.image);
menuitem.zoteroLocateInfo = locateEngine;
menuitem.addEventListener("command", this.addLocateEngine, false);
locateMenu.appendChild(menuitem);
}
}
// add manage menu item
locateMenu.appendChild(document.createElement("menuseparator"));
var menuitem = document.createElement("menuitem");
menuitem = _createMenuItem(Zotero.getString("locate.manageLocateEngines"), "zotero-manage-locate-menu");
menuitem.addEventListener("command", this.openLocateEngineManager, false);
locateMenu.appendChild(menuitem);
}
/**
* Open snapshots for selected items
*/
this.openItemSnapshot = function(event) {
ZoteroPane.viewAttachment(_getSnapshotIDs(ZoteroPane.getSelectedItems())[0], event);
}
/**
* Open URLs for selected items
*/
this.openItemURL = function(event) {
ZoteroPane.loadURI(_getURLs(ZoteroPane.getSelectedItems(), true), event);
}
/**
* Perform library lookup of selected items
*/
this.lookupItem = function(event) {
var urls = [];
for each(var item in ZoteroPane.getSelectedItems()) {
var url = Zotero.OpenURL.resolve(item);
if(url) urls.push(url);
}
ZoteroPane.loadURI(urls, event);
}
/**
* Perform library lookup of selected items
*/
this.waybackItem = function(event) {
ZoteroPane.loadURI(["http://web.archive.org/web/*/"+url
for each(url in _getURLs(ZoteroPane.getSelectedItems()))], event);
}
/**
* Locate selected items
*/
this.locateItem = function(event) {
var selectedItems = ZoteroPane.getSelectedItems();
// find selected engine
var selectedEngine = Zotero.LocateManager.getEngineByName(event.target.label);
if(!selectedEngine) throw "Selected locate engine not found";
var urls = [];
var postDatas = [];
for each(var item in selectedItems) {
var submission = selectedEngine.getItemSubmission(item);
urls.push(submission.uri.spec);
postDatas.push(submission.postData);
}
Zotero.debug("Loading using "+selectedEngine.name);
Zotero.debug(urls);
ZoteroPane.loadURI(urls, event, postDatas);
}
/**
* Add a new locate engine
*/
this.addLocateEngine = function(event) {
Zotero.LocateManager.addEngine(event.target.zoteroLocateInfo.href,
Components.interfaces.nsISearchEngine.TYPE_OPENSEARCH,
event.target.zoteroLocateInfo.image, false);
}
/**
* Open the locate manager
*/
this.openLocateEngineManager = function(event) {
window.openDialog('chrome://zotero/content/locateManager.xul',
'Zotero Locate Engine Manager',
'chrome,centerscreen'
);
}
}

View file

@ -0,0 +1,578 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright © 2009 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://zotero.org
This file is part of Zotero.
Zotero is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zotero is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
***** END LICENSE BLOCK *****
*/
Zotero.LocateManager = new function() {
const LOCATE_FILE_NAME = "engines.json";
const LOCATE_DIR_NAME = "locate";
var _jsonFile;
var _locateEngines;
var _ios;
var _timer;
/**
* Read locateEngines JSON file to initialize locate manager
*/
this.init = function() {
_ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
_jsonFile = _getLocateFile();
if(_jsonFile.exists()) {
_locateEngines = [new LocateEngine(engine)
for each(engine in JSON.parse(Zotero.File.getContents(_jsonFile)))];
} else {
this.restoreDefaultEngines();
}
}
/**
* Adds a new search engine
* confirm parameter is currently ignored
*/
this.addEngine = function(engineURL, dataType, iconURL, confirm) {
if(dataType !== Components.interfaces.nsISearchEngine.TYPE_OPENSEARCH) {
throw "LocateManager supports only OpenSearch engines";
}
Zotero.HTTP.doGet(engineURL, function(xmlhttp) {
var engine = new LocateEngine();
engine.initWithXML(xmlhttp.responseText, iconURL);
});
}
/**
* Gets all default search engines (not currently used)
*/
this.getDefaultEngines = function() [new LocateEngine(engine)
for each(engine in JSON.parse(Zotero.File.getContents(_getDefaultFile())))];
/**
* Returns an array of all search engines
*/
this.getEngines = function() _locateEngines.slice(0);
/**
* Returns an array of all search engines visible that should be visible in the dropdown
*/
this.getVisibleEngines = function() [engine for each(engine in _locateEngines) if(!engine.hidden)];
/**
* Returns an engine with a specific name
*/
this.getEngineByName = function(engineName) {
engineName = engineName.toLowerCase();
for each(var engine in _locateEngines) if(engine.name.toLowerCase() == engineName) return engine;
return null;
}
/**
* Returns the first engine with a specific alias
*/
this.getEngineByAlias = function(engineAlias) {
engineAlias = engineAlias.toLowerCase();
for each(var engine in _locateEngines) if(engine.alias.toLowerCase() == engineAlias) return engine;
return null;
}
/**
* Moves an engine in the list
*/
this.moveEngine = function(engine, newIndex) {
this.removeEngine(engine);
_locateEngines.splice(newIndex, engine);
}
/**
* Removes an engine from the list
*/
this.removeEngine = function(engine) {
var oldIndex = _locateEngines.indexOf(engine);
if(oldIndex === -1) throw "Engine is not currently listed";
_locateEngines.splice(oldIndex, 1);
engine._removeIcon();
_serializeLocateEngines();
}
/**
* Restore default engines by copying file from extension dir
*/
this.restoreDefaultEngines = function() {
// get locate dir
var locateDir = _getLocateDirectory();
// remove old locate dir
if(locateDir.exists()) locateDir.remove(true);
// create new locate dir
locateDir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0700);
// copy default file to new locate dir
_getDefaultFile().copyTo(locateDir, LOCATE_FILE_NAME);
// reread locate engines
this.init();
// reload icons for default locate engines
for each(var engine in this.getEngines()) engine._updateIcon();
}
/**
* Writes the engines to disk; called from the nsITimer spawned by _serializeLocateEngines
*/
this.notify = function() {
Zotero.File.putContents(_jsonFile, JSON.stringify(_locateEngines, null, "\t"));
_timer = undefined;
}
/**
* Gets the JSON file containing engine info
*/
function _getLocateFile() {
var locateDir = _getLocateDirectory();
locateDir.append(LOCATE_FILE_NAME);
return locateDir;
}
/**
* Gets the dir containing the JSON file and engine icons
*/
function _getLocateDirectory() {
var locateDir = Zotero.getZoteroDirectory();
locateDir.append(LOCATE_DIR_NAME);
return locateDir;
}
/**
* Gets the JSON file containing the engine info for the default engines
*/
function _getDefaultFile() {
var defaultFile = Zotero.getInstallDirectory();
defaultFile.append(LOCATE_FILE_NAME);
return defaultFile;
}
/**
* Writes the engines to disk when the current block is finished executing
*/
function _serializeLocateEngines() {
if(_timer) return;
_timer = Components.classes["@mozilla.org/timer;1"].
createInstance(Components.interfaces.nsITimer);
_timer.initWithCallback(Zotero.LocateManager, 0, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
}
/**
* Function to call to attach to watch engine properties and perform deferred serialization
*/
function _watchLocateEngineProperties(id, oldval, newval) {
if(oldval !== newval) _serializeLocateEngines();
return newval;
}
/**
* Called when an engine icon is downloaded to write it to disk
*/
function _engineIconLoaded(iconBytes, engine, contentType) {
const iconExtensions = {
"image/png":"png",
"image/jpeg":"jpg",
"image/gif":"gif",
"image/x-icon":"ico"
};
// ensure there is an icon
if(!iconBytes) throw "Icon could not be retrieved for "+engine.name;
// ensure there is an extension
var extension = iconExtensions[contentType.toLowerCase()];
if(!extension) throw "Invalid MIME type "+contentType+" for icon for engine "+engine.name;
// remove old icon
engine._removeIcon();
// find a good place to put the icon file
var sanitizedAlias = engine.name.replace(/[^\w _]/g, "");
var iconFile = _getLocateDirectory();
iconFile.append(sanitizedAlias + "." + extension);
if(iconFile.exists()) {
for(var i=0; iconFile.exists(); i++) {
iconFile = iconFile.parent;
iconFile.append(sanitizedAlias + "_" + i + "." + extension);
}
}
// write the icon to the file
var fos = Components.classes["@mozilla.org/network/file-output-stream;1"].
createInstance(Components.interfaces.nsIFileOutputStream);
fos.init(iconFile, 0x02 | 0x08 | 0x20, 0664, 0); // write, create, truncate
var bos = Components.classes["@mozilla.org/binaryoutputstream;1"].
createInstance(Components.interfaces.nsIBinaryOutputStream);
bos.setOutputStream(fos);
bos.writeByteArray(iconBytes, iconBytes.length);
bos.close();
// get the URI of the icon
engine.icon = _ios.newFileURI(iconFile).spec;
}
/**
* Looks up a parameter in our list
*
* Supported parameters include
* - all standard OpenURL parameters, identified by any OpenURL namespace
* - "version", "identifier", and "format" identified by the OpenURL ctx namespace
* - "openURL" identified by the Zotero namespace (= the whole openURL)
* - "year" identified by the Zotero namespace
* - any Zotero field identified by the Zotero namespace
*/
function _lookupParam(item, itemOpenURL, engine, nsPrefix, param) {
const OPENURL_ITEM_PREFIXES = [
"info:ofi/fmt:kev:mtx:journal",
"info:ofi/fmt:kev:mtx:book",
"info:ofi/fmt:kev:mtx:patent",
"info:ofi/fmt:kev:mtx:sch_svc",
"info:ofi/fmt:kev:mtx:dissertation"
];
const OPENURL_CONTEXT_MAPPINGS = {
"version":"ctx_ver",
"identifier":"rfr_id",
"format":"rft_val_fmt"
};
if(nsPrefix) {
var ns = engine._urlNamespaces[nsPrefix];
if(!ns) return false;
} else {
if(param === "searchTerms") return [item.getField("title")];
return false;
}
if(OPENURL_ITEM_PREFIXES.indexOf(ns) !== -1) {
// take a normal "title," even though we don't use it, because it is valid (but not
// preferred) OpenURL
if(param === "title") {
var title = item.getField("title");
return (title ? [encodeURIComponent(title)] : false);
}
if(!itemOpenURL["rft."+param]) {
return false;
}
return [encodeURIComponent(val) for each(val in itemOpenURL["rft."+param])];
} else if(ns === "info:ofi/fmt:kev:mtx:ctx") {
if(!OPENURL_CONTEXT_MAPPINGS[param] || !itemOpenURL[OPENURL_CONTEXT_MAPPINGS[param]]) {
return false;
}
return [encodeURIComponent(val) for each(val in itemOpenURL[OPENURL_CONTEXT_MAPPINGS[param]])];
} else if(ns === "http://www.zotero.org/namespaces/openSearch#") {
if(param === "openURL") {
return [Zotero.OpenURL.createContextObject(item, "1.0")];
} else if(param === "year") {
return (itemOpenURL["rft.date"] ? [itemOpenURL["rft.date"][0].substr(0, 4)] : false);
} else {
var result = item.getField(param);
return (result ? [encodeURIComponent(result)] : false);
}
} else {
return false;
}
}
/**
* Theoretically implements nsISearchSubmission
*/
var LocateSubmission = function(uri, postData) {
this.uri = _ios.newURI(uri, null, null);
this.postData = postData;
}
/**
* @constructor
* Constructs a new LocateEngine
* @param {Object} [obj] The locate engine, in parsed form, as it was serialized to JSON
*/
var LocateEngine = function(obj) {
this.alias = this.name = "Untitled";
this.description = this._urlTemplate = this.icon = null;
this.hidden = false;
this._urlParams = [];
if(obj) for(var prop in obj) this[prop] = obj[prop];
// Queue deferred serialization whenever a property is modified
for each(var prop in ["alias", "name", "description", "icon", "hidden"]) {
this.watch(prop, _watchLocateEngineProperties);
}
}
LocateEngine.prototype = {
/**
* Initializes an engine with a string and an iconURL to use if none is defined in the file
*/
"initWithXML":function(xmlStr, iconURL) {
const OPENSEARCH_NAMESPACES = [
// These are the official namespaces
"http://a9.com/-/spec/opensearch/1.1/",
"http://a9.com/-/spec/opensearch/1.0/",
// These were also in nsSearchService.js
"http://a9.com/-/spec/opensearchdescription/1.1/",
"http://a9.com/-/spec/opensearchdescription/1.0/"
];
var xml = Zotero.Styles.cleanXML(xmlStr);
if(OPENSEARCH_NAMESPACES.indexOf(xml.namespace()) === "-1") {
throw "Invalid namespace";
}
default xml namespace = xml.namespace();
// get simple attributes
this.alias = xml.ShortName.toString();
this.name = xml.LongName.toString();
if(!this.name) this.name = this.alias;
this.description = xml.Description.toString();
// get the URL template
this._urlTemplate = undefined;
for each(var urlTag in xml.Url.(@type.toLowerCase() == "text/html")) {
if(urlTag.@rel == undefined || urlTag.@rel == "results") {
this._urlTemplate = urlTag.@template.toString();
break;
}
this._method = urlTag.@method.toUpperCase() === "POST" ? "POST" : "GET";
}
// TODO: better error handling
if(!this._urlTemplate) throw "No URL found for required content type";
// get namespaces
this._urlNamespaces = {};
for each(var ns in urlTag.inScopeNamespaces()) {
this._urlNamespaces[ns.prefix] = ns.uri;
}
// get params
this._urlParams = [];
for each(var param in urlTag.Param) {
this._urlParams[param.@name.toString()] = param.@value.toString();
}
// find the icon
this._iconSourceURI = iconURL;
for each(var img in xml.Image) {
if((img.@width == undefined && img.@height == undefined)
|| (img.@width.toString() == "16" && img.@height.toString() == "16")) {
this._iconSourceURI = img.toString();
}
}
if(this._iconSourceURI) {
// begin fetching the icon if necesssary
this._updateIcon();
}
// delete any old engine with the same name
var engine = Zotero.LocateManager.getEngineByName(this.name);
if(engine) Zotero.LocateManager.removeEngine(engine);
// add and serialize the new engine
_locateEngines.push(this);
_serializeLocateEngines();
},
"getItemSubmission":function(item, responseType) {
if(responseType && responseType !== "text/html") {
throw "LocateManager supports only responseType text/html";
}
var itemAsOpenURL = Zotero.OpenURL.createContextObject(item, "1.0", true);
// do substitutions
var me = this;
var abort = false;
var url = this._urlTemplate.replace(/{(?:([^}:]+):)?([^}:?]+)(\?)?}/g, function(all, nsPrefix, param, required) {
var result = _lookupParam(item, itemAsOpenURL, me, nsPrefix, param, required);
if(result) {
return result[0];
} else {
if(required) { // if no param and it wasn't optional, return
return "";
} else {
abort = true;
}
}
});
if(abort) return null;
// handle params
var paramsToAdd = [];
for(var param in this._urlParams) {
var m = this._urlParams[param].match(/^{(?:([^}:]+):)?([^}:?]+)(\?)?}$/);
if(!m) {
paramsToAdd.push(encodeURIComponent(param)+"="+encodeURIComponent(this._urlParams[param]));
} else {
var result = _lookupParam(item, itemAsOpenURL, me, m[1], m[2]);
if(result) {
paramsToAdd = paramsToAdd.concat([encodeURIComponent(param)+"="+encodeURIComponent(val) for(val in result)]);
} else if(m[3]) { // if no param and it wasn't optional, return
return null;
}
}
}
// attach params
if(paramsToAdd.length) {
if(this._method === "POST") {
var postData = paramsToAdd.join("&");
} else {
var postData = null;
if(url.indexOf("?") === -1) {
url += "?"+paramsToAdd.join("&");
} else {
url += "&"+paramsToAdd.join("&");
}
}
}
return new LocateSubmission(url, postData);
},
"_removeIcon":function() {
if(!this.icon) return;
var uri = _ios.newURI(this.icon, null, null);
var file = uri.QueryInterface(Components.interfaces.nsIFileURL).file;
if(file.exists()) file.remove(null);
},
"_updateIcon":function() {
// create new channel
var uri = _ios.newURI(this._iconSourceURI, null, null);
if(uri.scheme !== "http" && uri.scheme !== "https" && uri.scheme !== "ftp") return;
var chan = _ios.newChannelFromURI(uri);
var listener = new loadListener(chan, this, _engineIconLoaded);
chan.notificationCallbacks = listener;
chan.asyncOpen(listener, null);
}
}
/**
* Ripped from nsSearchService.js
*/
function loadListener(aChannel, aEngine, aCallback) {
this._channel = aChannel;
this._bytes = [];
this._engine = aEngine;
this._callback = aCallback;
}
loadListener.prototype = {
_callback: null,
_channel: null,
_countRead: 0,
_engine: null,
_stream: null,
QueryInterface: function SRCH_loadQI(aIID) {
if (aIID.equals(Ci.nsISupports) ||
aIID.equals(Ci.nsIRequestObserver) ||
aIID.equals(Ci.nsIStreamListener) ||
aIID.equals(Ci.nsIChannelEventSink) ||
aIID.equals(Ci.nsIInterfaceRequestor) ||
aIID.equals(Ci.nsIBadCertListener2) ||
aIID.equals(Ci.nsISSLErrorListener) ||
// See FIXME comment below
aIID.equals(Ci.nsIHttpEventSink) ||
aIID.equals(Ci.nsIProgressEventSink) ||
false)
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
// nsIRequestObserver
onStartRequest: function SRCH_loadStartR(aRequest, aContext) {
this._stream = Cc["@mozilla.org/binaryinputstream;1"].
createInstance(Ci.nsIBinaryInputStream);
},
onStopRequest: function SRCH_loadStopR(aRequest, aContext, aStatusCode) {
var requestFailed = !Components.isSuccessCode(aStatusCode);
if (!requestFailed && (aRequest instanceof Ci.nsIHttpChannel))
requestFailed = !aRequest.requestSucceeded;
if (requestFailed || this._countRead == 0) {
// send null so the callback can deal with the failure
this._callback(null, this._engine, this._channel.contentType);
} else
this._callback(this._bytes, this._engine, this._channel.contentType);
this._channel = null;
this._engine = null;
},
// nsIStreamListener
onDataAvailable: function SRCH_loadDAvailable(aRequest, aContext,
aInputStream, aOffset,
aCount) {
this._stream.setInputStream(aInputStream);
// Get a byte array of the data
this._bytes = this._bytes.concat(this._stream.readByteArray(aCount));
this._countRead += aCount;
},
// nsIChannelEventSink
onChannelRedirect: function SRCH_loadCRedirect(aOldChannel, aNewChannel,
aFlags) {
this._channel = aNewChannel;
},
// nsIInterfaceRequestor
getInterface: function SRCH_load_GI(aIID) {
return this.QueryInterface(aIID);
},
// nsIBadCertListener2
notifyCertProblem: function SRCH_certProblem(socketInfo, status, targetSite) {
return true;
},
// nsISSLErrorListener
notifySSLError: function SRCH_SSLError(socketInfo, error, targetSite) {
return true;
},
// FIXME: bug 253127
// nsIHttpEventSink
onRedirect: function (aChannel, aNewChannel) {},
// nsIProgressEventSink
onProgress: function (aRequest, aContext, aProgress, aProgressMax) {},
onStatus: function (aRequest, aContext, aStatus, aStatusArg) {}
}
}

View file

@ -89,89 +89,95 @@ Zotero.OpenURL = new function() {
/*
* Generates an OpenURL ContextObject from an item
*/
function createContextObject(item, version) {
function createContextObject(item, version, asObj) {
var entries = (asObj ? {} : []);
function _mapTag(data, tag, dontAddPrefix) {
if(!data) return;
if(version === "1.0" && !dontAddPrefix) tag = "rft."+tag;
if(asObj) {
if(!entries[tag]) entries[tag] = [];
entries[tag].push(data);
} else {
entries.push(tag+"="+encodeURIComponent(data));
}
}
if(item.toArray) {
item = item.toArray();
}
var identifiers = new Array();
if(item.DOI) {
identifiers.push("info:doi/"+item.DOI);
}
if(item.ISBN) {
identifiers.push("urn:isbn:"+item.ISBN);
}
// find pmid
const pmidRe = /(?:\n|^)PMID:\s*(\d+)/g;
var pmid = pmidRe.exec(item.extra);
if(pmid) pmid = pmid[1];
// encode ctx_ver (if available) and identifiers
// TODO identifiers may need to be encoded as follows:
// rft_id=info:doi/<the-url-encoded-doi>
// rft_id=http://<the-rest-of-the-url-encoded-url>
// encode ctx_ver (if available) and encode identifiers
if(version == "0.1") {
var co = "sid=Zotero:"+encodeURIComponent(Zotero.version);
for(var i=0; i<identifiers.length; i++) {
co += "&id="+encodeURIComponent(identifiers[i]);
}
_mapTag("Zotero:2", "sid", true);
if(item.DOI) _mapTag("doi:"+item.DOI, "id", true);
if(item.ISBN) _mapTag(item.ISBN, "isbn", true);
if(pmid) _mapTag("pmid:"+pmid, "id", true);
} else {
var co = "url_ver=Z39.88-2004&ctx_ver=Z39.88-2004"+
"&rfr_id="+encodeURIComponent("info:sid/zotero.org:"+Zotero.version);
for(var i=0; i<identifiers.length; i++) {
co += "&rft_id="+encodeURIComponent(identifiers[i])
}
_mapTag("Z39.88-2004", "url_ver", true);
_mapTag("Z39.88-2004", "ctx_ver", true);
_mapTag("info:sid/zotero.org:2", "rfr_id", true);
if(item.DOI) _mapTag("info:doi/"+item.DOI, "rft_id", true);
if(item.ISBN) _mapTag("urn:isbn:"+item.ISBN, "rft_id", true);
if(pmid) _mapTag("info:pmid/"+pmid, "rft_id", true);
}
// encode genre and item-specific data
if(item.itemType == "journalArticle") {
if(version == "0.1") {
co += "&genre=article";
} else {
co += "&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&rft.genre=article";
if(version === "1.0") {
_mapTag("info:ofi/fmt:kev:mtx:journal", "rft_val_fmt", true);
}
if(item.title) co += _mapTag(item.title, "atitle", version)
if(item.publicationTitle) co += _mapTag(item.publicationTitle, (version == "0.1" ? "title" : "jtitle"), version)
if(item.journalAbbreviation) co += _mapTag(item.journalAbbreviation, "stitle", version);
if(item.volume) co += _mapTag(item.volume, "volume", version);
if(item.issue) co += _mapTag(item.issue, "issue", version);
_mapTag("article", "genre");
if(item.title) _mapTag(item.title, "atitle")
if(item.publicationTitle) _mapTag(item.publicationTitle, (version == "0.1" ? "title" : "jtitle"))
if(item.journalAbbreviation) _mapTag(item.journalAbbreviation, "stitle");
if(item.volume) _mapTag(item.volume, "volume");
if(item.issue) _mapTag(item.issue, "issue");
} else if(item.itemType == "book" || item.itemType == "bookSection" || item.itemType == "conferencePaper") {
if(version == "0.1") {
co += "&genre=book";
} else {
co += "&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook";
if(version === "1.0") {
_mapTag("info:ofi/fmt:kev:mtx:book", "rft_val_fmt", true);
}
if(item.itemType == "book") {
co += "&rft.genre=book";
if(item.title) co += _mapTag(item.title, (version == "0.1" ? "title" : "btitle"), version);
_mapTag("book", "genre");
if(item.title) _mapTag(item.title, (version == "0.1" ? "title" : "btitle"));
} else if (item.itemType == "conferencePaper") {
co += "&rft.genre=proceeding";
if(item.title) co += _mapTag(item.title, "atitle", version)
if(item.proceedingsTitle) co += _mapTag(item.proceedingsTitle, (version == "0.1" ? "title" : "btitle"), version);
_mapTag("proceeding", "genre");
if(item.title) _mapTag(item.title, "atitle")
if(item.proceedingsTitle) _mapTag(item.proceedingsTitle, (version == "0.1" ? "title" : "btitle"));
} else {
co += "&rft.genre=bookitem";
if(item.title) co += _mapTag(item.title, "atitle", version)
if(item.publicationTitle) co += _mapTag(item.publicationTitle, (version == "0.1" ? "title" : "btitle"), version);
_mapTag("bookitem", "genre");
if(item.title) _mapTag(item.title, "atitle")
if(item.publicationTitle) _mapTag(item.publicationTitle, (version == "0.1" ? "title" : "btitle"));
}
if(item.place) co += _mapTag(item.place, "place", version);
if(item.publisher) co += _mapTag(item.publisher, "publisher", version)
if(item.edition) co += _mapTag(item.edition, "edition", version);
if(item.series) co += _mapTag(item.series, "series", version);
if(item.place) _mapTag(item.place, "place");
if(item.publisher) _mapTag(item.publisher, "publisher")
if(item.edition) _mapTag(item.edition, "edition");
if(item.series) _mapTag(item.series, "series");
} else if(item.itemType == "thesis" && version == "1.0") {
co += "&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Adissertation";
_mapTag("info:ofi/fmt:kev:mtx:dissertation", "rft_val_fmt", true);
if(item.title) co += _mapTag(item.title, "title", version);
if(item.publisher) co += _mapTag(item.publisher, "inst", version);
if(item.type) co += _mapTag(item.type, "degree", version);
if(item.title) _mapTag(item.title, "title");
if(item.publisher) _mapTag(item.publisher, "inst");
if(item.type) _mapTag(item.type, "degree");
} else if(item.itemType == "patent" && version == "1.0") {
co += "&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Apatent";
_mapTag("info:ofi/fmt:kev:mtx:patent", "rft_val_fmt", true);
if(item.title) co += _mapTag(item.title, "title", version);
if(item.assignee) co += _mapTag(item.assignee, "assignee", version);
if(item.patentNumber) co += _mapTag(item.patentNumber, "number", version);
if(item.title) _mapTag(item.title, "title");
if(item.assignee) _mapTag(item.assignee, "assignee");
if(item.patentNumber) _mapTag(item.patentNumber, "number");
if(item.issueDate) {
co += _mapTag(Zotero.Date.strToISO(item.issueDate), "date", version);
_mapTag(Zotero.Date.strToISO(item.issueDate), "date");
}
} else {
return false;
@ -181,32 +187,41 @@ Zotero.OpenURL = new function() {
// encode first author as first and last
var firstCreator = item.creators[0];
if(item.itemType == "patent") {
co += _mapTag(firstCreator.firstName, "invfirst", version);
co += _mapTag(firstCreator.lastName, "invlast", version);
_mapTag(firstCreator.firstName, "invfirst");
_mapTag(firstCreator.lastName, "invlast");
} else {
if(firstCreator.isInstitution) {
co += _mapTag(firstCreator.lastName, "aucorp", version);
_mapTag(firstCreator.lastName, "aucorp");
} else {
co += _mapTag(firstCreator.firstName, "aufirst", version);
co += _mapTag(firstCreator.lastName, "aulast", version);
_mapTag(firstCreator.firstName, "aufirst");
_mapTag(firstCreator.lastName, "aulast");
}
}
// encode subsequent creators as au
for(var i=0; i<identifiers.length; i++) {
co += _mapTag((creators[i].firstName ? creators[i].firstName+" " : "")+creators[i].lastName, (item.itemType == "patent" ? "inventor" : "au"), version);
for(var i=0; i<item.creators.length; i++) {
_mapTag((item.creators[i].firstName ? item.creators[i].firstName+" " : "")+
item.creators[i].lastName, (item.itemType == "patent" ? "inventor" : "au"));
}
}
if(item.date) {
co += _mapTag(Zotero.Date.strToISO(item.date), (item.itemType == "patent" ? "appldate" : "date"), version);
_mapTag(Zotero.Date.strToISO(item.date), (item.itemType == "patent" ? "appldate" : "date"));
}
if(item.pages) co += _mapTag(item.pages, "pages", version);
if(item.numPages) co += _mapTag(item.numPages, "tpages", version);
if(item.ISBN) co += _mapTag(item.ISBN, "isbn", version);
if(item.ISSN) co += _mapTag(item.ISSN, "issn", version);
if(item.pages) {
_mapTag(item.pages, "pages");
var pages = item.pages.split("-");
if(pages.length >= 1) {
_mapTag(pages[0], "spage");
if(pages.length >= 2) _mapTag(pages[1], "epage");
}
}
if(item.numPages) _mapTag(item.numPages, "tpages");
if(item.ISBN) _mapTag(item.ISBN, "isbn");
if(item.ISSN) _mapTag(item.ISSN, "issn");
return co;
if(asObj) return entries;
return entries.join("&");
}
/*
@ -445,19 +460,4 @@ Zotero.OpenURL = new function() {
return item;
}
/*
* Used to map tags for generating OpenURL contextObjects
*/
function _mapTag(data, tag, version) {
if(data) {
if(version == "0.1") {
return "&"+tag+"="+encodeURIComponent(data);
} else {
return "&rft."+tag+"="+encodeURIComponent(data);
}
} else {
return "";
}
}
}

View file

@ -601,6 +601,9 @@ var Zotero = new function(){
// Initialize keyboard shortcuts
Zotero.Keys.init();
// Initialize Locate Manager
Zotero.LocateManager.init();
this.initialized = true;
Zotero.debug("Initialized in "+((new Date()).getTime() - start)+" ms");

View file

@ -2416,51 +2416,57 @@ var ZoteroPane = new function()
* (e.g. meta-click == new background tab, meta-shift-click == new front tab,
* shift-click == new window, no modifier == frontmost tab
*/
function loadURI(uri, event, data) {
// Ignore javascript: and data: URIs
if (uri.match(/^(javascript|data):/)) {
return;
function loadURI(uris, event, data) {
if(typeof uris === "string") {
uris = [uris];
}
if (Zotero.isStandalone && uri.match(/^https?/)) {
var io = Components.classes['@mozilla.org/network/io-service;1']
.getService(Components.interfaces.nsIIOService);
var uri = io.newURI(uri, null, null);
var handler = Components.classes['@mozilla.org/uriloader/external-protocol-service;1']
.getService(Components.interfaces.nsIExternalProtocolService)
.getProtocolHandlerInfo('http');
handler.preferredAction = Components.interfaces.nsIHandlerInfo.useSystemDefault;
handler.launchWithURI(uri, null);
return;
}
// Open in new tab
var openInNewTab = event && (event.metaKey || (!Zotero.isMac && event.ctrlKey));
if (event && event.shiftKey) {
window.open(uri, "zotero-loaded-page",
"menubar=yes,location=yes,toolbar=yes,personalbar=yes,resizable=yes,scrollbars=yes,status=yes");
}
else if (openInNewTab || !window.loadURI) {
// if no gBrowser, find it
if(!gBrowser) {
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
var browserWindow = wm.getMostRecentWindow("navigator:browser");
var gBrowser = browserWindow.gBrowser;
for each(var uri in uris) {
// Ignore javascript: and data: URIs
if (uri.match(/^(javascript|data):/)) {
return;
}
// load in a new tab
var tab = gBrowser.addTab(uri);
var browser = gBrowser.getBrowserForTab(tab);
if (event && event.shiftKey || !openInNewTab) {
// if shift key is down, or we are opening in a new tab because there is no loadURI,
// select new tab
gBrowser.selectedTab = tab;
if (Zotero.isStandalone && uri.match(/^https?/)) {
var io = Components.classes['@mozilla.org/network/io-service;1']
.getService(Components.interfaces.nsIIOService);
var uri = io.newURI(uri, null, null);
var handler = Components.classes['@mozilla.org/uriloader/external-protocol-service;1']
.getService(Components.interfaces.nsIExternalProtocolService)
.getProtocolHandlerInfo('http');
handler.preferredAction = Components.interfaces.nsIHandlerInfo.useSystemDefault;
handler.launchWithURI(uri, null);
return;
}
// Open in new tab
var openInNewTab = event && (event.metaKey || (!Zotero.isMac && event.ctrlKey));
if (event && event.shiftKey && !openInNewTab) {
window.open(uri, "zotero-loaded-page",
"menubar=yes,location=yes,toolbar=yes,personalbar=yes,resizable=yes,scrollbars=yes,status=yes");
}
else if (openInNewTab || !window.loadURI || uris.length > 1) {
// if no gBrowser, find it
if(!gBrowser) {
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
var browserWindow = wm.getMostRecentWindow("navigator:browser");
var gBrowser = browserWindow.gBrowser;
}
// load in a new tab
var tab = gBrowser.addTab(uri);
var browser = gBrowser.getBrowserForTab(tab);
if (event && event.shiftKey || !openInNewTab) {
// if shift key is down, or we are opening in a new tab because there is no loadURI,
// select new tab
gBrowser.selectedTab = tab;
}
}
else {
window.loadURI(uri);
}
}
else {
window.loadURI(uri);
}
}
@ -3351,7 +3357,6 @@ var ZoteroPane = new function()
"zotero-error-report", "chrome,centerscreen,modal", io);
}
/*
* Display an error message saying that an error has occurred and Firefox
* needs to be restarted.
@ -3408,4 +3413,5 @@ var ZoteroPane = new function()
}
}
}
}

View file

@ -43,6 +43,7 @@
<script src="timelineInterface.js"/>
<script src="recognizePDF.js"/>
<script src="browser.js"/>
<script src="locateMenu.js"/>
<commandset id="mainCommandSet">
<command id="cmd_zotero_search" oncommand="ZoteroPane.search();"/>
@ -334,38 +335,45 @@
<splitter id="zotero-view-splitter" resizebefore="closest" resizeafter="closest"/>
<vbox id="zotero-item-pane" persist="width">
<hbox class="toolbar" align="center" pack="end">
<hbox id="zotero-tb-sync-progress-box" hidden="true" align="center">
<toolbarbutton id="zotero-tb-sync-storage-cancel"
tooltiptext="Cancel Storage Sync"
oncommand="Zotero.Sync.Storage.QueueManager.cancel()"/>
<progressmeter id="zotero-tb-sync-progress" mode="determined"
value="0" tooltip="zotero-tb-sync-progress-tooltip">
</progressmeter>
<tooltip id="zotero-tb-sync-progress-tooltip" noautohide="true">
<grid>
<columns>
<column/>
<column/>
</columns>
<rows>
<row>
<label value="&zotero.sync.storage.progress;"/>
<label id="zotero-tb-sync-progress-tooltip-progress"/>
</row>
<row>
<label value="&zotero.sync.storage.downloads;"/>
<label
id="zotero-tb-sync-progress-tooltip-downloads"/>
</row>
<row>
<label value="&zotero.sync.storage.uploads;"/>
<label
id="zotero-tb-sync-progress-tooltip-uploads"/>
</row>
</rows>
</grid>
</tooltip>
<hbox class="toolbar" align="center">
<hbox align="center" pack="start" flex="1">
<toolbarbutton id="zotero-tb-locate" class="zotero-tb-button" tooltiptext="&zotero.toolbar.openURL.label;" type="menu">
<menupopup id="zotero-tb-locate-menu" onpopupshowing="Zotero_LocateMenu.buildLocateMenu()"/>
</toolbarbutton>
</hbox>
<hbox align="center" pack="end">
<hbox id="zotero-tb-sync-progress-box" hidden="true" align="center">
<toolbarbutton id="zotero-tb-sync-storage-cancel"
tooltiptext="Cancel Storage Sync"
oncommand="Zotero.Sync.Storage.QueueManager.cancel()"/>
<progressmeter id="zotero-tb-sync-progress" mode="determined"
value="0" tooltip="zotero-tb-sync-progress-tooltip">
</progressmeter>
<tooltip id="zotero-tb-sync-progress-tooltip" noautohide="true">
<grid>
<columns>
<column/>
<column/>
</columns>
<rows>
<row>
<label value="&zotero.sync.storage.progress;"/>
<label id="zotero-tb-sync-progress-tooltip-progress"/>
</row>
<row>
<label value="&zotero.sync.storage.downloads;"/>
<label
id="zotero-tb-sync-progress-tooltip-downloads"/>
</row>
<row>
<label value="&zotero.sync.storage.uploads;"/>
<label
id="zotero-tb-sync-progress-tooltip-uploads"/>
</row>
</rows>
</grid>
</tooltip>
</hbox>
</hbox>
<toolbarbutton id="zotero-tb-sync-warning" hidden="true"/>
<toolbarbutton id="zotero-tb-sync" class="zotero-tb-button" tooltip="_child"

View file

@ -150,6 +150,14 @@
<!ENTITY zotero.preferences.prefpane.advanced "Advanced">
<!ENTITY zotero.preferences.prefpane.locate "Locate">
<!ENTITY zotero.preferences.locate.locateEngineManager "Article Lookup Engine Manager">
<!ENTITY zotero.preferences.locate.description "Description">
<!ENTITY zotero.preferences.locate.name "Name">
<!ENTITY zotero.preferences.locate.locateEnginedescription "A Lookup Engine extends the capability of the Locate drop down in the Info pane. By enabling Lookup Engines in the list below they will be added to the drop down and can be used to locate resources from your library on the web.">
<!ENTITY zotero.preferences.locate.addDescription "To add a Lookup Engine that is not on the list, visit the desired search engine in your browser and select 'Add' from the Firefox Search Bar. When you reopen this preference pane you will have the option to enable the new Lookup Engine.">
<!ENTITY zotero.preferences.locate.restoreDefaults "Restore Defaults">
<!ENTITY zotero.preferences.charset "Character Encoding">
<!ENTITY zotero.preferences.charset.importCharset "Import Character Encoding">
<!ENTITY zotero.preferences.charset.displayExportOption "Display character encoding option on export">

View file

@ -167,10 +167,6 @@ pane.items.interview.manyParticipants = Interview by %S et al.
pane.item.selected.zero = No items selected
pane.item.selected.multiple = %S items selected
pane.item.goToURL.online.label = View
pane.item.goToURL.online.tooltip = Go to this item online
pane.item.goToURL.snapshot.label = View Snapshot
pane.item.goToURL.snapshot.tooltip = View snapshot for this item
pane.item.changeType.title = Change Item Type
pane.item.changeType.text = Are you sure you want to change the item type?\n\nThe following fields will be lost:
pane.item.defaultFirstName = first
@ -710,3 +706,13 @@ rtfScan.scannedFileSuffix = (Scanned)
lookup.failure.title = Lookup Failed
lookup.failure.description = Zotero could not find a record for the specified identifier. Please verify the identifier and try again.
locate.online.label = View Online
locate.online.tooltip = Go to this item online
locate.snapshot.label = View Snapshot
locate.snapshot.tooltip = View snapshot for this item
locate.libraryLookup.label = Library Lookup
locate.libraryLookup.tooltip = Look up this item using the selected OpenURL resolver
locate.waybackMachine.label = Wayback Machine
locate.waybackMachine.tooltip = View an archived version of this item
locate.manageLocateEngines = Manage Locate Engines...

View file

@ -15,24 +15,6 @@ textbox, tagsbox textbox
margin-left: 0;
}
#go-buttons button
{
list-style-image: url('chrome://zotero/skin/toolbar-go-arrow.png');
-moz-box-direction: reverse;
-moz-box-flex: 1;
}
#go-buttons button#locate-button
{
-moz-box-direction: normal;
}
#go-buttons button[disabled=true]
{
list-style-image: url('chrome://zotero/skin/toolbar-go-arrow-disabled.png');
}
/* DEBUG: this doesn't seem to work, unfortunately
label[singleField=false]:after

View file

@ -204,6 +204,11 @@
list-style-image: url('chrome://zotero/skin/toolbar-advanced-search.png');
}
#zotero-tb-locate
{
list-style-image: url('chrome://zotero/skin/toolbar-go-arrow.png');
}
#zotero-tb-sync-storage-cancel
{
list-style-image: url(chrome://zotero/skin/control_stop_blue.png);

View file

@ -247,3 +247,14 @@ grid row hbox:first-child
{
margin: .75em 0;
}
treechildren::-moz-tree-checkbox {
/* unchecked checkbox treecells. This style MUST come before treechildren::-moz-tree-checkbox(checked) otherwise it won't take effect. */
list-style-image: none;
}
treechildren::-moz-tree-checkbox(checked){
/* css for checked cells. cbox-check.gif isn't available in Firefox 1, 2, and 3 on Mac OS X, so you should specify a URL to an image
in your extension or elsewhere. */
list-style-image: url("chrome://global/skin/checkbox/cbox-check.gif");
}

View file

@ -90,6 +90,7 @@ var xpcomFiles = [
'integration',
'integration_compat',
'itemTreeView',
'locateManager',
'mime',
'mimeTypeHandler',
'notifier',

43
engines.json Normal file
View file

@ -0,0 +1,43 @@
[
{
"name": "CrossRef Lookup",
"alias": "CrossRef",
"_urlTemplate": "http://crossref.org/openurl?{z:openURL}&pid=zter:zter321",
"description": "CrossRef Search Engine",
"hidden": false,
"_urlParams": [],
"_urlNamespaces": {
"z": "http://www.zotero.org/namespaces/openSearch#",
"": "http://a9.com/-/spec/opensearch/1.1/"
},
"_iconSourceURI": "http://crossref.org/favicon.ico"
},
{
"name": "Google Scholar Search",
"alias": "Google Scholar",
"_urlTemplate": "http://scholar.google.com/scholar?as_q=&as_epq={z:title}&as_occt=title&as_sauthors={rft:aufirst?}+{rft:aulast?}&as_ylo={z:year?}&as_yhi={z:year?}&as_sdt=1.&as_sdtp=on&as_sdtf=&as_sdts=22&",
"description": "Google Scholar Search",
"hidden": true,
"_urlParams": [],
"_urlNamespaces": {
"rft": "info:ofi/fmt:kev:mtx:journal",
"z": "http://www.zotero.org/namespaces/openSearch#",
"": "http://a9.com/-/spec/opensearch/1.1/"
},
"_iconSourceURI": "http://scholar.google.com/favicon.ico"
},
{
"name": "Pubget Lookup",
"alias": "Pubget",
"_urlTemplate": "http://pubget.com/openurl?rft.title={rft:title}&rft.issue={rft:issue?}&rft.spage={rft:spage?}&rft.epage={rft:epage?}&rft.issn={rft:issn?}&rft.jtitle={rft:stitle?}&doi={z:DOI?}",
"description": "Pubget Article Lookup",
"hidden": true,
"_urlParams": [],
"_urlNamespaces": {
"rft": "info:ofi/fmt:kev:mtx:journal",
"z": "http://www.zotero.org/namespaces/openSearch#",
"": "http://a9.com/-/spec/opensearch/1.1/"
},
"_iconSourceURI": "http://pubget.com/favicon.ico"
}
]