diff --git a/chrome/content/zotero/fileInterface.js b/chrome/content/zotero/fileInterface.js index fb0f2c85a..94a455f0c 100644 --- a/chrome/content/zotero/fileInterface.js +++ b/chrome/content/zotero/fileInterface.js @@ -356,13 +356,13 @@ var Zotero_File_Interface = new function() { var csl = Zotero.Cite.getStyle(style); var itemSet = csl.generateItemSet(items); - var itemIDs = [];; + var itemIDs = []; for (var i=0; i= parseInt(etAlMin, 10)) { - maxCreators = parseInt(etAlUseFirst, 10); - useEtAl = true; + if(context) { + // figure out if we need to use "et al" + var etAlMin = context.option.(@name == "et-al-min").@value.toString(); + var etAlUseFirst = context.option.(@name == "et-al-use-first").@value.toString(); + + if(position == "subsequent" && context.option.(@name == "et-al-subsequent-min").length()) { + etAlMin = context.option.(@name == "et-al-subsequent-min").@value.toString(); + } + if(position == "subsequent" && context.option.(@name == "et-al-subsequent-use-first").length()) { + etAlUseFirst = context.option.(@name == "et-al-subsequent-use-first").@value.toString(); + } + + if(etAlMin && etAlUseFirst && maxCreators >= parseInt(etAlMin, 10)) { + maxCreators = parseInt(etAlUseFirst, 10); + useEtAl = true; + } + + // add additional names to disambiguate + if(variables[j] == "author" && useEtAl) { + var disambigNames = item.getProperty("disambiguate-add-names"); + if(disambigNames != "") { + maxCreators = disambigNames; + if(disambigNames == creators.length) useEtAl = false; + } + } + + var authorStrings = []; + var firstName, lastName; + + if(child.@form == "short") { + var fullNames = item.getProperty("disambiguate-add-givenname").split(","); + } } // parse authors into strings - var authorStrings = []; - var firstName, lastName; for(var i=0; i 1) { - if(useEtAl) { // multiple creators and need et al - authorStrings.push(this._getTerm("et-al")); - } else { // multiple creators but no et al - // add and to last creator - if(child["@and"].length()) { - if(child["@and"] == "symbol") { - var and = "&" - } else if(child["@and"] == "text") { - var and = this._getTerm("and"); + if(formattedString.format != "Sort") { + // figure out if we need an "and" or an "et al" + var joinString = (child["@delimiter"].length() ? child["@delimiter"].toString() : ", "); + if(creators.length > 1) { + if(useEtAl) { // multiple creators and need et al + authorStrings.push(this._getTerm("et-al")); + } else { // multiple creators but no et al + // add and to last creator + if(child["@and"].length()) { + if(child["@and"] == "symbol") { + var and = "&" + } else if(child["@and"] == "text") { + var and = this._getTerm("and"); + } + + authorStrings[maxCreators-1] = and+" "+authorStrings[maxCreators-1]; } - - authorStrings[maxCreators-1] = and+" "+authorStrings[maxCreators-1]; + } + + // check whether to use a serial comma + if((authorStrings.length == 2 && (child["@delimiter-precedes-last"] != "always" || useEtAl)) || + (authorStrings.length > 2 && child["@delimiter-precedes-last"] == "never")) { + var lastString = authorStrings.pop(); + authorStrings[authorStrings.length-1] = authorStrings[authorStrings.length-1]+" "+lastString; } } - - // check whether to use a serial comma - if((authorStrings.length == 2 && child["@delimiter-precedes-last"] != "always") || - (authorStrings.length > 2 && child["@delimiter-precedes-last"] == "never")) { - var lastString = authorStrings.pop(); - authorStrings[authorStrings.length-1] = authorStrings[authorStrings.length-1]+" "+lastString; - } + newString.append(authorStrings.join(joinString), child); } - newString.append(authorStrings.join(joinString), child); - } else if(name == "label" && variables[j] != "author") { + } else if(formattedString.format != "Sort" && + name == "label" && variables[j] != "author") { newString.append(this._getTerm(variables[j], (maxCreators != 1), child["@form"].toString()), child); } } @@ -487,7 +453,6 @@ Zotero.CSL.prototype._processNames = function(item, element, formattedString, co */ Zotero.CSL.prototype._processElements = function(item, element, formattedString, context, position, locator, locatorType, ignore, isSingle) { - if(!ignore) { ignore = new Array(); ignore[0] = new Array(); // for variables @@ -513,8 +478,8 @@ Zotero.CSL.prototype._processElements = function(item, element, formattedString, var name = child.localName(); if(name == "text") { - if(child["@term-name"].length()) { - var term = this._getTerm(child["@term-name"].toString(), child.@plural.length(), child.@form.toString()); + if(child["@term"].length()) { + var term = this._getTerm(child["@term"].toString(), child.@plural.length(), child.@form.toString()); if(term) { formattedString.append(term, child); } @@ -527,7 +492,12 @@ Zotero.CSL.prototype._processElements = function(item, element, formattedString, for(var j=0; j 1) { - newDisambiguate = oldLetter.substr(0, oldDisambiguate.length-1); - } - - var charCode = oldDisambiguate.charCodeAt(oldDisambiguate.length-1); - if(charCode == 122) { - // item is z; add another letter - newDisambiguate += "a"; - } else { - // next lowercase letter - newDisambiguate += String.fromCharCode(charCode+1); - } - - item.setProperty("disambiguate", newDisambiguate); - } - } - - item.setProperty("subsequent-author-substitute", "1"); - } - - item.setProperty("number", i+1); - - lastItem = item; - lastNames = names; - lastYear = year; - } - } -} - -/* - * Sorts the item set, running postprocessing afterwards - */ -Zotero.CSL.ItemSet.prototype.resort = function() { - var me = this; - - this.items = this.items.sort(function(a, b) { - return me._compareItem(a, b); - }); + this.resort(); } /* @@ -1305,13 +1264,261 @@ Zotero.CSL.ItemSet.prototype.resort = function() { Zotero.CSL.ItemSet.prototype.getItemsByIds = function(ids) { var items = []; for each(var id in ids) { - if(this.itemsById[id]) { + if(this.itemsById[id] != undefined) { items.push(this.itemsById[id]); } } return items; } +/* + * Adds items to the given item set; must be passed either CSL.Item + * objects or objects that may be wrapped as CSL.Item objects + */ +Zotero.CSL.ItemSet.prototype.add = function(items) { + for(var i in items) { + if(items[i] instanceof Zotero.CSL.Item) { + var newItem = items[i]; + } else { + var newItem = new Zotero.CSL.Item(items[i]); + } + + this.itemsById[newItem.getID()] = newItem; + this.items.push(newItem); + } +} + +/* + * Removes items from the item set; must be passed either CSL.Item objects + * or item IDs + */ +Zotero.CSL.ItemSet.prototype.remove = function(items) { + for(var i in items) { + if(items[i] instanceof Zotero.CSL.Item) { + var item = items[i]; + } else { + var item = this.itemsById[items[i]]; + } + this.itemsById[item.getID()] = undefined; + this.items.splice(this.items.indexOf(item), 1); + } +} + +/* + * Sorts the item set, also running postprocessing and returning items whose + * citations have changed + */ +Zotero.CSL.ItemSet.prototype.resort = function() { + // sort, if necessary + if(this.bibliography.option.(@name == "sort-algorithm").length() + && this.bibliography.option.(@name == "sort-algorithm").@value != "cited") { + var me = this; + + this.items = this.items.sort(function(a, b) { + return me._compareItem(a, b); + }); + } + + changedCitations = new Array(); + + // first loop through to collect disambiguation data by item, so we can + // see if any items have changed + if(enabledDisambiguationOptions.length) { + oldDisambiguate = new Array(); + for(var i in enabledDisambiguationOptions) { + oldDisambiguate[i] = new Array(); + for(var j in this.items) { + if(this.items[j] == undefined) continue; + oldDisambiguate[i][j] = this.items[j].getProperty(enabledDisambiguationOptions[i]); + this.items[j].setProperty(enabledDisambiguationOptions[i], ""); + } + } + } + + // loop through once to determine where items equal the previous item + if(enabledDisambiguationOptions.length) { + citationsEqual = []; + for(var i in this.items) { + citationsEqual[i] = this._compareCitations(this.items[i-1], this.items[i]); + } + } + + var lastItem = false; + var lastNames = false; + var lastYear = false; + for(var i in this.items) { + var item = this.items[i]; + if(item == undefined) continue; + + var year = item.getDate("issued"); + if(year) year = year.getDateVariable("year"); + var names = item.getNames("author"); + var disambiguated = false; + + // true only if names are an exact match + var exactMatch = this._compareNames(item, lastItem); + + if(enabledDisambiguationOptions.length && i != 0 && !citationsEqual[i] + && year == lastYear) { + // some options can only be applied if there are actual authors + if(names && lastNames) { + if(exactMatch == 0) { + // copy from previous item + this._copyDisambiguation(lastItem, item); + } else { + // these options only apply if not an _exact_ match + if(this.options["disambiguate-add-names"]) { + // try adding names to disambiguate + var oldAddNames = lastItem.getProperty("disambiguate-add-names"); + + // if a different number of names, disambiguation is + // easy, although we should still see if there is a + // smaller number of names that works + var numberOfNames = names.length; + if(numberOfNames > lastNames.length) { + numberOfNames = lastNames.length; + item.setProperty("disambiguate-add-names", numberOfNames+1); + + // have to check old property + if(!oldAddNames || oldAddNames < numberOfNames) { + lastItem.setProperty("disambiguate-add-names", numberOfNames); + } + + disambiguated = true; + } else if(numberOfNames != lastNames.length) { + item.setProperty("disambiguate-add-names", numberOfNames); + + // have to check old property + if(!oldAddNames || oldAddNames < numberOfNames+1) { + lastItem.setProperty("disambiguate-add-names", numberOfNames+1); + } + + disambiguated = true; + } + } + + // now, loop through and see whether there's a + // dissimilarity before the end + for(var j=0; j 1) { + newDisambiguate = oldLetter.substr(0, lastDisambiguate.length-1); + } + + var charCode = lastDisambiguate.charCodeAt(lastDisambiguate.length-1); + if(charCode == 122) { + // item is z; add another letter + newDisambiguate += "a"; + } else { + // next lowercase letter + newDisambiguate += String.fromCharCode(charCode+1); + } + + item.setProperty("disambiguate-add-year-suffix", newDisambiguate); + } + + disambiguated = true; + } + + // add a title, if the above didn't work + if(!disambiguated) { + lastItem.setProperty("disambiguate-add-title", true); + item.setProperty("disambiguate-add-title", true); + + disambiguated = true; + } + } + + if(this.options["subsequent-author-substitute"] && names && lastNames + && exactMatch) { + item.setProperty("subsequent-author-substitute", "1"); + } + + + item.setProperty("number", i+1); + + lastItem = item; + lastNames = names; + lastYear = year; + } + + // find changed citations + if(enabledDisambiguationOptions.length) { + for(var j in this.items) { + if(this.items[j] == undefined) continue; + for(var i in enabledDisambiguationOptions) { + if(this.items[j].getProperty(enabledDisambiguationOptions[i]) != oldDisambiguate[i][j]) { + changedCitations.push(this.items[j]); + } + } + } + } + + return changedCitations; +} + +/* + * Copies disambiguation settings (with the exception of disambiguate-add-year-suffix) + * from one item to another + */ +Zotero.CSL.ItemSet.prototype._copyDisambiguation = function(fromItem, toItem) { + for each(var option in ["disambiguate-add-givenname", "disambiguate-add-names", + "disambiguate-add-title"]) { + var value = fromItem.getProperty(option); + if(value) { + toItem.setProperty(option, value); + } + } +} + /* * Compares two items, in order to sort the reference list * Returns -1 if A comes before B, 1 if B comes before A, or 0 if they are equal @@ -1321,17 +1528,17 @@ Zotero.CSL.ItemSet.prototype._compareItem = function(a, b) { var sortB = []; // author - if(this.bib.option.(@name == "sort-algorithm").@value == "author-date") { + if(this.bibliography.option.(@name == "sort-algorithm").@value == "author-date") { var sortString = new Zotero.CSL.SortString(); this.csl._processElements(a, this.csl._csl.macro.(@name == "author"), sortString); sortA.push(sortString.get().toLowerCase()); - var date = a.getDate("published"); + var date = a.getDate("issued"); if(date) sortA.push(date.getDateVariable("sort")); sortString = new Zotero.CSL.SortString(); this.csl._processElements(b, this.csl._csl.macro.(@name == "author"), sortString); sortB.push(sortString.get().toLowerCase()); - var date = b.getDate("published"); + var date = b.getDate("issued"); if(date) sortB.push(date.getDateVariable("sort")); } @@ -1356,21 +1563,57 @@ Zotero.CSL.ItemSet.prototype._compareItem = function(a, b) { return 0; } +/* + * Compares two citations; returns true if they are different, false if they are equal + */ +Zotero.CSL.ItemSet.prototype._compareCitations = function(a, b) { + if((!a && b) || (a && !b)) { + return true; + } else if(!a && !b) { + return false; + } + + var aString = new Zotero.CSL.FormattedString(this, "Text"); + this.csl._processElements(a, this.citation.layout, aString, + this.citation, "subsequent"); + + var bString = new Zotero.CSL.FormattedString(this, "Text"); + this.csl._processElements(b, this.citation.layout, bString, + this.citation, "subsequent"); + + return !(aString.get() == bString.get()); +} + /* * Compares the names from two items * Returns -1 if A comes before B, 1 if B comes before A, or 0 if they are equal */ -Zotero.CSL.ItemSet.prototype._compareNames = function(a, b) { +Zotero.CSL.ItemSet.prototype._compareNames = function(a, b, context) { + if(!a && b) { + return -1; + } else if(!b && a) { + return 1; + } else if(!b && !a) { + return 0; + } + var sortString = new Zotero.CSL.SortString(); - this.csl._processElements(a, this.bib.macro.(@name == "author"), sortString); + this.csl._processElements(a, this.csl._csl.macro.(@name == "author"), sortString, context); aString = sortString.get().toLowerCase(); sortString = new Zotero.CSL.SortString(); - this.csl._processElements(b, this.bib.macro.(@name == "author"), sortString); + this.csl._processElements(b, this.csl._csl.macro.(@name == "author"), sortString, context); bString = sortString.get().toLowerCase(); - + if(aString != bString) { - return this._collation.compareString(0, aString, bString);; + var b = this._collation.compareString(0, aString, bString); + if(b != 0) return b; + + if(aString < bString) { + return -1; + } else { + return 1; + } } return 0; } @@ -1604,16 +1847,16 @@ Zotero.CSL.FormattedString.prototype.clone = function(delimiter) { Zotero.CSL.SortString = function() { this.format = "Sort"; this.string = ""; - this.delimiter = "\u0000"; // null character + this.delimiter = "\003"; // null character } Zotero.CSL.SortString.prototype.concat = function(string) { newString = string.get(); // Replace old delimiter if concatenated string has a delimiter as wel - if(newString.match("\u0000")) { + if(newString.match("\003")) { delimiterRegexp = new RegExp(this.delimiter, "g"); - this.delimiter += "\u0000"; + this.delimiter += "\003"; this.string = this.string.replace(delimiterRegexp, this.delimiter); } diff --git a/chrome/content/zotero/xpcom/cite_compat.js b/chrome/content/zotero/xpcom/cite_compat.js index 05620372e..ef9faa175 100644 --- a/chrome/content/zotero/xpcom/cite_compat.js +++ b/chrome/content/zotero/xpcom/cite_compat.js @@ -279,14 +279,42 @@ Zotero.CSL.Compat.Global = new function() { * of items */ Zotero.CSL.Compat.prototype.generateItemSet = function(items) { - Zotero.debug("CSL: preprocessing items"); - - this._ignore = null; + return new Zotero.CSL.Compat.ItemSet(items, this); +} + +Zotero.CSL.Compat.ItemSet = function(items, csl) { + this.items = []; + this.csl = csl; + this.add(items); + this.resort(); +} + +Zotero.CSL.Compat.ItemSet.prototype.getItemsByIds = function(ids) { + return Zotero.Items.get(ids); +} + +Zotero.CSL.Compat.ItemSet.prototype.add = function(items) { + this.items = this.items.concat(items); +} + +Zotero.CSL.Compat.ItemSet.prototype.remove = function(items) { + for(var i in items) { + if(items[i] instanceof Zotero.Item) { + var item = items[i]; + } else { + var item = this.itemsById[items[i]]; + } + this.items.splice(this.items.indexOf(item), 1); + } +} + +Zotero.CSL.Compat.ItemSet.prototype.resort = function() { + var oldDisambiguation = {}; // get data necessary to generate citations before sorting - for(var i in items) { - var item = items[i]; - var dateModified = this._getField(item, "dateModified"); + for(var i in this.items) { + var item = this.items[i]; + var dateModified = this.csl._getField(item, "dateModified"); if(!item._csl || item._csl.dateModified != dateModified) { // namespace everything in item._csl so there's no chance of overlap @@ -294,34 +322,38 @@ Zotero.CSL.Compat.prototype.generateItemSet = function(items) { item._csl.dateModified = dateModified; // separate item into authors, editors, translators - var creators = this._separateItemCreators(item); + var creators = this.csl._separateItemCreators(item); item._csl.authors = creators[0]; item._csl.editors = creators[1]; item._csl.translators = creators[2]; // parse date - item._csl.date = Zotero.CSL.Compat.prototype._processDate(this._getField(item, "date")); + item._csl.date = Zotero.CSL.Compat.prototype._processDate(this.csl._getField(item, "date")); } + // clear disambiguation and subsequent author substitute - if(item._csl.date && item._csl.date.disambiguation) item._csl.date.disambiguation = undefined; + if(item._csl.date && item._csl.date.disambiguation) { + oldDisambiguation[item.getID()] = item._csl.date.disambiguation; + item._csl.date.disambiguation = undefined; + } if(item._csl.subsequentAuthorSubstitute) item._csl.subsequentAuthorSubstitute = undefined; } // sort by sort order - if(this._bib.sortOrder) { - Zotero.debug("CSL: sorting items"); - var me = this; - items.sort(function(a, b) { + if(this.csl._bib.sortOrder) { + Zotero.debug("CSL: sorting this.items"); + var me = this.csl; + this.items.sort(function(a, b) { return me._compareItem(a, b); }); } - // disambiguate items after preprocessing and sorting + // disambiguate this.items after preprocessing and sorting var usedCitations = new Array(); var lastAuthors; - for(var i in items) { - var item = items[i]; + for(var i in this.items) { + var item = this.items[i]; // handle subsequent author substitutes if(item._csl.authors.length && lastAuthors) { @@ -340,7 +372,7 @@ Zotero.CSL.Compat.prototype.generateItemSet = function(items) { lastAuthors = item._csl.authors; // handle (2006a) disambiguation for author-date styles - if(this.class == "author-date") { + if(this.csl.class == "author-date") { var year = item._csl.date.year; if(authorsAreSame) { @@ -378,13 +410,17 @@ Zotero.CSL.Compat.prototype.generateItemSet = function(items) { item._csl.number = i; } - // for compatibility, create an itemSet object - var me = this; - itemSet = new Object(); - itemSet.items = items; - itemSet.resort = function() { this.items = me.generateItemSet(items).items }; - - return itemSet; + // see which items have changed + var returnItems = []; + for each(var item in this.items) { + if(item._csl.date && item._csl.date.disambiguation) { + var oldDisambig = oldDisambiguation[item.getID()]; + if(!oldDisambig || oldDisambig != item._csl.date.disambiguation) { + returnItems += item; + } + } + } + return returnItems; } Zotero.CSL.Compat._locatorTerms = { @@ -393,9 +429,7 @@ Zotero.CSL.Compat._locatorTerms = { l:"line" }; -Zotero.CSL.Compat.prototype.createCitation = function(itemSet, ids, format, position, locators, locatorTypes) { - var items = Zotero.Items.get(ids); - +Zotero.CSL.Compat.prototype.createCitation = function(itemSet, items, format, position, locators, locatorTypes) { var string = new Zotero.CSL.Compat.FormattedString(this, format); if(position == "ibid" || position == "ibid-pages") { // indicates ibid var term = this._getTerm("ibid"); @@ -406,7 +440,7 @@ Zotero.CSL.Compat.prototype.createCitation = function(itemSet, ids, format, posi var locator = locators[0]; if(locator) { - var locatorType = Zotero.CSL.Compat._locatorTerms[locatorTypes[0]]; + var locatorType = locatorTypes[0]; // search for elements with the same serialization var element = this._getFieldDefaults("locator"); @@ -429,7 +463,7 @@ Zotero.CSL.Compat.prototype.createCitation = function(itemSet, ids, format, posi var locatorType = false; var locator = false; if(locators) { - locatorType = Zotero.CSL.Compat._locatorTerms[locatorTypes[i]]; + locatorType = locatorTypes[i]; locator = locators[i]; } @@ -1088,7 +1122,7 @@ Zotero.CSL.Compat.prototype._processCreators = function(type, element, creators, // check whether to use a serial comma Zotero.debug(child["delimiter-precedes-last"]); - if((authorStrings.length == 2 && child["delimiter-precedes-last"] != "always") || + if((authorStrings.length == 2 && (useEtAl || child["delimiter-precedes-last"] != "always")) || (authorStrings.length > 2 && child["delimiter-precedes-last"] == "never")) { var lastString = authorStrings.pop(); authorStrings[authorStrings.length-1] = authorStrings[authorStrings.length-1]+" "+lastString; diff --git a/chrome/content/zotero/xpcom/integration.js b/chrome/content/zotero/xpcom/integration.js index 92c09b2da..05fed06c2 100644 --- a/chrome/content/zotero/xpcom/integration.js +++ b/chrome/content/zotero/xpcom/integration.js @@ -376,9 +376,7 @@ Zotero.Integration.SOAP = new function() { for(var i=3; i - - American Psychological Association - http://purl.org/net/xbiblio/csl/styles/apa.csl - http://purl.org/net/xbiblio/csl/styles/apa.csl - - Simon Kornblith - simon@simonster.com - - - - 2007-07-11T14:27:56+08:00 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + American Psychological Association + http://purl.org/net/xbiblio/csl/styles/apa.csl + http://purl.org/net/xbiblio/csl/styles/apa.csl + + Simon Kornblith + simon@simonster.com + + + + 2007-08-04T15:15:00+08:00 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '); REPLACE INTO csl VALUES('http://www.zotero.org/namespaces/CSL/chicago-author-date.csl', '2007-04-25 23:40:00', 'Chicago Manual of Style (Author-Date)',