- fix (most) unnecessary citation update issues

- switch Zotero.JSON to native Firefox JSON support
- update to citeproc-js 1.0.21

From Frank's announcement:
In this release:

- Tighten up internal "NUMERIC" update_mode to mean styles that render
citation-number in citations (renderings in the bibliography are now
ignored for purposes of setting this flag).

- The numeric styles fix introduced at version 1.0.17 broke with
styles that sort the bibliography on anything other than citation-
number (i.e. document first-reference order).  With this release,
arbitrary sorts of the bibliography work with numeric styles.

- Position evaluation code is now invoked only in styles that make use
of position testing (to save a few cycles).

- Numeric styles now perform targetted citation updates correctly.
This commit is contained in:
Simon Kornblith 2010-06-05 17:49:04 +00:00
parent e44dcb1bb4
commit f0f22009c4
3 changed files with 62 additions and 179 deletions

View File

@ -1432,7 +1432,7 @@ CSL.dateParser = function (txt) {
};
CSL.Engine = function (sys, style, lang, xmlmode) {
var attrs, langspec, localexml, locale;
this.processor_version = "1.0.20";
this.processor_version = "1.0.21";
this.csl_version = "1.0";
this.sys = sys;
this.sys.xml = new CSL.System.Xml.Parsing();
@ -2224,7 +2224,7 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
sortedItems.push(newitem);
citation.citationItems[pos].item = Item;
}
if (!this[this.tmp.area].opt["citation-number-sort"] && sortedItems && sortedItems.length > 1 && this.citation_sort.tokens.length > 0) {
if (!this.citation.opt["citation-number-sort"] && sortedItems && sortedItems.length > 1 && this.citation_sort.tokens.length > 0) {
len = sortedItems.length;
for (pos = 0; pos < len; pos += 1) {
sortedItems[pos][1].sortkeys = CSL.getSortKeys.call(this, sortedItems[pos][0], "citation_sort");
@ -2248,7 +2248,7 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
}
this.registry.citationreg.citationByIndex = citationByIndex;
this.registry.citationreg.citationsByItemId = {};
if (this.opt.update_mode === CSL.POSITION || true) {
if (this.opt.update_mode === CSL.POSITION) {
textCitations = [];
noteCitations = [];
}
@ -2267,7 +2267,7 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
this.registry.citationreg.citationsByItemId[item[1].id].push(citationByIndex[pos]);
}
}
if (this.opt.update_mode === CSL.POSITION || true) {
if (this.opt.update_mode === CSL.POSITION) {
if (citationByIndex[pos].properties.noteIndex) {
noteCitations.push(citationByIndex[pos]);
} else {
@ -2278,7 +2278,7 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
if (!has_bibliography) {
this.updateItems(update_items);
}
if (this.opt.update_mode === CSL.POSITION || true) {
if (this.opt.update_mode === CSL.POSITION) {
for (pos = 0; pos < 2; pos += 1) {
citations = [textCitations, noteCitations][pos];
first_ref = {};
@ -2385,7 +2385,7 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
}
}
}
if (this[this.tmp.area].opt["citation-number-sort"] && sortedItems && sortedItems.length > 1 && this.citation_sort.tokens.length > 0) {
if (this.citation.opt["citation-number-sort"] && sortedItems && sortedItems.length > 1 && this.citation_sort.tokens.length > 0) {
len = sortedItems.length;
for (pos = 0; pos < len; pos += 1) {
sortedItems[pos][1].sortkeys = CSL.getSortKeys.call(this, sortedItems[pos][0], "citation_sort");
@ -4188,7 +4188,9 @@ CSL.Node.text = {
}
if ("citation-number" === variable || "year-suffix" === variable || "citation-label" === variable) {
if (variable === "citation-number") {
state.opt.update_mode = CSL.NUMERIC;
if (state.build.area === "citation") {
state.opt.update_mode = CSL.NUMERIC;
}
if ("citation-number" === state[state.tmp.area].opt.collapse) {
this.range_prefix = "-";
}
@ -7032,6 +7034,7 @@ CSL.Registry = function (state) {
this.uncited = [];
this.refreshes = {};
this.akeys = {};
this.oldseq = {};
this.ambigcites = {};
this.sorter = new CSL.Registry.Comparifier(state, "bibliography_sort");
this.modes = CSL.getModes.call(this.state);
@ -7047,6 +7050,7 @@ CSL.Registry = function (state) {
};
CSL.Registry.prototype.init = function (myitems, uncited_flag) {
var len, pos;
this.oldseq = {};
if (uncited_flag && this.mylist && this.mylist.length) {
this.uncited = myitems;
for (pos = 0, len = myitems.length; pos < len; pos += 1) {
@ -7139,6 +7143,7 @@ CSL.Registry.prototype.rebuildlist = function () {
for (pos = 0; pos < len; pos += 1) {
item = this.mylist[pos];
this.reflist.push(this.registry[item]);
this.oldseq[item] = this.registry[item].seq;
this.registry[item].seq = (pos + 1);
}
};
@ -7200,10 +7205,10 @@ CSL.Registry.prototype.renumber = function () {
len = this.reflist.length;
for (pos = 0; pos < len; pos += 1) {
item = this.reflist[pos];
if (this.state.opt.update_mode === CSL.NUMERIC && this.state.tmp.taintedItemIDs && item.seq !== (pos + 1)) {
item.seq = (pos + 1);
if (this.state.opt.update_mode === CSL.NUMERIC && this.state.tmp.taintedItemIDs && item.seq !== this.oldseq[item.id]) {
this.state.tmp.taintedItemIDs[item.id] = true;
}
item.seq = (pos + 1);
}
};
CSL.Registry.prototype.yearsuffix = function () {
@ -7893,4 +7898,4 @@ CSL.getModes = function () {
CSL.Registry.CitationReg = function (state) {
this.citationById = {};
this.citationByIndex = [];
};
};

View File

@ -557,12 +557,6 @@ Zotero.Integration.Document.prototype._updateSession = function(newField, editFi
}
var endTime = (new Date()).getTime();
Zotero.debug("Collected "+this._fields.length+" fields in "+(endTime-collectFieldsTime)/1000+"; "+1000/((endTime-collectFieldsTime)/this._fields.length)+" fields/second");
// if we are reloading this session, assume no item IDs to be updated except for edited items
if(this._reloadSession) {
this._session.updateItemIDs = {};
this._session.bibliographyHasChanged = false;
}
// load uncited items from bibliography
if(bibliographyData && !this._session.bibliographyData) {
@ -588,7 +582,13 @@ Zotero.Integration.Document.prototype._updateSession = function(newField, editFi
}
}
this._session.updateCitations(true);
// if we are reloading this session, assume no item IDs to be updated except for edited items
if(this._reloadSession) {
this._session.updateCitations();
this._session.updateIndices = {};
this._session.updateItemIDs = {};
this._session.bibliographyHasChanged = false;
}
// create new citation or edit existing citation
if(editFieldIndex) {
@ -886,7 +886,6 @@ Zotero.Integration.Session.prototype.setData = function(data) {
this.data = data;
if(data.style.styleID && oldStyleID != data.style.styleID) {
this.styleID = data.style.styleID;
Zotero.debug("style is "+data.style.styleID);
try {
var getStyle = Zotero.Styles.get(data.style.styleID);
data.style.hasBibliography = getStyle.hasBibliography;
@ -990,12 +989,15 @@ Zotero.Integration.Session.prototype.getCitationField = function(citation) {
var type;
var field = [];
field.push('"citationID":'+Zotero.JSON.serialize(citation.citationID));
field.push('"citationID":'+uneval(citation.citationID));
var properties = [];
for(var j=0; j<Zotero.Integration.Session._saveProperties.length; j++) {
var property = Zotero.Integration.Session._saveProperties[j];
if(citation.properties[property] || citation.properties[property] === false) {
properties.push('"'+property+'":'+Zotero.JSON.serialize(citation.properties[property]));
let propval = typeof citation.properties[property] == "object" ?
Zotero.JSON.serialize(citation.properties[property]) :
uneval(citation.properties[property]);
properties.push('"'+property+'":'+propval);
}
}
if(properties.length) field.push('"properties":{'+properties.join(",")+"}");
@ -1009,7 +1011,10 @@ Zotero.Integration.Session.prototype.getCitationField = function(citation) {
type = typeof(citation.citationItems[j][k]);
if(citation.citationItems[j][k] && Zotero.Integration.Session._saveItems.indexOf(k) !== -1
&& Zotero.Integration.Session._acceptableTypes.indexOf(type) !== -1) {
citationItem.push('"'+k+'":'+Zotero.JSON.serialize(citation.citationItems[j][k]));
let propval = typeof citation.citationItems[j][k] == "object" ?
Zotero.JSON.serialize(citation.citationItems[j][k]) :
uneval(citation.citationItems[j][k]);
citationItem.push('"'+k+'":'+propval);
}
}
@ -1125,13 +1130,16 @@ Zotero.Integration.Session.prototype.addCitation = function(index, noteIndex, ar
}
}
if(!citation.citationID) {
this.newIndices[index] = true;
this.updateIndices[index] = true;
} else if(!this.oldCitationIDs[citation.citationID]) {
var needNewID = !citation.citationID || this.citationIDs[citation.citationID];
if(needNewID || !this.oldCitationIDs[citation.citationID]) {
if(needNewID) {
Zotero.debug("Zotero.Integration: "+citation.citationID+" ("+index+") needs new citationID");
citation.citationID = Zotero.randomString();
}
this.newIndices[index] = true;
this.updateIndices[index] = true;
}
Zotero.debug("Zotero.Integration: adding citationID "+citation.citationID);
this.citationIDs[citation.citationID] = true;
}
/**
@ -1251,9 +1259,9 @@ Zotero.Integration.Session.prototype.deleteCitation = function(index) {
}
}
}
if(oldCitation.citationID) delete this.citationIDs[oldCitation.citationID];
}
Zotero.debug("Zotero.Integration: deleting old citationID "+oldCitation.citationID);
if(oldCitation.citationID) delete this.citationIDs[oldCitation.citationID];
this.updateIndices[index] = true;
}
@ -1319,7 +1327,9 @@ Zotero.Integration.Session.prototype.formatCitation = function(index, citation)
//Zotero.debug("style.processCitationCluster("+citation.toSource()+", "+citationsPre.toSource()+", "+citationsPost.toSource());
var newCitations = this.style.processCitationCluster(citation, citationsPre, citationsPost);
for each(var newCitation in newCitations) {
Zotero.debug("Zotero.Integration: Citation "+citationIndices[newCitation[0]]+" needs to be updated");
this.citationText[citationIndices[newCitation[0]]] = newCitation[1];
this.updateIndices[citationIndices[newCitation[0]]] = true;
}
// this is a heuristic: if other citations get updated, then we should update the
// bibliography. it would be nice if citeproc-js gave us a better hint about this
@ -1330,8 +1340,8 @@ Zotero.Integration.Session.prototype.formatCitation = function(index, citation)
/**
* Updates the list of citations to be serialized to the document
*/
Zotero.Integration.Session.prototype.updateCitations = function(force) {
var allUpdatesForced = false;
Zotero.Integration.Session.prototype.updateCitations = function() {
/*var allUpdatesForced = false;
var forcedUpdates = {};
if(force) {
allUpdatesForced = true;
@ -1353,23 +1363,21 @@ Zotero.Integration.Session.prototype.updateCitations = function(force) {
}
}
}
}
}*/
Zotero.debug("Zotero.Integration: indices of new citations");
Zotero.debug([key for(key in this.newIndices)]);
Zotero.debug("Zotero.Integration: indices of updated citations");
Zotero.debug([key for(key in this.updateIndices)]);
Zotero.debug("Zotero.Integration: indices of forcedUpdates");
Zotero.debug([key for(key in forcedUpdates)]);
var deleteCitations = [];
for each(var indexList in [this.newIndices, this.updateIndices, forcedUpdates]) {
for each(var indexList in [this.newIndices, this.updateIndices]) {
for(var index in indexList) {
index = parseInt(index);
var citation = this.citationsByIndex[index];
if(citation.properties.delete) {
deleteCitations.push(index);
if(deleteCitations.indexOf(index) == -1) deleteCitations.push(index);
continue;
}
if(this.formatCitation(index, citation)) {
@ -1382,10 +1390,10 @@ Zotero.Integration.Session.prototype.updateCitations = function(force) {
}
}
if(allUpdatesForced) {
/*if(allUpdatesForced) {
this.newIndices = {};
this.updateIndices = {};
}
}*/
return deleteCitations;
}
@ -1489,18 +1497,20 @@ Zotero.Integration.Session.prototype.getBibliographyData = function() {
* and position)
*/
Zotero.Integration.Session.prototype.previewCitation = function(citation) {
// remove cached citation text
this.citationText = {};
this.addCitation(citation.properties.index, citation.properties.noteIndex, citation);
// add citation items
try {
this.addCitation(citation.properties.index, citation.properties.noteIndex, citation);
this.formatCitation(citation.properties.index, citation);
this.deleteCitation(citation.properties.index);
} catch(e) {
Zotero.debug(e);
this.deleteCitation(citation.properties.index);
throw e;
}
this.deleteCitation(citation.properties.index);
// we don't delete the citationText only because the add citation dialog always calls
// previewCitation before returning
return this.citationText[citation.properties.index];
}
@ -1542,10 +1552,6 @@ Zotero.Integration.Session.prototype.editCitation = function(index, noteIndex, c
.openWindow(null, 'chrome://zotero/content/integration/addCitationDialog.xul', '',
'chrome,centerscreen,resizable', io);
while(!window.closed) Zotero.mainThread.processNextEvent(true);
if(citation && !io.citation.citationItems.length) {
io.citation = citation;
}
if(io.citation.citationItems.length) { // we have an item
this.addCitation(index, noteIndex, io.citation);
@ -1625,7 +1631,7 @@ Zotero.Integration.Session.BibliographyEditInterface.prototype.remove = function
citation.properties["delete"] = true;
}
delete this.session.citationsByItemID[itemID];
this.session.updateCitations(true);
this.session.updateCitations();
}
// delete uncited if neceessary

View File

@ -2552,144 +2552,16 @@ Zotero.WebProgressFinishListener = function(onFinish) {
}
/*
* Saves or loads JSON objects. Based on public domain code from
* http://www.json.org/json.js
* Saves or loads JSON objects.
*/
Zotero.JSON = new function() {
this.serialize = serialize;
this.unserialize = unserialize;
var nativeJSON = Components.classes["@mozilla.org/dom/json;1"].createInstance(Components.interfaces.nsIJSON);
// m is a table of character substitutions.
var m = {
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
};
// Format integers to have at least two digits.
function f(n) {
return n < 10 ? '0' + n : n;
this.serialize = function(arg) {
return nativeJSON.encode(arg);
}
function replaceFunction(a) {
var c = m[a];
if (c) {
return c;
}
c = a.charCodeAt();
return '\\u00' +
Math.floor(c / 16).toString(16) +
(c % 16).toString(16);
}
function serialize(arg) {
if(arg === null) {
return "null";
} else if(arg instanceof Array) {
var a = [], // The array holding the partial texts.
i, // Loop counter.
l = arg.length,
v; // The value to be stringified.
// For each value in arg array...
for (i = 0; i < l; i += 1) {
var out = serialize(arg[i]);
if(out !== undefined) {
a.push(out);
}
}
// Join all of the member texts together and wrap them in brackets.
return '[' + a.join(',') + ']';
} else if(typeof(arg) == "boolean") {
return String(arg);
} else if(arg instanceof Date) {
// Eventually, this method will be based on the date.toISOString method.
return '"' + arg.getUTCFullYear() + '-' +
f(arg.getUTCMonth() + 1) + '-' +
f(arg.getUTCDate()) + 'T' +
f(arg.getUTCHours()) + ':' +
f(arg.getUTCMinutes()) + ':' +
f(arg.getUTCSeconds()) + 'Z"';
} else if(typeof(arg) == "number") {
// JSON numbers must be finite. Encode non-finite numbers as null.
return isFinite(arg) ? String(arg) : 'null';
} else if(typeof(arg) == "string") {
if (/["\\\x00-\x1f]/.test(arg)) {
return '"' + arg.replace(/[\x00-\x1f\\"]/g, replaceFunction) + '"';
}
return '"' + arg + '"';
} else if(arg instanceof Object) {
var a = [], // The array holding the partial texts.
k, // The current key.
v; // The current value.
// Iterate through all of the keys in the object, ignoring the proto chain
// and keys that are not strings.
for (k in arg) {
if (typeof k === 'string' &&
Object.prototype.hasOwnProperty.apply(arg, [k])) {
var out = serialize(arg[k]);
if(out !== undefined) {
a.push(serialize(k) + ':' + out);
}
}
}
// Join all of the member texts together and wrap them in braces.
return '{' + a.join(',') + '}';
}
return undefined;
}
function unserialize(arg) {
var j;
// Parsing happens in three stages. In the first stage, we run the text against
// a regular expression which looks for non-JSON characters. We are especially
// concerned with '()' and 'new' because they can cause invocation, and '='
// because it can cause mutation. But just to be safe, we will reject all
// unexpected characters.
// We split the first stage into 3 regexp operations in order to work around
// crippling deficiencies in Safari's regexp engine. First we replace all
// backslash pairs with '@' (a non-JSON character). Second we delete all of
// the string literals. Third, we look to see if only JSON characters
// remain. If so, then the text is safe for eval.
if (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/.test(arg.
replace(/\\./g, '@').
replace(/"[^"\\\n\r]*"/g, ''))) {
// In the second stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.
// Friendly AMO reviewer: This is the official json.org library and is safe.
j = eval('(' + arg + ')');
// In the optional third stage, we recursively walk the new structure, passing
// each name/value pair to a filter function for possible transformation.
return j;
}
// If the text is not JSON parseable, then a SyntaxError is thrown.
throw new SyntaxError('parseJSON');
this.unserialize = function(arg) {
return nativeJSON.decode(arg);
}
}