commit f0f22009c49790c38d5ea72a3cffc2af11eca0bd
parent e44dcb1bb4f8fb689b9bd1f9b93e40c1c83db33d
Author: Simon Kornblith <simon@simonster.com>
Date: Sat, 5 Jun 2010 17:49:04 +0000
- 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.
Diffstat:
3 files changed, 62 insertions(+), 180 deletions(-)
diff --git a/chrome/content/zotero/xpcom/citeproc.js b/chrome/content/zotero/xpcom/citeproc.js
@@ -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 = [];
-};
-\ No newline at end of file
+};
diff --git a/chrome/content/zotero/xpcom/integration.js b/chrome/content/zotero/xpcom/integration.js
@@ -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
diff --git a/chrome/content/zotero/xpcom/zotero.js b/chrome/content/zotero/xpcom/zotero.js
@@ -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;
-
- // 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;
- }
+ var nativeJSON = Components.classes["@mozilla.org/dom/json;1"].createInstance(Components.interfaces.nsIJSON);
- 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);
+ this.serialize = function(arg) {
+ return nativeJSON.encode(arg);
}
- 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);
}
}
\ No newline at end of file