Addresses #400, Report generation
A work in progress: - Implemented zotero:// custom protocol handler, which will likely be useful for other things too - First version of XHTML/CSS detail view -- definitely needs feedback, work, and refinement but is more or less functional - Added XUL-side interface and context menu options for loading report URLs Going forward: - Other formats (RTF, CSV) - Other views (list view, annotated bibliography, etc.) - Report options window (let the user which fields to include (with saved templates?)) - Ability to specify custom CSS files? - Extension of Zotero protocol handler to trigger Zotero events? This would allow more interactive reports with the ability to click to select items in the Z pane, run searches by clicking on tags, etc., but would have to be limited to idempotent actions. Other changes: - ZoteroPane.getSortField() and ZoteroPane.getSortDirection() - Zotero.Utilities.htmlSpecialChars(str) - Fixed sort direction in items pane (triangle icon now goes the right direction, though the default direction on clicking a new column is incorrect) - firstCreator now included in toArray(), though it's not particularly correct (#287, more or less) - ZoteroPane.getSelectedCollection/SavedSearch/Items now take asIDs parameter to return ids instead of objects
This commit is contained in:
parent
ab7f618a3e
commit
d3e29108a8
|
@ -51,6 +51,8 @@ var ZoteroPane = new function()
|
||||||
this.getSelectedCollection = getSelectedCollection;
|
this.getSelectedCollection = getSelectedCollection;
|
||||||
this.getSelectedSavedSearch = getSelectedSavedSearch;
|
this.getSelectedSavedSearch = getSelectedSavedSearch;
|
||||||
this.getSelectedItems = getSelectedItems;
|
this.getSelectedItems = getSelectedItems;
|
||||||
|
this.getSortField = getSortField;
|
||||||
|
this.getSortDirection = getSortDirection;
|
||||||
this.buildCollectionContextMenu = buildCollectionContextMenu;
|
this.buildCollectionContextMenu = buildCollectionContextMenu;
|
||||||
this.buildItemContextMenu = buildItemContextMenu;
|
this.buildItemContextMenu = buildItemContextMenu;
|
||||||
this.onDoubleClick = onDoubleClick;
|
this.onDoubleClick = onDoubleClick;
|
||||||
|
@ -536,11 +538,17 @@ var ZoteroPane = new function()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns Zotero.ItemTreeView instance for collections pane
|
||||||
|
*/
|
||||||
function getCollectionsView()
|
function getCollectionsView()
|
||||||
{
|
{
|
||||||
return collectionsView;
|
return collectionsView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns Zotero.ItemTreeView instance for items pane
|
||||||
|
*/
|
||||||
function getItemsView()
|
function getItemsView()
|
||||||
{
|
{
|
||||||
return itemsView;
|
return itemsView;
|
||||||
|
@ -568,31 +576,46 @@ var ZoteroPane = new function()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSelectedCollection()
|
function getSelectedCollection(asID)
|
||||||
{
|
{
|
||||||
if(collectionsView.selection.count > 0 && collectionsView.selection.currentIndex != -1)
|
if(collectionsView.selection.count > 0 && collectionsView.selection.currentIndex != -1)
|
||||||
{
|
{
|
||||||
var collection = collectionsView._getItemAtRow(collectionsView.selection.currentIndex);
|
var collection = collectionsView._getItemAtRow(collectionsView.selection.currentIndex);
|
||||||
if(collection && collection.isCollection())
|
if (collection && collection.isCollection()) {
|
||||||
|
if (asID) {
|
||||||
|
return collection.ref.getID();
|
||||||
|
}
|
||||||
|
else {
|
||||||
return collection.ref;
|
return collection.ref;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSelectedSavedSearch()
|
|
||||||
{
|
|
||||||
if(collectionsView.selection.count > 0 && collectionsView.selection.currentIndex != -1)
|
|
||||||
{
|
|
||||||
var collection = collectionsView._getItemAtRow(collectionsView.selection.currentIndex);
|
|
||||||
if(collection && collection.isSearch())
|
|
||||||
{
|
|
||||||
return collection.ref;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSelectedItems()
|
function getSelectedSavedSearch(asID)
|
||||||
|
{
|
||||||
|
if(collectionsView.selection.count > 0 && collectionsView.selection.currentIndex != -1)
|
||||||
|
{
|
||||||
|
var collection = collectionsView._getItemAtRow(collectionsView.selection.currentIndex);
|
||||||
|
if (collection && collection.isSearch()) {
|
||||||
|
if (asID) {
|
||||||
|
return collection.ref.id;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return collection.ref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return an array of Item objects for selected items
|
||||||
|
*
|
||||||
|
* If asIDs is true, return an array of itemIDs instead
|
||||||
|
*/
|
||||||
|
function getSelectedItems(asIDs)
|
||||||
{
|
{
|
||||||
if(itemsView)
|
if(itemsView)
|
||||||
{
|
{
|
||||||
|
@ -602,13 +625,38 @@ var ZoteroPane = new function()
|
||||||
for (var i=0, len=itemsView.selection.getRangeCount(); i<len; i++)
|
for (var i=0, len=itemsView.selection.getRangeCount(); i<len; i++)
|
||||||
{
|
{
|
||||||
itemsView.selection.getRangeAt(i,start,end);
|
itemsView.selection.getRangeAt(i,start,end);
|
||||||
for (var j=start.value; j<=end.value; j++)
|
for (var j=start.value; j<=end.value; j++) {
|
||||||
|
if (asIDs) {
|
||||||
|
items.push(itemsView._getItemAtRow(j).ref.getID());
|
||||||
|
}
|
||||||
|
else {
|
||||||
items.push(itemsView._getItemAtRow(j).ref);
|
items.push(itemsView._getItemAtRow(j).ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getSortField() {
|
||||||
|
if (!itemsView) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemsView.getSortField();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getSortDirection() {
|
||||||
|
if (!itemsView) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemsView.getSortDirection();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function buildCollectionContextMenu()
|
function buildCollectionContextMenu()
|
||||||
{
|
{
|
||||||
var menu = document.getElementById('zotero-collectionmenu');
|
var menu = document.getElementById('zotero-collectionmenu');
|
||||||
|
@ -618,36 +666,39 @@ var ZoteroPane = new function()
|
||||||
collectionsView._getItemAtRow(collectionsView.selection.currentIndex).isCollection())
|
collectionsView._getItemAtRow(collectionsView.selection.currentIndex).isCollection())
|
||||||
{
|
{
|
||||||
var hide = [0,1,5,7,10,12,13];
|
var hide = [0,1,5,7,10,12,13];
|
||||||
var show = [2,3,4,6,8,9,11];
|
var show = [2,3,4,6,8,9,11,14];
|
||||||
if (itemsView.rowCount>0)
|
if (itemsView.rowCount>0)
|
||||||
{
|
{
|
||||||
var enable = [9,11];
|
var enable = [9,11,14];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var disable = [9,11];
|
var disable = [9,11,14];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
menu.childNodes[14].setAttribute('label', Zotero.getString('pane.collections.menu.generateReport.collection'));
|
||||||
}
|
}
|
||||||
// Saved Search
|
// Saved Search
|
||||||
else if (collectionsView.selection.count == 1 &&
|
else if (collectionsView.selection.count == 1 &&
|
||||||
collectionsView._getItemAtRow(collectionsView.selection.currentIndex).isSearch())
|
collectionsView._getItemAtRow(collectionsView.selection.currentIndex).isSearch())
|
||||||
{
|
{
|
||||||
var hide = [0,1,2,3,4,6,9,11,13];
|
var hide = [0,1,2,3,4,6,9,11,13];
|
||||||
var show = [5,7,8,10,12];
|
var show = [5,7,8,10,12,14];
|
||||||
if (itemsView.rowCount>0)
|
if (itemsView.rowCount>0)
|
||||||
{
|
{
|
||||||
var enable = [10,12];
|
var enable = [10,12,14];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var disable = [10,12];
|
var disable = [10,12,14];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
menu.childNodes[14].setAttribute('label', Zotero.getString('pane.collections.menu.generateReport.savedSearch'));
|
||||||
}
|
}
|
||||||
// Library
|
// Library
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var hide = [2,4,5,6,7,8,9,10,11,12];
|
var hide = [2,4,5,6,7,8,9,10,11,12,14];
|
||||||
var show = [0,1,3,13];
|
var show = [0,1,3,13];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -680,7 +731,7 @@ var ZoteroPane = new function()
|
||||||
|
|
||||||
if(itemsView && itemsView.selection.count > 0)
|
if(itemsView && itemsView.selection.count > 0)
|
||||||
{
|
{
|
||||||
enable.push(4,5,6,8);
|
enable.push(0,1,2,4,5,6,7,8,9);
|
||||||
|
|
||||||
// Multiple items selected
|
// Multiple items selected
|
||||||
if (itemsView.selection.count > 1)
|
if (itemsView.selection.count > 1)
|
||||||
|
@ -707,7 +758,7 @@ var ZoteroPane = new function()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
disable.push(4,5,7,8);
|
disable.push(0,1,2,4,5,7,8,9);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove from collection
|
// Remove from collection
|
||||||
|
@ -725,6 +776,7 @@ var ZoteroPane = new function()
|
||||||
menu.childNodes[5].setAttribute('label', Zotero.getString('pane.items.menu.erase' + multiple));
|
menu.childNodes[5].setAttribute('label', Zotero.getString('pane.items.menu.erase' + multiple));
|
||||||
menu.childNodes[7].setAttribute('label', Zotero.getString('pane.items.menu.export' + multiple));
|
menu.childNodes[7].setAttribute('label', Zotero.getString('pane.items.menu.export' + multiple));
|
||||||
menu.childNodes[8].setAttribute('label', Zotero.getString('pane.items.menu.createBib' + multiple));
|
menu.childNodes[8].setAttribute('label', Zotero.getString('pane.items.menu.createBib' + multiple));
|
||||||
|
menu.childNodes[9].setAttribute('label', Zotero.getString('pane.items.menu.generateReport' + multiple));
|
||||||
|
|
||||||
for (var i in disable)
|
for (var i in disable)
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
|
|
||||||
<script src="overlay.js"/>
|
<script src="overlay.js"/>
|
||||||
<script src="fileInterface.js"/>
|
<script src="fileInterface.js"/>
|
||||||
|
<script src="reportInterface.js"/>
|
||||||
|
|
||||||
<commandset id="mainCommandSet">
|
<commandset id="mainCommandSet">
|
||||||
<command id="cmd_zotero_search" oncommand="ZoteroPane.search();"/>
|
<command id="cmd_zotero_search" oncommand="ZoteroPane.search();"/>
|
||||||
|
@ -73,6 +74,7 @@
|
||||||
<menuitem label="&zotero.toolbar.createBibCollection.label;" oncommand="Zotero_File_Interface.bibliographyFromCollection();"/>
|
<menuitem label="&zotero.toolbar.createBibCollection.label;" oncommand="Zotero_File_Interface.bibliographyFromCollection();"/>
|
||||||
<menuitem label="&zotero.toolbar.createBibSavedSearch.label;" oncommand="Zotero_File_Interface.bibliographyFromCollection()"/>
|
<menuitem label="&zotero.toolbar.createBibSavedSearch.label;" oncommand="Zotero_File_Interface.bibliographyFromCollection()"/>
|
||||||
<menuitem label="&zotero.toolbar.export.label;" oncommand="Zotero_File_Interface.exportFile()"/>
|
<menuitem label="&zotero.toolbar.export.label;" oncommand="Zotero_File_Interface.exportFile()"/>
|
||||||
|
<menuitem oncommand="Zotero_Report_Interface.loadCollectionReport()"/>
|
||||||
</popup>
|
</popup>
|
||||||
<popup id="zotero-itemmenu" onpopupshowing="ZoteroPane.buildItemContextMenu();">
|
<popup id="zotero-itemmenu" onpopupshowing="ZoteroPane.buildItemContextMenu();">
|
||||||
<menuitem label="&zotero.items.menu.attach.note;" oncommand="ZoteroPane.newNote(false, this.parentNode.getAttribute('itemID'))"/>
|
<menuitem label="&zotero.items.menu.attach.note;" oncommand="ZoteroPane.newNote(false, this.parentNode.getAttribute('itemID'))"/>
|
||||||
|
@ -84,6 +86,7 @@
|
||||||
<menuseparator/>
|
<menuseparator/>
|
||||||
<menuitem oncommand="Zotero_File_Interface.exportItems();"/>
|
<menuitem oncommand="Zotero_File_Interface.exportItems();"/>
|
||||||
<menuitem oncommand="Zotero_File_Interface.bibliographyFromItems();"/>
|
<menuitem oncommand="Zotero_File_Interface.bibliographyFromItems();"/>
|
||||||
|
<menuitem oncommand="Zotero_Report_Interface.loadItemReport()"/>
|
||||||
</popup>
|
</popup>
|
||||||
</popupset>
|
</popupset>
|
||||||
<vbox id="zotero-collections-pane" persist="width" flex="1">
|
<vbox id="zotero-collections-pane" persist="width" flex="1">
|
||||||
|
|
84
chrome/content/zotero/reportInterface.js
Normal file
84
chrome/content/zotero/reportInterface.js
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Copyright (c) 2006 Center for History and New Media
|
||||||
|
George Mason University, Fairfax, Virginia, USA
|
||||||
|
http://chnm.gmu.edu
|
||||||
|
|
||||||
|
Licensed under the Educational Community License, Version 1.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.opensource.org/licenses/ecl1.php
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
***** END LICENSE BLOCK *****
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
Zotero_Report_Interface = new function() {
|
||||||
|
this.loadCollectionReport = loadCollectionReport;
|
||||||
|
this.loadItemReport = loadItemReport;
|
||||||
|
this.loadItemReportByIds = loadItemReportByIds;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load a report for the currently selected collection
|
||||||
|
*/
|
||||||
|
function loadCollectionReport() {
|
||||||
|
var queryString = '';
|
||||||
|
|
||||||
|
var id = ZoteroPane.getSelectedCollection(true);
|
||||||
|
var sortColumn = ZoteroPane.getSortField();
|
||||||
|
var sortDirection = ZoteroPane.getSortDirection();
|
||||||
|
|
||||||
|
if (sortColumn != 'title' || sortDirection != 'ascending') {
|
||||||
|
queryString = '?sort=' + sortColumn +
|
||||||
|
(sortDirection != 'ascending' ? '/d' : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
window.loadURI('zotero://report/collection/' + id + queryString);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var id = ZoteroPane.getSelectedSavedSearch(true);
|
||||||
|
if (id) {
|
||||||
|
window.loadURI('zotero://report/search/' + id + queryString);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw ('No collection currently selected');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load a report for the currently selected collection
|
||||||
|
*/
|
||||||
|
function loadItemReport() {
|
||||||
|
var items = ZoteroPane.getSelectedItems(true);
|
||||||
|
|
||||||
|
if (!items || !items.length) {
|
||||||
|
throw ('No items currently selected');
|
||||||
|
}
|
||||||
|
|
||||||
|
window.loadURI('zotero://report/items/' + items.join('-'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load a report for the specified items
|
||||||
|
*/
|
||||||
|
function loadItemReportByIds(ids) {
|
||||||
|
if (!ids || !ids.length) {
|
||||||
|
throw ('No itemIDs provided to loadItemReportByIds()');
|
||||||
|
}
|
||||||
|
|
||||||
|
window.loadURI('zotero://report/items/' + ids.join('-'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1758,7 +1758,7 @@ Zotero.Item.prototype.toArray = function(){
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Skip certain fields
|
// Skip certain fields
|
||||||
case 'firstCreator':
|
//case 'firstCreator':
|
||||||
case 'numNotes':
|
case 'numNotes':
|
||||||
case 'numAttachments':
|
case 'numAttachments':
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Constructor the the ItemTreeView object
|
* Constructor for the ItemTreeView object
|
||||||
*/
|
*/
|
||||||
Zotero.ItemTreeView = function(itemGroup, sourcesOnly)
|
Zotero.ItemTreeView = function(itemGroup, sourcesOnly)
|
||||||
{
|
{
|
||||||
|
@ -526,7 +526,7 @@ Zotero.ItemTreeView.prototype.sort = function()
|
||||||
// DEBUG: This may be necessary for 1.0.0b2.r1=>1.0.0b2.r2 transition
|
// DEBUG: This may be necessary for 1.0.0b2.r1=>1.0.0b2.r2 transition
|
||||||
column = this._treebox.columns.getFirstColumn();
|
column = this._treebox.columns.getFirstColumn();
|
||||||
}
|
}
|
||||||
var order = column.element.getAttribute('sortDirection') == 'descending';
|
var order = column.element.getAttribute('sortDirection') == 'ascending';
|
||||||
|
|
||||||
if(column.id == 'zotero-items-typeIcon-column')
|
if(column.id == 'zotero-items-typeIcon-column')
|
||||||
{
|
{
|
||||||
|
@ -767,6 +767,18 @@ Zotero.ItemTreeView.prototype.rememberSelection = function(selection)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Zotero.ItemTreeView.prototype.getSortField = function() {
|
||||||
|
var col = this._treebox.columns.getSortedColumn().id;
|
||||||
|
// zotero.items._________.column
|
||||||
|
return col.substring(13, col.length-7);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Zotero.ItemTreeView.prototype.getSortDirection = function() {
|
||||||
|
return this._treebox.columns.getSortedColumn().element.getAttribute('sortDirection');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// Command Controller:
|
/// Command Controller:
|
||||||
|
|
272
chrome/content/zotero/xpcom/report.js
Normal file
272
chrome/content/zotero/xpcom/report.js
Normal file
|
@ -0,0 +1,272 @@
|
||||||
|
/*
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Copyright (c) 2006 Center for History and New Media
|
||||||
|
George Mason University, Fairfax, Virginia, USA
|
||||||
|
http://chnm.gmu.edu
|
||||||
|
|
||||||
|
Licensed under the Educational Community License, Version 1.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.opensource.org/licenses/ecl1.php
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
***** END LICENSE BLOCK *****
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
Zotero.Report = new function() {
|
||||||
|
this.generateHTMLDetails = generateHTMLDetails;
|
||||||
|
this.generateHTMLList = generateHTMLList;
|
||||||
|
|
||||||
|
// Sites that don't need the query string
|
||||||
|
// (full URL is kept for link but stripped for display)
|
||||||
|
var _noQueryStringSites = [
|
||||||
|
/^http:\/\/([^\.]*\.)?nytimes\.com/
|
||||||
|
];
|
||||||
|
|
||||||
|
function generateHTMLDetails(items) {
|
||||||
|
var ZU = new Zotero.Utilities();
|
||||||
|
var escapeXML = ZU.htmlSpecialChars;
|
||||||
|
|
||||||
|
var content = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" ';
|
||||||
|
content += '"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\n';
|
||||||
|
content += '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">\n';
|
||||||
|
content += '<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\n';
|
||||||
|
content += '<title>' + Zotero.getString('report.title.default') + '</title>\n';
|
||||||
|
content += '<link rel="stylesheet" type="text/css" href="chrome://zotero/skin/report/detail.css"/>\n';
|
||||||
|
content += '<link rel="stylesheet" type="text/css" media="print" href="chrome://zotero/skin/report/detail_print.css"/>\n';
|
||||||
|
content += '</head>\n<body>\n';
|
||||||
|
|
||||||
|
content += '<ul class="report">\n';
|
||||||
|
for each(var arr in items) {
|
||||||
|
//Zotero.debug(arr);
|
||||||
|
|
||||||
|
content += '<li id="i' + arr.itemID + '" class="' + arr.itemType + '"><span>\n';
|
||||||
|
|
||||||
|
// Title
|
||||||
|
if (arr.title) {
|
||||||
|
content += '<h2>' + escapeXML(arr.title) + '</h2>\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata table
|
||||||
|
var table = false;
|
||||||
|
var tableContent = '<table>\n';
|
||||||
|
|
||||||
|
// Item type
|
||||||
|
tableContent += '<tr>\n';
|
||||||
|
tableContent += '<th>'
|
||||||
|
+ escapeXML(Zotero.getString('itemFields.itemType'))
|
||||||
|
+ '</th>\n';
|
||||||
|
tableContent += '<td>' + escapeXML(Zotero.getString('itemTypes.' + arr['itemType'])) + '</td>\n';
|
||||||
|
tableContent += '</tr>\n';
|
||||||
|
|
||||||
|
// Creators
|
||||||
|
if (arr['creators']) {
|
||||||
|
table = true;
|
||||||
|
var displayText;
|
||||||
|
|
||||||
|
for each(var creator in arr['creators']) {
|
||||||
|
// Two fields
|
||||||
|
if (creator['fieldMode']==0) {
|
||||||
|
displayText = creator['firstName'] + ' ' + creator['lastName'];
|
||||||
|
}
|
||||||
|
// Single field
|
||||||
|
else if (creator['fieldMode']==1) {
|
||||||
|
displayText = creator['lastName'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
tableContent += '<tr>\n';
|
||||||
|
tableContent += '<th class="' + creator.creatorType + '">'
|
||||||
|
+ escapeXML(Zotero.getString('creatorTypes.' + creator.creatorType))
|
||||||
|
+ '</th>\n';
|
||||||
|
tableContent += '<td>' + escapeXML(displayText) + '</td>\n';
|
||||||
|
tableContent += '</tr>\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move dateAdded and dateModified to the end of the array
|
||||||
|
var da = arr['dateAdded'];
|
||||||
|
var dm = arr['dateModified'];
|
||||||
|
delete arr['dateAdded'];
|
||||||
|
delete arr['dateModified'];
|
||||||
|
arr['dateAdded'] = dm;
|
||||||
|
arr['dateModified'] = dm;
|
||||||
|
|
||||||
|
for (var i in arr) {
|
||||||
|
// Skip certain fields
|
||||||
|
switch (i) {
|
||||||
|
case 'itemType':
|
||||||
|
case 'itemID':
|
||||||
|
case 'sourceItemID':
|
||||||
|
case 'title':
|
||||||
|
case 'firstCreator':
|
||||||
|
case 'creators':
|
||||||
|
case 'tags':
|
||||||
|
case 'seeAlso':
|
||||||
|
case 'notes':
|
||||||
|
case 'note':
|
||||||
|
case 'attachments':
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var localizedFieldName = Zotero.getString('itemFields.' + i);
|
||||||
|
}
|
||||||
|
// Skip fields we don't have a localized string for
|
||||||
|
catch (e) {
|
||||||
|
Zotero.debug('Localized string not available for ' + 'itemFields.' + i, 2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
table = true;
|
||||||
|
var fieldText;
|
||||||
|
|
||||||
|
// Shorten long URLs manually until Firefox wraps at ?
|
||||||
|
// (like Safari) or supports the CSS3 word-wrap property
|
||||||
|
var firstSpace = arr[i].indexOf(' ');
|
||||||
|
if (arr[i].indexOf('http://') === 0 &&
|
||||||
|
((firstSpace == -1 && arr[i].length > 29) || firstSpace > 29)) {
|
||||||
|
|
||||||
|
var stripped = false;
|
||||||
|
|
||||||
|
// Strip query string for sites we know don't need it
|
||||||
|
for each(var re in _noQueryStringSites) {
|
||||||
|
if (re.test(arr[i])){
|
||||||
|
var pos = arr[i].indexOf('?');
|
||||||
|
if (pos != -1) {
|
||||||
|
fieldText = arr[i].substr(0, pos);
|
||||||
|
stripped = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stripped) {
|
||||||
|
// Add a line-break after the ? of long URLs,
|
||||||
|
fieldText = arr[i].replace('?', "?<ZOTEROBREAK/>");
|
||||||
|
|
||||||
|
// Strip query string variables from the end while the
|
||||||
|
// query string is longer than the main part
|
||||||
|
var pos = fieldText.indexOf('?');
|
||||||
|
if (pos != -1) {
|
||||||
|
while (pos < (fieldText.length / 2)) {
|
||||||
|
var lastAmp = fieldText.lastIndexOf('&');
|
||||||
|
if (lastAmp == -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fieldText = fieldText.substr(0, lastAmp);
|
||||||
|
var shortened = true;
|
||||||
|
}
|
||||||
|
// Append '&...' to the end
|
||||||
|
if (shortened) {
|
||||||
|
fieldText += "&<ZOTEROHELLIP/>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 'url' && firstSpace == -1) {
|
||||||
|
fieldText = '<a href="' + escapeXML(arr[i]) + '">'
|
||||||
|
+ escapeXML(fieldText) + '</a>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Remove SQL date from multipart dates
|
||||||
|
// (e.g. '2006-00-00 Summer 2006' becomes 'Summer 2006')
|
||||||
|
else if (i=='date') {
|
||||||
|
fieldText = escapeXML(Zotero.Date.multipartToStr(arr[i]));
|
||||||
|
}
|
||||||
|
// Convert dates to local format
|
||||||
|
else if (i=='accessDate' || i=='dateAdded' || i=='dateModified') {
|
||||||
|
var date = Zotero.Date.sqlToDate(arr[i])
|
||||||
|
fieldText = escapeXML(date.toLocaleString());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fieldText = escapeXML(arr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
tableContent += '<tr>\n<th>' + escapeXML(localizedFieldName)
|
||||||
|
+ '</th>\n<td>' + fieldText + '</td>\n</tr>\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (table) {
|
||||||
|
content += tableContent + '</table>\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tags
|
||||||
|
if (arr['tags'] && arr['tags'].length) {
|
||||||
|
// TODO: localize
|
||||||
|
content += '<h3 class="tags">' + escapeXML('Tags') + '</h3>\n';
|
||||||
|
content += '<ul class="tags">\n';
|
||||||
|
for each(var tag in arr['tags']) {
|
||||||
|
content += '<li>' + escapeXML(tag) + '</li>\n';
|
||||||
|
}
|
||||||
|
content += '</ul>\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Child notes
|
||||||
|
if (arr['notes'] && arr['notes'].length) {
|
||||||
|
// TODO: localize
|
||||||
|
content += '<h3 class="notes">' + escapeXML(Zotero.getString('itemFields.notes')) + '</h3>\n';
|
||||||
|
content += '<ul class="notes">\n';
|
||||||
|
for each(var note in arr['notes']) {
|
||||||
|
content += '<li id="i' + note.itemID + '">\n';
|
||||||
|
content += '<p>' + escapeXML(note.note) + '</p>\n';
|
||||||
|
content += '</li>\n';
|
||||||
|
}
|
||||||
|
content += '</ul>\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Independent note
|
||||||
|
if (arr['note']) {
|
||||||
|
content += '<p>' + escapeXML(arr['note']) + '</p>\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attachments
|
||||||
|
if (arr['attachments'] && arr['attachments'].length) {
|
||||||
|
content += '<h3 class="attachments">' + escapeXML(Zotero.getString('itemFields.attachments')) + '</h3>\n';
|
||||||
|
content += '<ul class="attachments">\n';
|
||||||
|
for each(var attachment in arr['attachments']) {
|
||||||
|
content += '<li id="i' + attachment.itemID + '">';
|
||||||
|
content += escapeXML(attachment.title);
|
||||||
|
content += '</li>\n';
|
||||||
|
}
|
||||||
|
content += '</ul>\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attachments
|
||||||
|
if (arr['seeAlso'] && arr['seeAlso'].length) {
|
||||||
|
content += '<h3 class="related">' + escapeXML(Zotero.getString('itemFields.related')) + '</h3>\n';
|
||||||
|
content += '<ul class="related">\n';
|
||||||
|
var relateds = Zotero.Items.get(arr['seeAlso']);
|
||||||
|
for each(var related in relateds) {
|
||||||
|
content += '<li id="i' + related.getID() + '">';
|
||||||
|
content += escapeXML(related.getField('title'));
|
||||||
|
content += '</li>\n';
|
||||||
|
}
|
||||||
|
content += '</ul>\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
content += '</span></li>\n';
|
||||||
|
}
|
||||||
|
content += '</ul>\n';
|
||||||
|
content += '</body>\n</html>';
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function generateHTMLList(items) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -115,6 +115,47 @@ Zotero.Utilities.prototype.cleanTags = function(x) {
|
||||||
return x.replace(/<[^>]+>/g, "");
|
return x.replace(/<[^>]+>/g, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Encode special XML/HTML characters
|
||||||
|
*
|
||||||
|
* Certain entities can be inserted manually:
|
||||||
|
*
|
||||||
|
* <ZOTEROBREAK/> => <br/>
|
||||||
|
* <ZOTEROHELLIP/> => …
|
||||||
|
*/
|
||||||
|
Zotero.Utilities.prototype.htmlSpecialChars = function(str) {
|
||||||
|
if (typeof str != 'string') {
|
||||||
|
throw "htmlSpecialChars: argument must be a string";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!str) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
var chars = ['&', '"',"'",'<','>'];
|
||||||
|
var entities = ['amp', 'quot', 'apos', 'lt', 'gt'];
|
||||||
|
|
||||||
|
var newString = str;
|
||||||
|
for (var i = 0; i < chars.length; i++) {
|
||||||
|
var re = new RegExp(chars[i], 'g');
|
||||||
|
newString = newString.replace(re, '&' + entities[i] + ';');
|
||||||
|
}
|
||||||
|
|
||||||
|
newString = newString.replace(/<ZOTERO([^\/]+)\/>/g, function (str, p1, offset, s) {
|
||||||
|
switch (p1) {
|
||||||
|
case 'BREAK':
|
||||||
|
return '<br/>';
|
||||||
|
case 'HELLIP':
|
||||||
|
return '…';
|
||||||
|
default:
|
||||||
|
return p1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newString;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Test if a string is an integer
|
* Test if a string is an integer
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -4,6 +4,8 @@ pane.collections.name = Collection name:
|
||||||
pane.collections.rename = Rename collection:
|
pane.collections.rename = Rename collection:
|
||||||
pane.collections.library = My Library
|
pane.collections.library = My Library
|
||||||
pane.collections.untitled = Untitled
|
pane.collections.untitled = Untitled
|
||||||
|
pane.collections.menu.generateReport.collection = Generate Report from Collection...
|
||||||
|
pane.collections.menu.generateReport.savedSearch = Generate Report from Saved Search...
|
||||||
|
|
||||||
pane.items.delete = Are you sure you want to delete the selected item?
|
pane.items.delete = Are you sure you want to delete the selected item?
|
||||||
pane.items.delete.multiple = Are you sure you want to delete the selected items?
|
pane.items.delete.multiple = Are you sure you want to delete the selected items?
|
||||||
|
@ -17,6 +19,8 @@ pane.items.menu.export = Export Selected Item...
|
||||||
pane.items.menu.export.multiple = Export Selected Items...
|
pane.items.menu.export.multiple = Export Selected Items...
|
||||||
pane.items.menu.createBib = Create Bibliography from Selected Item...
|
pane.items.menu.createBib = Create Bibliography from Selected Item...
|
||||||
pane.items.menu.createBib.multiple = Create Bibliography from Selected Items...
|
pane.items.menu.createBib.multiple = Create Bibliography from Selected Items...
|
||||||
|
pane.items.menu.generateReport = Generate Report from Selected Item...
|
||||||
|
pane.items.menu.generateReport.multiple = Generate Report from Selected Items...
|
||||||
|
|
||||||
pane.item.selected.zero = No items selected
|
pane.item.selected.zero = No items selected
|
||||||
pane.item.selected.multiple = %S items selected
|
pane.item.selected.multiple = %S items selected
|
||||||
|
@ -79,11 +83,15 @@ itemTypes.radioBroadcast = Radio Broadcast
|
||||||
itemTypes.podcast = Podcast
|
itemTypes.podcast = Podcast
|
||||||
itemTypes.computerProgram = Computer Program
|
itemTypes.computerProgram = Computer Program
|
||||||
|
|
||||||
|
itemFields.itemType = Type
|
||||||
itemFields.title = Title
|
itemFields.title = Title
|
||||||
itemFields.dateAdded = Date Added
|
itemFields.dateAdded = Date Added
|
||||||
itemFields.dateModified = Modified
|
itemFields.dateModified = Modified
|
||||||
itemFields.source = Source
|
itemFields.source = Source
|
||||||
itemFields.notes = Notes
|
itemFields.notes = Notes
|
||||||
|
itemFields.tags = Tags
|
||||||
|
itemFields.attachments = Attachments
|
||||||
|
itemFields.related = Related
|
||||||
itemFields.url = URL
|
itemFields.url = URL
|
||||||
itemFields.rights = Rights
|
itemFields.rights = Rights
|
||||||
itemFields.series = Series
|
itemFields.series = Series
|
||||||
|
@ -244,3 +252,5 @@ date.abbreviation.day = d
|
||||||
|
|
||||||
citation.multipleSources = Multiple Sources...
|
citation.multipleSources = Multiple Sources...
|
||||||
citation.singleSource = Single Source...
|
citation.singleSource = Single Source...
|
||||||
|
|
||||||
|
report.title.default = Zotero Report
|
107
chrome/skin/default/zotero/report/detail.css
Normal file
107
chrome/skin/default/zotero/report/detail.css
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
/* Fonts */
|
||||||
|
li, li > p {
|
||||||
|
font-family: Georgia, Times New Roman, Times, serif;
|
||||||
|
font-size: .95em;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1.3em;
|
||||||
|
margin: .4em 0;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 1.05em;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
font-size: .95em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
ul li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Line between items */
|
||||||
|
ul.report > li {
|
||||||
|
display: block;
|
||||||
|
border-bottom: 1px #333 solid;
|
||||||
|
padding: .95em 0;
|
||||||
|
}
|
||||||
|
ul.report > li:after {
|
||||||
|
content: ".";
|
||||||
|
display: block;
|
||||||
|
height: 0;
|
||||||
|
clear: both;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
ul.report > li:first-child {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
ul.report > li:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Metadata table */
|
||||||
|
table {
|
||||||
|
border: 1px #ccc solid;
|
||||||
|
float: right;
|
||||||
|
width: 35%;
|
||||||
|
margin: .1em 0 .75em 1.5em;
|
||||||
|
padding: .2em .3em;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
vertical-align: top;
|
||||||
|
text-align: right;
|
||||||
|
padding-right: .1em;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
th:after {
|
||||||
|
content: ':';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Tags and child notes */
|
||||||
|
ul ul {
|
||||||
|
padding: 0 .5em;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
margin-bottom: .4em;
|
||||||
|
}
|
||||||
|
h3:after {
|
||||||
|
content: ':';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Display tags as comma-separated lists */
|
||||||
|
ul.tags li {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
ul.tags li:not(:last-child):after {
|
||||||
|
content: ', ';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Child notes */
|
||||||
|
ul.notes {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
ul.notes li {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
ul.notes li:first-child p:first-child {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
ul.notes li p:first-child {
|
||||||
|
margin-top: .7em;
|
||||||
|
display: list-item;
|
||||||
|
list-style: square outside;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.notes li p, li.note p {
|
||||||
|
white-space: -moz-pre-wrap;
|
||||||
|
|
||||||
|
}
|
27
chrome/skin/default/zotero/report/detail_print.css
Normal file
27
chrome/skin/default/zotero/report/detail_print.css
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
a {
|
||||||
|
color: #000;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Firefox gets funky with floated elements that span two pages, so we pretend we're a table
|
||||||
|
*/
|
||||||
|
ul.report {
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.report > li {
|
||||||
|
display: table-row;
|
||||||
|
page-break-inside: avoid; /* Waiting on https://bugzilla.mozilla.org/show_bug.cgi?id=132035 */
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.report > li > span {
|
||||||
|
display: table-cell;
|
||||||
|
border-bottom: 1px solid #000; /* Normal <li> border disappears in table mode */
|
||||||
|
padding: .95em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.report > li:last-child > span {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
356
components/zotero-protocol-handler.js
Normal file
356
components/zotero-protocol-handler.js
Normal file
|
@ -0,0 +1,356 @@
|
||||||
|
/*
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Copyright (c) 2006 Center for History and New Media
|
||||||
|
George Mason University, Fairfax, Virginia, USA
|
||||||
|
http://chnm.gmu.edu
|
||||||
|
|
||||||
|
Licensed under the Educational Community License, Version 1.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.opensource.org/licenses/ecl1.php
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
Based on nsChromeExtensionHandler example code by Ed Anuff at
|
||||||
|
http://kb.mozillazine.org/Dev_:_Extending_the_Chrome_Protocol
|
||||||
|
|
||||||
|
|
||||||
|
***** END LICENSE BLOCK *****
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
const ZOTERO_SCHEME = "zotero";
|
||||||
|
const ZOTERO_PROTOCOL_CID = Components.ID("{9BC3D762-9038-486A-9D70-C997AF848A7C}");
|
||||||
|
const ZOTERO_PROTOCOL_CONTRACTID = "@mozilla.org/network/protocol;1?name=" + ZOTERO_SCHEME;
|
||||||
|
const ZOTERO_PROTOCOL_NAME = "Zotero Chrome Extension Protocol";
|
||||||
|
|
||||||
|
// Dummy chrome URL used to obtain a valid chrome channel
|
||||||
|
// This one was chosen at random and should be able to be substituted
|
||||||
|
// for any other well known chrome URL in the browser installation
|
||||||
|
const DUMMY_CHROME_URL = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
|
||||||
|
|
||||||
|
|
||||||
|
function ChromeExtensionHandler() {
|
||||||
|
this.wrappedJSObject = this;
|
||||||
|
this._systemPrincipal = null;
|
||||||
|
this._extensions = {};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Report generation extension for Zotero protocol
|
||||||
|
*
|
||||||
|
* Example URLs:
|
||||||
|
*
|
||||||
|
* zotero://report/ -- library
|
||||||
|
* zotero://report/collection/12345
|
||||||
|
* zotero://report/search/12345
|
||||||
|
* zotero://report/items/12345-23456-34567
|
||||||
|
* zotero://report/item/12345
|
||||||
|
*
|
||||||
|
* Optional format can be specified after ids
|
||||||
|
*
|
||||||
|
* - 'html', 'rtf', 'csv'
|
||||||
|
* - defaults to 'html' if not specified
|
||||||
|
*
|
||||||
|
* e.g. zotero://report/collection/12345/rtf
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Sorting:
|
||||||
|
*
|
||||||
|
* - 'sort' query string variable
|
||||||
|
* - format is field[/order] [, field[/order], ...]
|
||||||
|
* - order can be 'asc', 'a', 'desc' or 'd'; defaults to ascending order
|
||||||
|
*
|
||||||
|
* zotero://report/collection/13245?sort=itemType/d,title
|
||||||
|
*/
|
||||||
|
var ReportExtension = new function(){
|
||||||
|
this.newChannel = newChannel;
|
||||||
|
|
||||||
|
function newChannel(uri){
|
||||||
|
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
|
||||||
|
.getService(Components.interfaces.nsIIOService);
|
||||||
|
|
||||||
|
var Zotero = Components.classes["@zotero.org/Zotero;1"]
|
||||||
|
.getService(Components.interfaces.nsISupports)
|
||||||
|
.wrappedJSObject;
|
||||||
|
|
||||||
|
generateContent:try {
|
||||||
|
var mimeType, content = '';
|
||||||
|
|
||||||
|
var [path, queryString] = uri.path.substr(1).split('?');
|
||||||
|
var [type, ids, format] = path.split('/');
|
||||||
|
|
||||||
|
// Get query string variables
|
||||||
|
if (queryString) {
|
||||||
|
var queryVars = queryString.split('&');
|
||||||
|
for (var i in queryVars) {
|
||||||
|
var [key, val] = queryVars[i].split('=');
|
||||||
|
switch (key) {
|
||||||
|
case 'sort':
|
||||||
|
var sortBy = val;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type){
|
||||||
|
case 'collection':
|
||||||
|
var items = Zotero.getItems(ids);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'search':
|
||||||
|
var s = new Zotero.Search(ids);
|
||||||
|
var items = Zotero.Items.get(s.search());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'items':
|
||||||
|
case 'item':
|
||||||
|
var items = Zotero.Items.get(ids.split('-'));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
var type = 'library';
|
||||||
|
var s = new Zotero.Search();
|
||||||
|
s.addCondition('noChildren', 'true');
|
||||||
|
var items = Zotero.Items.get(s.search());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!items){
|
||||||
|
mimeType = 'text/html';
|
||||||
|
content = 'Invalid ID';
|
||||||
|
break generateContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert item objects to export arrays
|
||||||
|
for (var i in items) {
|
||||||
|
items[i] = items[i].toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort items
|
||||||
|
if (!sortBy) {
|
||||||
|
sortBy = 'title';
|
||||||
|
}
|
||||||
|
|
||||||
|
var sorts = sortBy.split(',');
|
||||||
|
for (var i in sorts) {
|
||||||
|
var [field, order] = sorts[i].split('/');
|
||||||
|
switch (order) {
|
||||||
|
case 'd':
|
||||||
|
case 'desc':
|
||||||
|
order = -1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
order = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sorts[i] = {
|
||||||
|
field: field,
|
||||||
|
order: order
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var compareFunction = function(a, b) {
|
||||||
|
var index = 0;
|
||||||
|
|
||||||
|
// Multidimensional sort
|
||||||
|
do {
|
||||||
|
var result = a[sorts[index].field] > b[sorts[index].field] ?
|
||||||
|
sorts[index].order
|
||||||
|
: a[sorts[index].field] < b[sorts[index].field] ?
|
||||||
|
(sorts[index].order * -1)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
while (result == 0 && sorts[index]);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
items.sort(compareFunction);
|
||||||
|
|
||||||
|
|
||||||
|
// Pass off to the appropriate handler
|
||||||
|
switch (format){
|
||||||
|
case 'rtf':
|
||||||
|
mimeType = 'text/rtf';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'csv':
|
||||||
|
mimeType = 'text/plain';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
format = 'html';
|
||||||
|
mimeType = 'application/xhtml+xml';
|
||||||
|
content = Zotero.Report.generateHTMLDetails(items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e){
|
||||||
|
Zotero.debug(e);
|
||||||
|
throw (e);
|
||||||
|
}
|
||||||
|
|
||||||
|
var uri_str = 'data:' + (mimeType ? mimeType + ',' : '') + encodeURIComponent(content);
|
||||||
|
var ext_uri = ioService.newURI(uri_str, null, null);
|
||||||
|
var extChannel = ioService.newChannelFromURI(ext_uri);
|
||||||
|
|
||||||
|
return extChannel;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var ReportExtensionSpec = ZOTERO_SCHEME + "://report"
|
||||||
|
ReportExtensionSpec = ReportExtensionSpec.toLowerCase();
|
||||||
|
|
||||||
|
this._extensions[ReportExtensionSpec] = ReportExtension;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Implements nsIProtocolHandler
|
||||||
|
*/
|
||||||
|
ChromeExtensionHandler.prototype = {
|
||||||
|
scheme: ZOTERO_SCHEME,
|
||||||
|
|
||||||
|
defaultPort : -1,
|
||||||
|
|
||||||
|
protocolFlags : Components.interfaces.nsIProtocolHandler.URI_STD,
|
||||||
|
|
||||||
|
allowPort : function(port, scheme) {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
newURI : function(spec, charset, baseURI) {
|
||||||
|
var newURL = Components.classes["@mozilla.org/network/standard-url;1"]
|
||||||
|
.createInstance(Components.interfaces.nsIStandardURL);
|
||||||
|
newURL.init(1, -1, spec, charset, baseURI);
|
||||||
|
|
||||||
|
return newURL.QueryInterface(Components.interfaces.nsIURI);
|
||||||
|
},
|
||||||
|
|
||||||
|
newChannel : function(uri) {
|
||||||
|
var chromeService = Components.classes["@mozilla.org/network/protocol;1?name=chrome"]
|
||||||
|
.getService(Components.interfaces.nsIProtocolHandler);
|
||||||
|
|
||||||
|
var newChannel = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
var uriString = uri.spec.toLowerCase();
|
||||||
|
|
||||||
|
for (extSpec in this._extensions) {
|
||||||
|
var ext = this._extensions[extSpec];
|
||||||
|
|
||||||
|
if (uriString.indexOf(extSpec) == 0) {
|
||||||
|
|
||||||
|
if (this._systemPrincipal == null) {
|
||||||
|
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
|
||||||
|
.getService(Components.interfaces.nsIIOService);
|
||||||
|
|
||||||
|
var chromeURI = chromeService.newURI(DUMMY_CHROME_URL, null, null);
|
||||||
|
var chromeChannel = chromeService.newChannel(chromeURI);
|
||||||
|
|
||||||
|
this._systemPrincipal = chromeChannel.owner;
|
||||||
|
|
||||||
|
var chromeRequest = chromeChannel.QueryInterface(Components.interfaces.nsIRequest);
|
||||||
|
chromeRequest.cancel(0x804b0002); // BINDING_ABORTED
|
||||||
|
}
|
||||||
|
|
||||||
|
var extChannel = ext.newChannel(uri);
|
||||||
|
|
||||||
|
if (this._systemPrincipal != null) {
|
||||||
|
// applying cached system principal to extension channel
|
||||||
|
extChannel.owner = this._systemPrincipal;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// no cached system principal to apply to extension channel
|
||||||
|
}
|
||||||
|
|
||||||
|
extChannel.originalURI = uri;
|
||||||
|
|
||||||
|
return extChannel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pass request through to ChromeProtocolHandler::newChannel
|
||||||
|
if (uriString.indexOf("chrome") != 0) {
|
||||||
|
uriString = uri.spec;
|
||||||
|
uriString = "chrome" + uriString.substring(uriString.indexOf(":"));
|
||||||
|
uri = chromeService.newURI(uriString, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
newChannel = chromeService.newChannel(uri);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
throw Components.results.NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newChannel;
|
||||||
|
},
|
||||||
|
|
||||||
|
QueryInterface : function(iid) {
|
||||||
|
if (!iid.equals(Components.interfaces.nsIProtocolHandler) &&
|
||||||
|
!iid.equals(Components.interfaces.nsISupports)) {
|
||||||
|
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// XPCOM goop
|
||||||
|
//
|
||||||
|
|
||||||
|
var ChromeExtensionModule = {
|
||||||
|
cid: ZOTERO_PROTOCOL_CID,
|
||||||
|
|
||||||
|
contractId: ZOTERO_PROTOCOL_CONTRACTID,
|
||||||
|
|
||||||
|
registerSelf : function(compMgr, fileSpec, location, type) {
|
||||||
|
compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
|
||||||
|
compMgr.registerFactoryLocation(
|
||||||
|
ZOTERO_PROTOCOL_CID,
|
||||||
|
ZOTERO_PROTOCOL_NAME,
|
||||||
|
ZOTERO_PROTOCOL_CONTRACTID,
|
||||||
|
fileSpec,
|
||||||
|
location,
|
||||||
|
type
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
getClassObject : function(compMgr, cid, iid) {
|
||||||
|
if (!cid.equals(ZOTERO_PROTOCOL_CID)) {
|
||||||
|
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||||
|
}
|
||||||
|
if (!iid.equals(Components.interfaces.nsIFactory)) {
|
||||||
|
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
return this.myFactory;
|
||||||
|
},
|
||||||
|
|
||||||
|
canUnload : function(compMgr) {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
myFactory : {
|
||||||
|
createInstance : function(outer, iid) {
|
||||||
|
if (outer != null) {
|
||||||
|
throw Components.results.NS_ERROR_NO_AGGREGATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ChromeExtensionHandler().QueryInterface(iid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function NSGetModule(compMgr, fileSpec) {
|
||||||
|
return ChromeExtensionModule;
|
||||||
|
}
|
|
@ -58,6 +58,10 @@ Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||||
.getService(Ci.mozIJSSubScriptLoader)
|
.getService(Ci.mozIJSSubScriptLoader)
|
||||||
.loadSubScript("chrome://zotero/content/xpcom/cite.js");
|
.loadSubScript("chrome://zotero/content/xpcom/cite.js");
|
||||||
|
|
||||||
|
Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||||
|
.getService(Ci.mozIJSSubScriptLoader)
|
||||||
|
.loadSubScript("chrome://zotero/content/xpcom/report.js");
|
||||||
|
|
||||||
Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||||
.getService(Ci.mozIJSSubScriptLoader)
|
.getService(Ci.mozIJSSubScriptLoader)
|
||||||
.loadSubScript("chrome://zotero/content/xpcom/utilities.js");
|
.loadSubScript("chrome://zotero/content/xpcom/utilities.js");
|
||||||
|
|
Loading…
Reference in New Issue
Block a user