www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | README | LICENSE

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:
Mchrome/content/zotero/integration/quickFormat.js | 28+++++++++++++++++++---------
Mchrome/content/zotero/xpcom/cite.js | 29+++++++++++++++++++++++++++++
Mchrome/content/zotero/xpcom/data/item.js | 14+++++++++++++-
Mchrome/content/zotero/xpcom/integration.js | 136++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mchrome/content/zotero/xpcom/utilities.js | 113++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
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; + } + } + } + } } }