Allow citing embedded items and editing citations containing embedded items

This commit is contained in:
Simon Kornblith 2011-12-19 03:38:58 -05:00
parent 878f70998f
commit f81f0d7143
5 changed files with 288 additions and 111 deletions

View File

@ -254,13 +254,19 @@ var Zotero_QuickFormat = new function () {
break;
}
}
if(!mismatch) return;
if(!mismatch) {
_resize();
return;
}
}
curIDs = searchResultIDs;
// Check to see which search results match items already in the document
var citedItems, completed = false, preserveSelection = false;
var citedItems, completed = false, isAsync = false;
io.getItems(function(citedItems) {
// Don't do anything if panel is already closed
if(isAsync && referencePanel.state !== "open" && referencePanel.state !== "showing") return;
completed = true;
if(str.toLowerCase() === Zotero.getString("integration.ibid").toLowerCase()) {
@ -290,14 +296,14 @@ var Zotero_QuickFormat = new function () {
Zotero.debug("Searched cited items");
}
_updateItemList(citedItemsMatchingSearch, searchResultIDs, preserveSelection);
_updateItemList(citedItemsMatchingSearch, searchResultIDs, isAsync);
});
if(!completed) {
// We are going to have to wait until items have been retrieved from the document.
// Until then, show item list without cited items.
_updateItemList(false, searchResultIDs);
preserveSelection = true;
isAsync = true;
}
} else {
// No search conditions, so just clear the box
@ -378,7 +384,7 @@ var Zotero_QuickFormat = new function () {
referenceBox.appendChild(_buildListItem(item));
previousLibrary = libraryID;
if(preserveSelection && item.id === previousItemID) {
if(preserveSelection && (item.cslItemID ? item.cslItemID : item.id) === previousItemID) {
selectedIndex = referenceBox.childNodes.length-1;
}
}
@ -390,7 +396,6 @@ var Zotero_QuickFormat = new function () {
referenceBox.ensureIndexIsVisible(selectedIndex);
}
}
/**
* Builds a string describing an item. We avoid CSL here for speed.
@ -480,7 +485,7 @@ var Zotero_QuickFormat = new function () {
rll.setAttribute("orient", "vertical");
rll.setAttribute("flex", "1");
rll.setAttribute("class", "quick-format-item");
rll.setAttribute("zotero-item", item.id);
rll.setAttribute("zotero-item", item.cslItemID ? item.cslItemID : item.id);
rll.appendChild(titleNode);
rll.appendChild(infoNode);
rll.addEventListener("click", _bubbleizeSelected, false);
@ -515,7 +520,7 @@ var Zotero_QuickFormat = new function () {
* Builds the string to go inside a bubble
*/
function _buildBubbleString(citationItem) {
var item = Zotero.Items.get(citationItem.id);
var item = Zotero.Cite.getItem(citationItem.id);
// create text for bubble
var title, delimiter;
var str = item.getField("firstCreator");
@ -577,6 +582,11 @@ var Zotero_QuickFormat = new function () {
if(!referenceBox.hasChildNodes() || !referenceBox.selectedItem) return false;
var citationItem = {"id":referenceBox.selectedItem.getAttribute("zotero-item")};
if(typeof citationItem.id === "string" && citationItem.id.indexOf("/") !== -1) {
var item = Zotero.Cite.getItem(citationItem.id);
citationItem.uris = item.cslURIs;
citationItem.itemData = item.cslItemData;
}
if(curLocator) {
citationItem["locator"] = curLocator;
if(curLocatorLabel) {
@ -781,7 +791,7 @@ var Zotero_QuickFormat = new function () {
locator.value = target.citationItem["locator"] ? target.citationItem["locator"] : "";
suppressAuthor.checked = !!target.citationItem["suppress-author"];
var item = Zotero.Items.get(target.citationItem.id);
var item = Zotero.Cite.getItem(target.citationItem.id);
document.getElementById("citation-properties-title").textContent = item.getDisplayTitle();
while(info.hasChildNodes()) info.removeChild(info.firstChild);
_buildItemDescription(item, info);

View File

@ -390,6 +390,35 @@ Zotero.Cite.makeFormattedBibliography = function(cslEngine, format) {
}
}
/**
* Get an item by ID, either by retrieving it from the library or looking for the document it
* belongs to.
* @param {String|Number|Array} id
*/
Zotero.Cite.getItem = function(id) {
var slashIndex;
if(id instanceof Array) {
return [Zotero.Cite.getItem(anId) for each(anId in id)];
} else if(typeof id === "string" && (slashIndex = id.indexOf("/")) !== -1) {
var sessionID = id.substr(0, slashIndex),
session = Zotero.Integration.sessions[sessionID],
item;
if(session) {
item = session.embeddedZoteroItems[id.substr(slashIndex+1)];
}
if(!item) {
item = new Zotero.Item("document");
item.setField("title", "Missing Item");
Zotero.log("CSL item "+id+" not found");
}
return item;
} else {
return Zotero.Items.get(id);
}
}
Zotero.Cite.labels = ["page", "book", "chapter", "column", "figure", "folio",
"issue", "line", "note", "opus", "paragraph", "part", "section", "sub verbo",
"volume", "verse"];

View File

@ -183,7 +183,19 @@ Zotero.Item.prototype.getField = function(field, unformatted, includeBaseMapped)
this.loadPrimaryData(true);
}
if (field == 'id' || Zotero.Items.isPrimaryField(field)) {
if (field === 'firstCreator' && !this.id) {
// Hack to get a firstCreator for an unsaved item
var creators = this.getCreators();
if(creators.length === 0) {
return "";
} else if(creators.length === 1) {
return creators[0].ref.lastName;
} else if(creators.length === 2) {
return creators[0].ref.lastName+" "+Zotero.getString('general.and')+" "+creators[1].ref.lastName;
} else if(creators.length > 3) {
return creators[0].ref.lastName+" et al."
}
} else if (field === 'id' || Zotero.Items.isPrimaryField(field)) {
var privField = '_' + field;
//Zotero.debug('Returning ' + (this[privField] ? this[privField] : '') + ' (typeof ' + typeof this[privField] + ')');
return this[privField];

View File

@ -1274,7 +1274,7 @@ Zotero.Integration.Fields.prototype._updateDocument = function(forceCitations, f
* Brings up the addCitationDialog, prepopulated if a citation is provided
*/
Zotero.Integration.Fields.prototype.addEditCitation = function(field, callback) {
var newField, citation, fieldIndex, session = this._session, me = this;
var newField, citation, fieldIndex, session = this._session, me = this, loadFirst;
// if there's already a citation, make sure we have item IDs in addition to keys
if(field) {
@ -1285,18 +1285,13 @@ Zotero.Integration.Fields.prototype.addEditCitation = function(field, callback)
}
citation = session.unserializeCitation(content);
var zoteroItem;
for each(var citationItem in citation.citationItems) {
var item = false;
if(!citationItem.id) {
zoteroItem = false;
if(citationItem.uris) {
[zoteroItem, ] = session.uriMap.getZoteroItemForURIs(citationItem.uris);
} else if(citationItem.key) {
zoteroItem = Zotero.Items.getByKey(citationItem.key);
}
if(zoteroItem) citationItem.id = zoteroItem.id;
try {
session.lookupItems(citation);
} catch(e) {
if(e instanceof MissingItemException) {
citation.citationItems = [];
} else {
throw e;
}
}
@ -1471,9 +1466,8 @@ Zotero.Integration.CitationEditInterface.prototype = {
* has already been updated if it should be.
*/
"_getItems":function(itemsCallback) {
// TODO handle items not in library
var citationsByItemID = this._session.citationsByItemID;
var items = [itemID for(itemID in citationsByItemID)
var ids = [itemID for(itemID in citationsByItemID)
if(citationsByItemID[itemID] && citationsByItemID[itemID].length
// Exclude this item
&& (citationsByItemID[itemID].length > 1
@ -1481,7 +1475,7 @@ Zotero.Integration.CitationEditInterface.prototype = {
// Sort all previously cited items at top, and all items cited later at bottom
var fieldIndex = this._fieldIndex;
items.sort(function(a, b) {
ids.sort(function(a, b) {
var indexA = citationsByItemID[a][0].properties.zoteroIndex,
indexB = citationsByItemID[b][0].properties.zoteroIndex;
@ -1494,7 +1488,7 @@ Zotero.Integration.CitationEditInterface.prototype = {
return indexB - indexA;
});
itemsCallback(Zotero.Items.get(items));
itemsCallback(Zotero.Cite.getItem(ids));
}
}
@ -1506,6 +1500,7 @@ Zotero.Integration.Session = function(doc) {
this.uncitedItems = {};
this.omittedItems = {};
this.embeddedItems = {};
this.embeddedZoteroItems = {};
this.embeddedItemsByURI = {};
this.customBibliographyText = {};
this.reselectedItems = {};
@ -1779,89 +1774,7 @@ Zotero.Integration.Session.prototype.addCitation = function(index, noteIndex, ar
}
// get items
for(var i=0, n=citation.citationItems.length; i<n; i++) {
var citationItem = citation.citationItems[i];
// get Zotero item
var zoteroItem = false;
if(citationItem.uris) {
[zoteroItem, needUpdate] = this.uriMap.getZoteroItemForURIs(citationItem.uris);
if(needUpdate) this.updateIndices[index] = true;
} else {
if(citationItem.key) {
zoteroItem = Zotero.Items.getByKey(citationItem.key);
} else if(citationItem.itemID) {
zoteroItem = Zotero.Items.get(citationItem.itemID);
} else if(citationItem.id) {
zoteroItem = Zotero.Items.get(citationItem.id);
}
if(zoteroItem) this.updateIndices[index] = true;
}
// if no item, check if it was already reselected and otherwise handle as a missing item
if(!zoteroItem) {
if(citationItem.uris) {
var reselectKeys = citationItem.uris;
var reselectKeyType = RESELECT_KEY_URI;
} else if(citationItem.key) {
var reselectKeys = [citationItem.key];
var reselectKeyType = RESELECT_KEY_ITEM_KEY;
} else if(citationItem.id) {
var reselectKeys = [citationItem.id];
var reselectKeyType = RESELECT_KEY_ITEM_ID;
} else {
var reselectKeys = [citationItem.itemID];
var reselectKeyType = RESELECT_KEY_ITEM_ID;
}
// look to see if item has already been reselected
for each(var reselectKey in reselectKeys) {
if(this.reselectedItems[reselectKey]) {
zoteroItem = Zotero.Items.get(this.reselectedItems[reselectKey]);
citationItem.id = zoteroItem.id;
this.updateIndices[index] = true;
break;
}
}
if(!zoteroItem) {
// check embedded items
if(citationItem.uris) {
var success = false;
for(var j=0, m=citationItem.uris.length; j<m; j++) {
var embeddedItem = this.embeddedItemsByURI[citationItem.uris[j]];
if(embeddedItem) {
citationItem.id = this.data.sessionID+"/"+embeddedItem.id;
success = true;
break;
}
}
if(success) continue;
}
if(citationItem.itemData) {
// add new embedded item
var itemData = Zotero.Utilities.deepCopy(citationItem.itemData);
for(var j=0, m=citationItem.uris.length; j<m; j++) {
this.embeddedItemsByURI[citationItem.uris[j]] = itemData;
}
// assign a random string as an item ID
var anonymousID = itemData.id = Zotero.randomString();
this.embeddedItems[anonymousID] = itemData;
citationItem.id = this.data.sessionID+"/"+anonymousID;
} else {
// if not already reselected, throw a MissingItemException
throw(new Zotero.Integration.MissingItemException(
reselectKeys, reselectKeyType, i, citation.citationItems.length));
}
}
}
if(zoteroItem) {
citationItem.id = zoteroItem.id;
}
}
this.lookupItems(citation, index);
citation.properties.added = true;
citation.properties.zoteroIndex = index;
@ -1900,6 +1813,104 @@ Zotero.Integration.Session.prototype.addCitation = function(index, noteIndex, ar
this.citationIDs[citation.citationID] = true;
}
/**
* Looks up item IDs to correspond with keys or generates embedded items for given citation object.
* Throws a MissingItemException if item was not found.
*/
Zotero.Integration.Session.prototype.lookupItems = function(citation, index) {
for(var i=0, n=citation.citationItems.length; i<n; i++) {
var citationItem = citation.citationItems[i];
// get Zotero item
var zoteroItem = false;
if(citationItem.cslItemID) {
} else if(citationItem.uris) {
[zoteroItem, needUpdate] = this.uriMap.getZoteroItemForURIs(citationItem.uris);
if(needUpdate && index) this.updateIndices[index] = true;
} else {
if(citationItem.key) {
zoteroItem = Zotero.Items.getByKey(citationItem.key);
} else if(citationItem.itemID) {
zoteroItem = Zotero.Items.get(citationItem.itemID);
} else if(citationItem.id) {
zoteroItem = Zotero.Items.get(citationItem.id);
}
if(zoteroItem && index) this.updateIndices[index] = true;
}
// if no item, check if it was already reselected and otherwise handle as a missing item
if(!zoteroItem) {
if(citationItem.uris) {
var reselectKeys = citationItem.uris;
var reselectKeyType = RESELECT_KEY_URI;
} else if(citationItem.key) {
var reselectKeys = [citationItem.key];
var reselectKeyType = RESELECT_KEY_ITEM_KEY;
} else if(citationItem.id) {
var reselectKeys = [citationItem.id];
var reselectKeyType = RESELECT_KEY_ITEM_ID;
} else {
var reselectKeys = [citationItem.itemID];
var reselectKeyType = RESELECT_KEY_ITEM_ID;
}
// look to see if item has already been reselected
for each(var reselectKey in reselectKeys) {
if(this.reselectedItems[reselectKey]) {
zoteroItem = Zotero.Items.get(this.reselectedItems[reselectKey]);
citationItem.id = zoteroItem.id;
if(index) this.updateIndices[index] = true;
break;
}
}
if(!zoteroItem) {
// check embedded items
if(citationItem.uris) {
var success = false;
for(var j=0, m=citationItem.uris.length; j<m; j++) {
var embeddedItem = this.embeddedItemsByURI[citationItem.uris[j]];
if(embeddedItem) {
citationItem.id = embeddedItem.id;
success = true;
break;
}
}
if(success) continue;
}
if(citationItem.itemData) {
// add new embedded item
var itemData = Zotero.Utilities.deepCopy(citationItem.itemData);
for(var j=0, m=citationItem.uris.length; j<m; j++) {
this.embeddedItemsByURI[citationItem.uris[j]] = itemData;
}
// assign a random string as an item ID
var anonymousID = Zotero.randomString();
var globalID = itemData.id = citationItem.id = this.data.sessionID+"/"+anonymousID;
this.embeddedItems[anonymousID] = itemData;
var surrogateItem = this.embeddedZoteroItems[anonymousID] = new Zotero.Item();
Zotero.Utilities.itemFromCSLJSON(surrogateItem, itemData);
surrogateItem.cslItemID = globalID;
surrogateItem.cslURIs = citationItem.uris;
surrogateItem.cslItemData = itemData;
} else {
// if not already reselected, throw a MissingItemException
throw(new Zotero.Integration.MissingItemException(
reselectKeys, reselectKeyType, i, citation.citationItems.length));
}
}
}
if(zoteroItem) {
citationItem.id = zoteroItem.id;
}
}
}
/**
* Unserializes a JSON citation into a citation object (sans items)
*/
@ -2706,3 +2717,7 @@ Zotero.Integration.URIMap.prototype.getZoteroItemForURIs = function(uris) {
return [zoteroItem, needUpdate];
}
/**
*
*/

View File

@ -33,9 +33,9 @@
*/
const CSL_NAMES_MAPPINGS = {
"author":"author",
"editor":"editor",
"bookAuthor":"container-author",
"composer":"composer",
"editor":"editor",
"interviewer":"interviewer",
"recipient":"recipient",
"seriesEditor":"collection-editor",
@ -1194,6 +1194,8 @@ Zotero.Utilities = {
/**
* Converts an item from toArray() format to citeproc-js JSON
* @param {Zotero.Item} item
* @return {Object} The CSL item
*/
"itemToCSLJSON":function(item) {
if(item instanceof Zotero.Item) {
@ -1298,5 +1300,114 @@ Zotero.Utilities = {
//this._cache[item.id] = cslItem;
return cslItem;
},
/**
* Converts an item in CSL JSON format to a Zotero tiem
* @param {Zotero.Item} item
* @param {Object} cslItem
*/
"itemFromCSLJSON":function(item, cslItem) {
var isZoteroItem = item instanceof Zotero.Item, zoteroType;
for(var type in CSL_TYPE_MAPPINGS) {
if(CSL_TYPE_MAPPINGS[zoteroType] == item.type) {
zoteroType = type;
break;
}
}
if(!zoteroType) zoteroType = "document";
var itemTypeID = Zotero.ItemTypes.getID(zoteroType);
if(isZoteroItem) {
item.setType(itemTypeID);
} else {
item.itemID = cslItem.id;
item.itemType = zoteroType;
}
// map text fields
for(var variable in CSL_TEXT_MAPPINGS) {
if(variable in cslItem) {
for each(var field in CSL_TEXT_MAPPINGS[variable]) {
var fieldID = Zotero.ItemFields.getID(field);
if(Zotero.ItemFields.isBaseField(fieldID)) {
var newFieldID = Zotero.ItemFields.getFieldIDFromTypeAndBase(itemTypeID, fieldID);
if(newFieldID) fieldID = newFieldID;
}
if(Zotero.ItemFields.isValidForType(fieldID, itemTypeID)) {
if(isZoteroItem) {
item.setField(fieldID, cslItem[variable], true);
} else {
item[field] = cslItem[variable];
}
}
}
}
}
// separate name variables
for(var field in CSL_NAMES_MAPPINGS) {
if(CSL_NAMES_MAPPINGS[field] in cslItem) {
var creatorTypeID = Zotero.CreatorTypes.getID(field);
if(!Zotero.CreatorTypes.isValidForItemType(creatorTypeID, itemTypeID)) {
creatorTypeID = Zotero.CreatorTypes.getPrimaryIDForType(itemTypeID);
}
for each(var cslAuthor in cslItem[CSL_NAMES_MAPPINGS[field]]) {
var creator = isZoteroItem ? new Zotero.Creator() : {};
if(cslAuthor.family || cslAuthor.given) {
if(cslAuthor.family) creator.lastName = cslAuthor.family;
if(cslAuthor.given) creator.firstName = cslAuthor.given;
} else if(cslAuthor.literal) {
creator.lastName = cslAuthor.literal;
creator.fieldMode = 1;
} else {
continue;
}
if(isZoteroItem) {
item.setCreator(item.getCreators().length, creator, creatorTypeID);
} else {
creator.creatorType = Zotero.CreatorTypes.getName(creatorTypeID);
item.creators.push(creator);
}
}
}
}
// get date variables
for(var variable in CSL_DATE_MAPPINGS) {
if(variable in cslItem) {
var field = CSL_DATE_MAPPINGS[variable],
fieldID = Zotero.ItemFields.getID(field),
cslDate = cslItem[variable];
var fieldID = Zotero.ItemFields.getID(field);
if(Zotero.ItemFields.isBaseField(fieldID)) {
var newFieldID = Zotero.ItemFields.getFieldIDFromTypeAndBase(itemTypeID, fieldID);
if(newFieldID) fieldID = newFieldID;
}
if(Zotero.ItemFields.isValidForType(fieldID, itemTypeID)) {
var date = "";
if(cslDate.literal) {
date = cslDate.literal;
} else if(cslDate.year) {
if(cslDate.month) cslDate.month--;
date = Zotero.Date.formatDate(cslDate);
if(cslDate.season) date = cslDate.season+date;
}
Zotero.debug(date);
if(isZoteroItem) {
item.setField(fieldID, date);
} else {
item[field] = date;
}
}
}
}
}
}