closes #1099, Add cancel button to Edit Bibliography

closes #744, Select multiple items/entire collection in edit bibliography dialog

- adds "Cancel", "Revert", and "Revert All" buttons to edit bibliography dialog
- permits multiple item selections in edit bibliography dialog
- removing items in the edit bibliography dialog now leaves the citations intact, but removes them from the document
This commit is contained in:
Simon Kornblith 2010-06-28 02:38:48 +00:00
parent 68c4a47bf3
commit 05583b1992
4 changed files with 353 additions and 91 deletions

View file

@ -28,22 +28,31 @@ var Zotero_Bibliography_Dialog = new function () {
var _lastSelectedItemID = false;
var _lastSelectedIndex = false;
var _lastSelectedValue = false;
var _accepted = false;
var _revertButton, _revertAllButton, _addButton, _removeButton;
var _itemList;
var _suppressAllSelectEvents = false;
this.load = load;
this.treeItemSelected = treeItemSelected;
this.listItemSelected = listItemSelected;
this.add = add;
this.remove = remove;
this.accept = accept;
/*
* initialize add citation dialog
/**
* Initializes add citation dialog
*/
function load() {
document.getElementById('editor').format = "RTF";
this.load = function() {
bibEditInterface = window.arguments[0].wrappedJSObject;
_revertAllButton = document.documentElement.getButton("extra2");
_revertButton = document.documentElement.getButton("extra1");
_addButton = document.getElementById("add");
_removeButton = document.getElementById("remove");
_itemList = document.getElementById("item-list");
_itemTree = document.getElementById("zotero-items-tree");
_revertAllButton.label = Zotero.getString("integration.revertAll.button");
_revertAllButton.disabled = bibEditInterface.isAnyEdited();
_revertButton.label = Zotero.getString("integration.revert.button");
_revertButton.disabled = true;
document.getElementById('editor').format = "RTF";
// load (from selectItemsDialog.js)
doLoad();
@ -51,93 +60,230 @@ var Zotero_Bibliography_Dialog = new function () {
_loadItems();
}
/*
* called when an item in the item selection tree is clicked
/**
* Called when an item in the item selection tree is clicked
*/
function treeItemSelected() {
var selectedItems = itemsView.getSelectedItems(true); // treeview from selectItemsDialog.js
this.treeItemSelected = function() {
if(_suppressAllSelectEvents) return;
var selectedItemIDs = itemsView.getSelectedItems(true); // treeview from selectItemsDialog.js
// disable add if item already in itemSet
document.getElementById("add").disabled = selectedItems.length && bibEditInterface.bibliography[0].entry_ids.indexOf(selectedItems[0].id) !== -1;
}
/*
* called when an item in the reference list is clicked
*/
function listItemSelected() {
var selectedListItem = document.getElementById("item-list").getSelectedItem(0);
// enable remove if item is selected
document.getElementById("remove").disabled = !selectedListItem;
if(selectedListItem) {
_updatePreview(selectedListItem.value);
} else {
_updatePreview();
// if all selected items are available in the list box on the right, select them there
// otherwise, clear the list box selection
var clearListItems = false;
var itemsToSelect = [];
if(selectedItemIDs.length) {
for each(var itemID in selectedItemIDs) {
var itemIndexToSelect = false;
for(var i in bibEditInterface.bibliography[0].entry_ids) {
if(bibEditInterface.bibliography[0].entry_ids[i].indexOf(itemID) !== -1) {
itemIndexToSelect = i;
continue;
}
}
if(itemIndexToSelect !== false) {
itemsToSelect.push(_itemList.getItemAtIndex(itemIndexToSelect));
} else {
clearListItems = true;
break;
}
}
}
_suppressAllSelectEvents = true;
_itemList.clearSelection();
if(clearListItems) {
_addButton.disabled = (itemsToSelect.length > 0);
_revertButton.disabled = _removeButton.disabled = true;
} else {
_addButton.disabled = true;
_removeButton.disabled = false;
_updateRevertButtonStatus();
[_itemList.toggleItemSelection(item) for each(item in itemsToSelect)];
_itemList.ensureIndexIsVisible(itemsToSelect[0]);
}
_suppressAllSelectEvents = false;
_updatePreview();
}
/*
* Adds a citation to the reference list
/**
* Called when an item in the reference list is clicked
*/
function add() {
var selectedItem = itemsView.getSelectedItems()[0]; // treeview from selectItemsDialog.js
Zotero.debug(selectedItem);
this.listItemSelected = function() {
if(_suppressAllSelectEvents) return;
bibEditInterface.add(selectedItem.id);
// enable remove if at least one item is selected
_addButton.disabled = true;
_removeButton.disabled = !_itemList.selectedItems.length;
if(_itemList.selectedItems.length) {
_suppressAllSelectEvents = true;
_itemTree.view.selection.clearSelection();
_suppressAllSelectEvents = false;
// only show revert button if at least one selected item has been edited
_updateRevertButtonStatus();
}
// update preview to blank if no items or multiple items are selected; otherwise show
// preview for selected items
_updatePreview();
}
/**
* Adds references to the reference list
*/
this.add = function() {
for each(var itemID in itemsView.getSelectedItems(true)) {
bibEditInterface.add(itemID);
}
document.getElementById("add").disabled = true;
_loadItems();
}
/*
* Deletes a citation from the reference list
/**
* Clears all customizations
*/
function remove() {
var selectedListItem = document.getElementById("item-list").getSelectedItem(0);
var itemID = bibEditInterface.bibliography[0].entry_ids[selectedListItem.value];
this.revertAll = function() {
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
if(bibEditInterface.isCited(itemID)) {
var out = {};
var regenerate = promptService.confirmEx(
window,
Zotero.getString('integration.revertAll.title'),
Zotero.getString('integration.revertAll.body'),
promptService.STD_OK_CANCEL_BUTTONS+promptService.BUTTON_POS_1_DEFAULT,
null, null, null, null, out
);
if(regenerate != 0) return;
bibEditInterface.revertAll();
_loadItems();
_updatePreview(true);
}
/**
* Clears customizations to selected entry
*/
this.revert = function() {
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
var out = {};
var regenerate = promptService.confirmEx(
window,
Zotero.getString('integration.revert.title'),
Zotero.getString('integration.revert.body'),
promptService.STD_OK_CANCEL_BUTTONS+promptService.BUTTON_POS_1_DEFAULT,
null, null, null, null, out
);
if(regenerate != 0) return;
for each(var itemID in _getSelectedListItemIDs()) {
bibEditInterface.revert(itemID);
}
_updatePreview();
}
/**
* Deletes selected references from the reference list
*/
this.remove = function() {
var selectedListItemIDs = _getSelectedListItemIDs();
// if cited in bibliography, warn before removing
var isCited = false;
for each(var itemID in selectedListItemIDs) {
isCited |= bibEditInterface.isCited(itemID);
}
if(isCited) {
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
var out = {};
var regenerate = promptService.confirmEx(
window,
Zotero.getString('integration.deleteCitedItem.title'),
Zotero.getString('integration.deleteCitedItem.body'),
Zotero.getString('integration.removeBibEntry.title'),
Zotero.getString('integration.removeBibEntry.body'),
promptService.STD_OK_CANCEL_BUTTONS+promptService.BUTTON_POS_1_DEFAULT,
null, null, null, null, out
);
if(regenerate != 0) return;
}
bibEditInterface.remove(itemID);
// remove
for each(var itemID in selectedListItemIDs) {
bibEditInterface.remove(itemID);
}
_loadItems();
}
function accept() {
_updatePreview();
/**
* Called when the user edits the currently selected bibliography entry
*/
this.textChanged = function() {
_revertButton.disabled = _revertAllButton.disabled = false;
}
/*
/**
* Called when OK button is pressed
*/
this.accept = function() {
_accepted = true;
_updatePreview(true);
}
/**
* Called when Cancel button is pressed
*/
this.close = function() {
if(!_accepted) bibEditInterface.cancel();
}
/**
* Gets selected item IDs from list box on right
*/
function _getSelectedListItemIDs() {
return [bibEditInterface.bibliography[0].entry_ids[item.value][0]
for each(item in _itemList.selectedItems)];
}
/**
* Update status of "Revert" button to match modification status of current item
*/
function _updateRevertButtonStatus() {
_revertButton.disabled = true;
var selectedListItemIDs = _getSelectedListItemIDs();
for each(var itemID in selectedListItemIDs) {
if(bibEditInterface.isEdited(itemID)) {
_revertButton.disabled = false;
break;
}
}
}
/**
* Updates the contents of the preview pane
*/
function _updatePreview(index) {
Zotero.debug("_updatePreview called");
function _updatePreview(ignoreSelection) {
var index = !ignoreSelection && _itemList.selectedItems.length == 1 ? _itemList.selectedIndex : undefined;
var editor = document.getElementById('editor');
if(_lastSelectedItemID) {
var newValue = editor.value;
if(_lastSelectedValue != newValue) {
Zotero.debug("setting bibliography for "+_lastSelectedItemID+" to "+newValue);
bibEditInterface.setCustomText(_lastSelectedItemID, newValue);
}
}
editor.readonly = index === undefined;
if(index !== undefined) {
Zotero.debug("updating preview of "+index);
var itemID = bibEditInterface.bibliography[0].entry_ids[index];
editor.value = bibEditInterface.bibliography[1][index];
_lastSelectedIndex = index;
@ -147,6 +293,8 @@ var Zotero_Bibliography_Dialog = new function () {
editor.value = "";
_lastSelectedIndex = _lastSelectedItemID = _lastSelectedValue = false;
}
_revertAllButton.disabled = !bibEditInterface.isAnyEdited();
}
/*

View file

@ -35,13 +35,17 @@
title="&zotero.integration.editBibliography.title;"
width="750" height="450"
onload="Zotero_Bibliography_Dialog.load();"
onunload="doUnload();"
ondialogaccept="Zotero_Bibliography_Dialog.accept();"
buttons="accept" buttonpack="end"
ondialogcancel="Zotero_Bibliography_Dialog.close();"
onclose="Zotero_Bibliography_Dialog.close();"
onunload="doUnload();"
buttons="extra1,extra2,accept,cancel" buttonpack="end"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
persist="screenX screenY width height"
resizable="true">
resizable="true"
ondialogextra1="Zotero_Bibliography_Dialog.revert()"
ondialogextra2="Zotero_Bibliography_Dialog.revertAll()">
<script src="../include.js"/>
<script src="../selectItemsDialog.js"/>
@ -72,7 +76,7 @@
</tree>
<tree id="zotero-items-tree"
flex="1" hidecolumnpicker="true" seltype="single"
flex="1" hidecolumnpicker="true" seltype="multiple"
onselect="Zotero_Bibliography_Dialog.treeItemSelected();">
<treecols>
<treecol
@ -101,14 +105,15 @@
</vbox>
<hbox id="source-list">
<vbox align="center" pack="center">
<vbox align="center" pack="center" id="citation-buttons">
<toolbarbutton id="add" oncommand="Zotero_Bibliography_Dialog.add()" disabled="true"/>
<toolbarbutton id="remove" oncommand="Zotero_Bibliography_Dialog.remove()" disabled="true"/>
</vbox>
<vbox>
<label value="&zotero.integration.references.label;"/>
<listbox id="item-list" flex="1" align="stretch" seltype="single"
style="width: 250px;" onselect="Zotero_Bibliography_Dialog.listItemSelected();"/>
<listbox id="item-list" flex="1" align="stretch" seltype="multiple"
style="width: 250px;" onselect="Zotero_Bibliography_Dialog.listItemSelected();"
onchanged="Zotero_Bibliography_Dialog.textChanged()"/>
</vbox>
</hbox>
</hbox>

View file

@ -215,9 +215,27 @@ Zotero.Cite.System.getAbbreviations = function() {
return {};
}
Zotero.Cite.makeFormattedBibliography = function(cslEngine, format, customBibliographyText) {
Zotero.Cite.removeFromBibliography = function(bib, itemsToRemove) {
var removeItems = [];
for(let i in bib[0].entry_ids) {
for(let j in bib[0].entry_ids[i]) {
if(itemsToRemove[bib[0].entry_ids[i][j]]) {
removeItems.push(i);
break;
}
}
}
for(let i=removeItems.length-1; i>=0; i--) {
bib[0].entry_ids.splice(removeItems[i], 1);
bib[1].splice(removeItems[i], 1);
}
}
Zotero.Cite.makeFormattedBibliography = function(cslEngine, format, customBibliographyText, omittedItems) {
if(format) cslEngine.setOutputFormat(format);
var bib = cslEngine.makeBibliography();
if(omittedItems) this.removeFromBibliography(bib, omittedItems);
if(format == "html") {
// TODO CSS
return bib[0].bibstart+bib[1].join("")+bib[0].bibend;

View file

@ -596,7 +596,10 @@ Zotero.Integration.Document.prototype._updateSession = function(newField, editFi
// if we are reloading this session, assume no item IDs to be updated except for edited items
if(this._reloadSession) {
this._session.restoreProcessorState();
//this._session.restoreProcessorState(); TODO doesn't appear to be working properly
this._session.updateUpdateIndices();
var deleteCitations = this._session.updateCitations();
this._deleteFields = this._deleteFields.concat([i for(i in deleteCitations)]);
this._session.updateIndices = {};
this._session.updateItemIDs = {};
this._session.bibliographyHasChanged = false;
@ -627,7 +630,7 @@ Zotero.Integration.Document.prototype._updateSession = function(newField, editFi
*/
Zotero.Integration.Document.prototype._updateDocument = function(forceCitations, forceBibliography) {
// update citations
this._session.updateUpdateIndices(forceCitations);
this._session.updateUpdateIndices();
var deleteCitations = this._session.updateCitations();
this._deleteFields = this._deleteFields.concat([i for(i in deleteCitations)]);
for(var i in this._session.updateIndices) {
@ -658,7 +661,7 @@ Zotero.Integration.Document.prototype._updateDocument = function(forceCitations,
if(this._bibliographyFields.length // if blbliography exists
&& (this._session.bibliographyHasChanged // and bibliography changed
|| forceBibliography)) { // or if we should generate regardless of changes
if(this._session.bibliographyDataHasChanged) {
if(forceBibliography || this._session.bibliographyDataHasChanged) {
var bibliographyData = this._session.getBibliographyData();
for each(var field in this._bibliographyFields) {
field.setCode(BIBLIOGRAPHY_CODE+" "+bibliographyData);
@ -865,6 +868,7 @@ Zotero.Integration.Document.JSEnumerator.prototype.getNext = function() {
Zotero.Integration.Session = function() {
// holds items not in document that should be in bibliography
this.uncitedItems = new Object();
this.omittedItems = new Object();
this.customBibliographyText = new Object();
this.reselectedItems = new Object();
this.citationIDs = new Object();
@ -1280,13 +1284,16 @@ Zotero.Integration.Session.prototype.deleteCitation = function(index) {
*/
Zotero.Integration.Session.prototype.getBibliography = function() {
// use real RTF, but chop off the first \n
return Zotero.Cite.makeFormattedBibliography(this.style, "rtf", this.customBibliographyText);
this.updateUncitedItems();
return Zotero.Cite.makeFormattedBibliography(this.style, "rtf", this.customBibliographyText, this.omittedItems);
}
/**
* Calls CSL.Engine.updateUncitedItems() to reconcile list of uncited items
*/
Zotero.Integration.Session.prototype.updateUncitedItems = function() {
// There appears to be a bug somewhere here.
Zotero.debug("UPDATING UNCITED ITEMS WITH "+this.uncitedItems.toSource());
this.style.updateUncitedItems([parseInt(i) for(i in this.uncitedItems)]);
}
@ -1414,7 +1421,7 @@ Zotero.Integration.Session.prototype.updateCitations = function() {
}
/**
* Updates the list of citations to be serialized to the document
* Restores processor state from document, without requesting citation updates
*/
Zotero.Integration.Session.prototype.restoreProcessorState = function() {
var citations = [];
@ -1423,7 +1430,6 @@ Zotero.Integration.Session.prototype.restoreProcessorState = function() {
citations.push(this.citationsByIndex[i]);
}
}
this.style.restoreProcessorState(citations);
}
@ -1441,15 +1447,21 @@ Zotero.Integration.Session.prototype.loadBibliographyData = function(json) {
}
}
var needUpdate;
// set uncited
if(documentData.uncited) {
if(documentData.uncited[0]) {
// new style array of arrays with URIs
var zoteroItem, needUpdate;
let zoteroItem, needUpdate;
for each(var uris in documentData.uncited) {
[zoteroItem, needUpdate] = this.uriMap.getZoteroItemForURIs(uris);
if(zoteroItem) this.uncitedItems[zoteroItem.id] = true;
if(needUpdate) this.bibliographyDataHasChanged = true;
[zoteroItem, update] = this.uriMap.getZoteroItemForURIs(uris);
if(zoteroItem && !this.citationsByItemID[zoteroItem.id]) {
this.uncitedItems[zoteroItem.id] = true;
} else {
needUpdate = true;
}
this.bibliographyDataHasChanged |= needUpdate;
}
} else {
for(var itemID in documentData.uncited) {
@ -1493,6 +1505,20 @@ Zotero.Integration.Session.prototype.loadBibliographyData = function(json) {
}
}
// set entries to be omitted from bibliography
if(documentData.omitted) {
let zoteroItem, needUpdate;
for each(var uris in documentData.omitted) {
[zoteroItem, update] = this.uriMap.getZoteroItemForURIs(uris);
if(zoteroItem && this.citationsByItemID[zoteroItem.id]) {
this.omittedItems[zoteroItem.id] = true;
} else {
needUpdate = true;
}
this.bibliographyDataHasChanged |= needUpdate;
}
}
this.bibliographyData = json;
}
@ -1509,6 +1535,12 @@ Zotero.Integration.Session.prototype.getBibliographyData = function() {
bibliographyData.uncited.push(this.uriMap.getURIsForItemID(item));
}
}
for(var item in this.omittedItems) {
if(item) {
if(!bibliographyData.omitted) bibliographyData.omitted = [];
bibliographyData.omitted.push(this.uriMap.getURIsForItemID(item));
}
}
// look for custom bibliography entries
bibliographyData.custom = [[this.uriMap.getURIsForItemID(id), this.customBibliographyText[id]]
@ -1609,6 +1641,18 @@ Zotero.Integration.Session.prototype.editBibliography = function() {
*/
Zotero.Integration.Session.BibliographyEditInterface = function(session) {
this.session = session;
this._changed = {
"customBibliographyText":{},
"uncitedItems":{},
"omittedItems":{}
}
for(var list in this._changed) {
for(var key in this.session[list]) {
this._changed[list][key] = this.session[list][key];
}
}
this._update();
}
@ -1619,18 +1663,70 @@ Zotero.Integration.Session.BibliographyEditInterface.prototype._update = functio
this.session.updateUncitedItems();
this.session.style.setOutputFormat("rtf");
this.bibliography = this.session.style.makeBibliography();
Zotero.Cite.removeFromBibliography(this.bibliography, this.session.omittedItems);
for(var i in this.bibliography[0].entry_ids) {
if(this.session.customBibliographyText[this.bibliography[0].entry_ids[i]]) {
this.bibliography[1][i] = this.session.customBibliographyText[this.bibliography[0].entry_ids[i]];
if(this.bibliography[0].entry_ids[i].length != 1) continue;
var itemID = this.bibliography[0].entry_ids[i][0];
if(this.session.customBibliographyText[itemID]) {
this.bibliography[1][i] = this.session.customBibliographyText[itemID];
}
}
}
/**
* Checks whether an item ID is cited in the bibliography being edited
* Reverts the text of an individual bibliography entry
*/
Zotero.Integration.Session.BibliographyEditInterface.prototype.revert = function(itemID) {
delete this.session.customBibliographyText[itemID];
this._update();
}
/**
* Reverts bibliography to condition in which no edits have been made
*/
Zotero.Integration.Session.BibliographyEditInterface.prototype.revertAll = function() {
for(var list in this._changed) {
this.session[list] = {};
}
this._update();
}
/**
* Reverts bibliography to condition before BibliographyEditInterface was opened
* Does not run _update automatically, since this will usually only happen with a cancel request
*/
Zotero.Integration.Session.BibliographyEditInterface.prototype.cancel = function() {
for(var list in this._changed) {
this.session[list] = this._changed[list];
}
this.session.updateUncitedItems();
}
/**
* Checks whether a given reference is cited within the main document text
*/
Zotero.Integration.Session.BibliographyEditInterface.prototype.isCited = function(item) {
if(this.session.citationsByItemID[item]) return true;
}
/**
* Checks whether an item ID is cited in the bibliography being edited
*/
Zotero.Integration.Session.BibliographyEditInterface.prototype.isEdited = function(itemID) {
if(this.session.customBibliographyText[itemID]) return true;
return false;
}
/**
* Checks whether any citations in the bibliography have been edited
*/
Zotero.Integration.Session.BibliographyEditInterface.prototype.isAnyEdited = function() {
for(var list in this._changed) {
for(var a in this.session[list]) {
return true;
}
}
return false;
}
@ -1638,8 +1734,11 @@ Zotero.Integration.Session.BibliographyEditInterface.prototype.isCited = functio
* Adds an item to the bibliography
*/
Zotero.Integration.Session.BibliographyEditInterface.prototype.add = function(itemID) {
// create new item
this.session.uncitedItems[itemID] = true;
if(this.session.omittedItems[itemID]) {
delete this.session.omittedItems[itemID];
} else {
this.session.uncitedItems[itemID] = true;
}
this._update();
}
@ -1647,19 +1746,11 @@ Zotero.Integration.Session.BibliographyEditInterface.prototype.add = function(it
* Removes an item from the bibliography being edited
*/
Zotero.Integration.Session.BibliographyEditInterface.prototype.remove = function(itemID) {
// delete citations if necessary
if(this.session.citationsByItemID[itemID]) {
for(var j=0; j<this.session.citationsByItemID[itemID].length; j++) {
var citation = this.session.citationsByItemID[itemID][j];
this.session.updateIndices[citation.properties.index] = true;
citation.properties["delete"] = true;
}
delete this.session.citationsByItemID[itemID];
this.session.updateCitations();
if(this.session.uncitedItems[itemID]) {
delete this.session.uncitedItems[itemID];
} else {
this.session.omittedItems[itemID] = true;
}
// delete uncited if neceessary
if(this.session.uncitedItems[itemID]) delete this.session.uncitedItems[itemID];
this._update();
}