commit f81f0d714322648deb480e0fda95a0ff6d8f267b
parent 878f70998f9024d9f3c1239cdaf3193bb92acc33
Author: Simon Kornblith <simon@simonster.com>
Date: Mon, 19 Dec 2011 03:38:58 -0500
Allow citing embedded items and editing citations containing embedded items
Diffstat:
5 files changed, 249 insertions(+), 71 deletions(-)
diff --git a/chrome/content/zotero/integration/quickFormat.js b/chrome/content/zotero/integration/quickFormat.js
@@ -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);
diff --git a/chrome/content/zotero/xpcom/cite.js b/chrome/content/zotero/xpcom/cite.js
@@ -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"];
\ No newline at end of file
diff --git a/chrome/content/zotero/xpcom/data/item.js b/chrome/content/zotero/xpcom/data/item.js
@@ -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];
diff --git a/chrome/content/zotero/xpcom/integration.js b/chrome/content/zotero/xpcom/integration.js
@@ -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,14 +1774,60 @@ Zotero.Integration.Session.prototype.addCitation = function(index, noteIndex, ar
}
// get items
+ this.lookupItems(citation, index);
+
+ citation.properties.added = true;
+ citation.properties.zoteroIndex = index;
+ citation.properties.noteIndex = noteIndex;
+ this.citationsByIndex[index] = citation;
+
+ // add to citationsByItemID and citationsByIndex
+ for(var i=0; i<citation.citationItems.length; i++) {
+ var citationItem = citation.citationItems[i];
+ if(!this.citationsByItemID[citationItem.id]) {
+ this.citationsByItemID[citationItem.id] = [citation];
+ this.bibliographyHasChanged = true;
+ } else {
+ var byItemID = this.citationsByItemID[citationItem.id];
+ if(byItemID[byItemID.length-1].properties.zoteroIndex < index) {
+ // if index is greater than the last index, add to end
+ byItemID.push(citation);
+ } else {
+ // otherwise, splice in at appropriate location
+ for(var j=0; byItemID[j].properties.zoteroIndex < index && j<byItemID.length-1; j++) {}
+ byItemID.splice(j++, 0, citation);
+ }
+ }
+ }
+
+ var needNewID = !citation.citationID || this.citationIDs[citation.citationID];
+ if(needNewID || !this.oldCitationIDs[citation.citationID]) {
+ if(needNewID) {
+ Zotero.debug("Integration: "+citation.citationID+" ("+index+") needs new citationID");
+ citation.citationID = Zotero.randomString();
+ }
+ this.newIndices[index] = true;
+ this.updateIndices[index] = true;
+ }
+ Zotero.debug("Integration: Adding citationID "+citation.citationID);
+ 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.uris) {
+ if(citationItem.cslItemID) {
+
+ } else if(citationItem.uris) {
[zoteroItem, needUpdate] = this.uriMap.getZoteroItemForURIs(citationItem.uris);
- if(needUpdate) this.updateIndices[index] = true;
+ if(needUpdate && index) this.updateIndices[index] = true;
} else {
if(citationItem.key) {
zoteroItem = Zotero.Items.getByKey(citationItem.key);
@@ -1795,7 +1836,7 @@ Zotero.Integration.Session.prototype.addCitation = function(index, noteIndex, ar
} else if(citationItem.id) {
zoteroItem = Zotero.Items.get(citationItem.id);
}
- if(zoteroItem) this.updateIndices[index] = true;
+ if(zoteroItem && index) this.updateIndices[index] = true;
}
// if no item, check if it was already reselected and otherwise handle as a missing item
@@ -1819,7 +1860,7 @@ Zotero.Integration.Session.prototype.addCitation = function(index, noteIndex, ar
if(this.reselectedItems[reselectKey]) {
zoteroItem = Zotero.Items.get(this.reselectedItems[reselectKey]);
citationItem.id = zoteroItem.id;
- this.updateIndices[index] = true;
+ if(index) this.updateIndices[index] = true;
break;
}
}
@@ -1831,7 +1872,7 @@ Zotero.Integration.Session.prototype.addCitation = function(index, noteIndex, ar
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;
+ citationItem.id = embeddedItem.id;
success = true;
break;
}
@@ -1847,9 +1888,15 @@ Zotero.Integration.Session.prototype.addCitation = function(index, noteIndex, ar
}
// assign a random string as an item ID
- var anonymousID = itemData.id = Zotero.randomString();
+ var anonymousID = Zotero.randomString();
+ var globalID = itemData.id = citationItem.id = this.data.sessionID+"/"+anonymousID;
this.embeddedItems[anonymousID] = itemData;
- citationItem.id = this.data.sessionID+"/"+anonymousID;
+
+ 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(
@@ -1862,42 +1909,6 @@ Zotero.Integration.Session.prototype.addCitation = function(index, noteIndex, ar
citationItem.id = zoteroItem.id;
}
}
-
- citation.properties.added = true;
- citation.properties.zoteroIndex = index;
- citation.properties.noteIndex = noteIndex;
- this.citationsByIndex[index] = citation;
-
- // add to citationsByItemID and citationsByIndex
- for(var i=0; i<citation.citationItems.length; i++) {
- var citationItem = citation.citationItems[i];
- if(!this.citationsByItemID[citationItem.id]) {
- this.citationsByItemID[citationItem.id] = [citation];
- this.bibliographyHasChanged = true;
- } else {
- var byItemID = this.citationsByItemID[citationItem.id];
- if(byItemID[byItemID.length-1].properties.zoteroIndex < index) {
- // if index is greater than the last index, add to end
- byItemID.push(citation);
- } else {
- // otherwise, splice in at appropriate location
- for(var j=0; byItemID[j].properties.zoteroIndex < index && j<byItemID.length-1; j++) {}
- byItemID.splice(j++, 0, citation);
- }
- }
- }
-
- var needNewID = !citation.citationID || this.citationIDs[citation.citationID];
- if(needNewID || !this.oldCitationIDs[citation.citationID]) {
- if(needNewID) {
- Zotero.debug("Integration: "+citation.citationID+" ("+index+") needs new citationID");
- citation.citationID = Zotero.randomString();
- }
- this.newIndices[index] = true;
- this.updateIndices[index] = true;
- }
- Zotero.debug("Integration: Adding citationID "+citation.citationID);
- this.citationIDs[citation.citationID] = true;
}
/**
@@ -2706,3 +2717,7 @@ Zotero.Integration.URIMap.prototype.getZoteroItemForURIs = function(uris) {
return [zoteroItem, needUpdate];
}
+
+/**
+ *
+ */
+\ No newline at end of file
diff --git a/chrome/content/zotero/xpcom/utilities.js b/chrome/content/zotero/xpcom/utilities.js
@@ -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;
+ }
+ }
+ }
+ }
}
}