Consolidate Integration.Fields into Integration.Session

This commit is contained in:
Adomas Venčkauskas 2020-07-30 15:42:54 +03:00 committed by Dan Stillman
parent 3701b84116
commit 2b3669afd8
2 changed files with 156 additions and 164 deletions

View file

@ -211,7 +211,7 @@ Zotero.Integration = new function() {
await document.setDocumentData(session.data.serialize()); await document.setDocumentData(session.data.serialize());
// And any citations marked for processing (like retraction warning ignore flag changes) // And any citations marked for processing (like retraction warning ignore flag changes)
if (Object.keys(session.processIndices).length) { if (Object.keys(session.processIndices).length) {
session.fields.updateDocument(FORCE_CITATIONS_FALSE, false, false); session.updateDocument(FORCE_CITATIONS_FALSE, false, false);
} }
} }
} }
@ -422,9 +422,13 @@ Zotero.Integration = new function() {
session.agent = agent; session.agent = agent;
session._doc = doc; session._doc = doc;
session.progressBar = progressBar; session.progressBar = progressBar;
// TODO: this is a pretty awful circular dependence session._fields = null;
session.fields = new Zotero.Integration.Fields(session, doc); session.ignoreEmptyBibliography = true;
session.progressCallback = null;
session._removeCodeFields = {};
session._deleteFields = {};
session._bibliographyFields = [];
if (dataString == EXPORTED_DOCUMENT_MARKER) { if (dataString == EXPORTED_DOCUMENT_MARKER) {
Zotero.Integration.currentSession = session; Zotero.Integration.currentSession = session;
data = await session.importDocument(); data = await session.importDocument();
@ -548,13 +552,13 @@ Zotero.Integration.Interface = function(app, doc, session) {
Zotero.Integration.Interface.prototype.addCitation = Zotero.Promise.coroutine(function* () { Zotero.Integration.Interface.prototype.addCitation = Zotero.Promise.coroutine(function* () {
yield this._session.init(false, false); yield this._session.init(false, false);
let [idx, field, citation] = yield this._session.fields.addEditCitation(null); let [idx, field, citation] = yield this._session.cite(null);
yield this._session.addCitation(idx, yield field.getNoteIndex(), citation); yield this._session.addCitation(idx, yield field.getNoteIndex(), citation);
if (this._session.data.prefs.delayCitationUpdates) { if (this._session.data.prefs.delayCitationUpdates) {
return this._session.writeDelayedCitation(field, citation); return this._session.writeDelayedCitation(field, citation);
} else { } else {
return this._session.fields.updateDocument(FORCE_CITATIONS_FALSE, false, false); return this._session.updateDocument(FORCE_CITATIONS_FALSE, false, false);
} }
}); });
@ -580,12 +584,12 @@ Zotero.Integration.Interface.prototype.addEditCitation = async function (docFiel
await this._session.init(false, false); await this._session.init(false, false);
docField = docField || await this._doc.cursorInField(this._session.data.prefs['fieldType']); docField = docField || await this._doc.cursorInField(this._session.data.prefs['fieldType']);
let [idx, field, citation] = await this._session.fields.addEditCitation(docField); let [idx, field, citation] = await this._session.cite(docField);
await this._session.addCitation(idx, await field.getNoteIndex(), citation); await this._session.addCitation(idx, await field.getNoteIndex(), citation);
if (this._session.data.prefs.delayCitationUpdates) { if (this._session.data.prefs.delayCitationUpdates) {
return this._session.writeDelayedCitation(field, citation); return this._session.writeDelayedCitation(field, citation);
} else { } else {
return this._session.fields.updateDocument(FORCE_CITATIONS_FALSE, false, false); return this._session.updateDocument(FORCE_CITATIONS_FALSE, false, false);
} }
}; };
@ -602,7 +606,7 @@ Zotero.Integration.Interface.prototype.addBibliography = Zotero.Promise.coroutin
"integration.error.title"); "integration.error.title");
} }
let field = new Zotero.Integration.BibliographyField(yield this._session.fields.addField()); let field = new Zotero.Integration.BibliographyField(yield this._session.addField());
var citationsMode = FORCE_CITATIONS_FALSE; var citationsMode = FORCE_CITATIONS_FALSE;
yield field.clearCode(); yield field.clearCode();
if(this._session.data.prefs.delayCitationUpdates) { if(this._session.data.prefs.delayCitationUpdates) {
@ -610,8 +614,8 @@ Zotero.Integration.Interface.prototype.addBibliography = Zotero.Promise.coroutin
this._session.reload = true; this._session.reload = true;
citationsMode = FORCE_CITATIONS_REGENERATE; citationsMode = FORCE_CITATIONS_REGENERATE;
} }
yield this._session.fields.updateSession(citationsMode); yield this._session.updateFromDocument(citationsMode);
yield this._session.fields.updateDocument(citationsMode, true, false); yield this._session.updateDocument(citationsMode, true, false);
}) })
/** /**
@ -621,7 +625,7 @@ Zotero.Integration.Interface.prototype.addBibliography = Zotero.Promise.coroutin
Zotero.Integration.Interface.prototype.editBibliography = Zotero.Promise.coroutine(function*() { Zotero.Integration.Interface.prototype.editBibliography = Zotero.Promise.coroutine(function*() {
// Make sure we have a bibliography // Make sure we have a bibliography
yield this._session.init(true, false); yield this._session.init(true, false);
var fields = yield this._session.fields.get(); var fields = yield this._session.getFields();
var bibliographyField; var bibliographyField;
for (let i = fields.length-1; i >= 0; i--) { for (let i = fields.length-1; i >= 0; i--) {
@ -643,9 +647,9 @@ Zotero.Integration.Interface.prototype.editBibliography = Zotero.Promise.corouti
this._session.reload = true; this._session.reload = true;
citationsMode = FORCE_CITATIONS_REGENERATE; citationsMode = FORCE_CITATIONS_REGENERATE;
} }
yield this._session.fields.updateSession(citationsMode); yield this._session.updateFromDocument(citationsMode);
yield this._session.editBibliography(bibliography); yield this._session.editBibliography(bibliography);
yield this._session.fields.updateDocument(citationsMode, true, false); yield this._session.updateDocument(citationsMode, true, false);
}); });
@ -658,7 +662,7 @@ Zotero.Integration.Interface.prototype.addEditBibliography = Zotero.Promise.coro
"integration.error.title"); "integration.error.title");
} }
var fields = yield this._session.fields.get(); var fields = yield this._session.getFields();
var bibliographyField; var bibliographyField;
for (let i = fields.length-1; i >= 0; i--) { for (let i = fields.length-1; i >= 0; i--) {
@ -671,7 +675,7 @@ Zotero.Integration.Interface.prototype.addEditBibliography = Zotero.Promise.coro
var newBibliography = !bibliographyField; var newBibliography = !bibliographyField;
if (!bibliographyField) { if (!bibliographyField) {
bibliographyField = new Zotero.Integration.BibliographyField(yield this._session.fields.addField()); bibliographyField = new Zotero.Integration.BibliographyField(yield this._session.addField());
yield bibliographyField.clearCode(); yield bibliographyField.clearCode();
} }
@ -682,9 +686,9 @@ Zotero.Integration.Interface.prototype.addEditBibliography = Zotero.Promise.coro
this._session.reload = true; this._session.reload = true;
citationsMode = FORCE_CITATIONS_REGENERATE; citationsMode = FORCE_CITATIONS_REGENERATE;
} }
yield this._session.fields.updateSession(citationsMode); yield this._session.updateFromDocument(citationsMode);
if (!newBibliography) yield this._session.editBibliography(bibliography); if (!newBibliography) yield this._session.editBibliography(bibliography);
yield this._session.fields.updateDocument(citationsMode, true, false); yield this._session.updateDocument(citationsMode, true, false);
}); });
/** /**
@ -695,8 +699,8 @@ Zotero.Integration.Interface.prototype.refresh = async function() {
await this._session.init(true, false) await this._session.init(true, false)
this._session.reload = this._session.reload || this._session.data.prefs.delayCitationUpdates; this._session.reload = this._session.reload || this._session.data.prefs.delayCitationUpdates;
await this._session.fields.updateSession(FORCE_CITATIONS_REGENERATE) await this._session.updateFromDocument(FORCE_CITATIONS_REGENERATE);
await this._session.fields.updateDocument(FORCE_CITATIONS_REGENERATE, true, false); await this._session.updateDocument(FORCE_CITATIONS_REGENERATE, true, false);
} }
/** /**
@ -704,10 +708,9 @@ Zotero.Integration.Interface.prototype.refresh = async function() {
* @return {Promise} * @return {Promise}
*/ */
Zotero.Integration.Interface.prototype.removeCodes = Zotero.Promise.coroutine(function* () { Zotero.Integration.Interface.prototype.removeCodes = Zotero.Promise.coroutine(function* () {
var me = this;
yield this._session.init(true, false) yield this._session.init(true, false)
let fields = yield this._session.fields.get() let fields = yield this._session.getFields()
var result = yield me._doc.displayAlert(Zotero.getString("integration.removeCodesWarning"), var result = yield this._doc.displayAlert(Zotero.getString("integration.removeCodesWarning"),
DIALOG_ICON_WARNING, DIALOG_BUTTONS_OK_CANCEL); DIALOG_ICON_WARNING, DIALOG_BUTTONS_OK_CANCEL);
if (result) { if (result) {
for(var i=fields.length-1; i>=0; i--) { for(var i=fields.length-1; i>=0; i--) {
@ -730,7 +733,7 @@ Zotero.Integration.Interface.prototype.setDocPrefs = Zotero.Promise.coroutine(fu
} else { } else {
// Can get fields while dialog is open // Can get fields while dialog is open
oldData = yield Zotero.Promise.all([ oldData = yield Zotero.Promise.all([
this._session.fields.get(), this._session.getFields(),
this._session.setDocPrefs(true) this._session.setDocPrefs(true)
]).spread(function (fields, setDocPrefs) { ]).spread(function (fields, setDocPrefs) {
// Only return value from setDocPrefs // Only return value from setDocPrefs
@ -743,7 +746,7 @@ Zotero.Integration.Interface.prototype.setDocPrefs = Zotero.Promise.coroutine(fu
if (!oldData) return false; if (!oldData) return false;
// Perform noteType or fieldType conversion // Perform noteType or fieldType conversion
let fields = yield this._session.fields.get(); let fields = yield this._session.getFields();
var convertBibliographies = oldData.prefs.fieldType != this._session.data.prefs.fieldType; var convertBibliographies = oldData.prefs.fieldType != this._session.data.prefs.fieldType;
var convertItems = convertBibliographies var convertItems = convertBibliographies
@ -773,14 +776,14 @@ Zotero.Integration.Interface.prototype.setDocPrefs = Zotero.Promise.coroutine(fu
fieldNoteTypes.length); fieldNoteTypes.length);
} }
// Refresh contents // Refresh field info
this._session.fields = new Zotero.Integration.Fields(this._session, this._doc); this._session._fields = null;
this._session.fields.ignoreEmptyBibliography = false; this._session.ignoreEmptyBibliography = true;
if (this._session.data.prefs.delayCitationUpdates && !fieldsToConvert.length) return; if (this._session.data.prefs.delayCitationUpdates && !fieldsToConvert.length) return;
yield this._session.fields.updateSession(FORCE_CITATIONS_RESET_TEXT); yield this._session.updateFromDocument(FORCE_CITATIONS_RESET_TEXT);
return this._session.fields.updateDocument(FORCE_CITATIONS_RESET_TEXT, true, true); return this._session.updateDocument(FORCE_CITATIONS_RESET_TEXT, true, true);
}); });
/** /**
@ -808,37 +811,48 @@ Zotero.Integration.JSEnumerator.prototype.getNext = function() {
} }
/** /**
* Methods for retrieving fields from a document * Keeps track of all session-specific variables
* @constructor
*/ */
Zotero.Integration.Fields = function(session, doc) { Zotero.Integration.Session = function(doc, app) {
this.embeddedItems = {};
this.embeddedZoteroItems = {};
this.embeddedItemsByURI = {};
this.citationsByIndex = {};
this.resetRequest(doc);
this.primaryFieldType = app.primaryFieldType;
this.secondaryFieldType = app.secondaryFieldType;
this.outputFormat = app.outputFormat || 'rtf';
this._sessionUpToDate = false;
this._app = app;
this._fields = null;
this.ignoreEmptyBibliography = true; this.ignoreEmptyBibliography = true;
// Callback called while retrieving fields with the percentage complete. // Callback called while retrieving fields with the percentage complete.
this.progressCallback = null; this.progressCallback = null;
this._session = session;
this._doc = doc;
this._removeCodeFields = {}; this._removeCodeFields = {};
this._deleteFields = {}; this._deleteFields = {};
this._bibliographyFields = []; this._bibliographyFields = [];
this.sessionID = Zotero.randomString();
Zotero.Integration.sessions[this.sessionID] = this;
} }
/** /**
* Checks that it is appropriate to add fields to the current document at the current * Checks that it is appropriate to add fields to the current document at the current
* position, then adds one. * position, then adds one.
*/ */
Zotero.Integration.Fields.prototype.addField = async function(note) { Zotero.Integration.Session.prototype.addField = async function(note) {
// Get citation types if necessary // Get citation types if necessary
if (!await this._doc.canInsertField(this._session.data.prefs['fieldType'])) { if (!await this._doc.canInsertField(this.data.prefs['fieldType'])) {
return Zotero.Promise.reject(new Zotero.Exception.Alert("integration.error.cannotInsertHere", return Zotero.Promise.reject(new Zotero.Exception.Alert("integration.error.cannotInsertHere",
[], "integration.error.title")); [], "integration.error.title"));
} }
var field = await this._doc.cursorInField(this._session.data.prefs['fieldType']); var field = await this._doc.cursorInField(this.data.prefs['fieldType']);
if (field) { if (field) {
if (!await this._session.displayAlert(Zotero.getString("integration.replace"), if (!await this.displayAlert(Zotero.getString("integration.replace"),
DIALOG_ICON_STOP, DIALOG_ICON_STOP,
DIALOG_BUTTONS_OK_CANCEL)) { DIALOG_BUTTONS_OK_CANCEL)) {
return Zotero.Promise.reject(new Zotero.Exception.UserCancelled("inserting citation")); return Zotero.Promise.reject(new Zotero.Exception.UserCancelled("inserting citation"));
@ -846,13 +860,13 @@ Zotero.Integration.Fields.prototype.addField = async function(note) {
} }
if (!field) { if (!field) {
field = await this._doc.insertField(this._session.data.prefs['fieldType'], field = await this._doc.insertField(this.data.prefs['fieldType'],
(note ? this._session.data.prefs["noteType"] : 0)); (note ? this.data.prefs["noteType"] : 0));
// Older doc plugins do not initialize the field code to anything meaningful // Older doc plugins do not initialize the field code to anything meaningful
// so we ensure it here manually // so we ensure it here manually
field.setCode('TEMP'); field.setCode('TEMP');
} }
// If fields already retrieved, further this.get() calls will returned the cached version // If fields already retrieved, further this.getFields() calls will returned the cached version
// So we append this field to that list // So we append this field to that list
if (this._fields) { if (this._fields) {
this._fields.push(field); this._fields.push(field);
@ -865,7 +879,7 @@ Zotero.Integration.Fields.prototype.addField = async function(note) {
* Gets all fields for a document * Gets all fields for a document
* @return {Promise} Promise resolved with field list. * @return {Promise} Promise resolved with field list.
*/ */
Zotero.Integration.Fields.prototype.get = new function() { Zotero.Integration.Session.prototype.getFields = new function() {
var deferred; var deferred;
return async function(force=false) { return async function(force=false) {
// If we already have fields, just return them // If we already have fields, just return them
@ -882,12 +896,12 @@ Zotero.Integration.Fields.prototype.get = new function() {
// Otherwise, start getting fields // Otherwise, start getting fields
var timer = new Zotero.Integration.Timer(); var timer = new Zotero.Integration.Timer();
timer.start(); timer.start();
this._session.progressBar.start(); this.progressBar.start();
try { try {
var fields = this._fields = Array.from(await this._doc.getFields(this._session.data.prefs['fieldType'])); var fields = this._fields = Array.from(await this._doc.getFields(this.data.prefs['fieldType']));
var retrieveTime = timer.stop(); var retrieveTime = timer.stop();
this._session.progressBar.finishSegment(); this.progressBar.finishSegment();
Zotero.debug("Integration: Retrieved " + fields.length + " fields in " + Zotero.debug("Integration: Retrieved " + fields.length + " fields in " +
retrieveTime + "; " + fields.length/retrieveTime + " fields/second"); retrieveTime + "; " + fields.length/retrieveTime + " fields/second");
deferred.resolve(fields); deferred.resolve(fields);
@ -901,11 +915,11 @@ Zotero.Integration.Fields.prototype.get = new function() {
} }
/** /**
* Updates Zotero.Integration.Session attached to Zotero.Integration.Fields in line with document * Updates Zotero.Integration.Session citations from the session document
*/ */
Zotero.Integration.Fields.prototype.updateSession = Zotero.Promise.coroutine(function* (forceCitations) { Zotero.Integration.Session.prototype.updateFromDocument = Zotero.Promise.coroutine(function* (forceCitations) {
yield this.get(); yield this.getFields();
this._session.resetRequest(this._doc); this.resetRequest(this._doc);
this._removeCodeFields = {}; this._removeCodeFields = {};
this._deleteFields = {}; this._deleteFields = {};
@ -913,50 +927,50 @@ Zotero.Integration.Fields.prototype.updateSession = Zotero.Promise.coroutine(fun
var timer = new Zotero.Integration.Timer(); var timer = new Zotero.Integration.Timer();
timer.start(); timer.start();
this._session.progressBar.start(); this.progressBar.start();
if (forceCitations) { if (forceCitations) {
this._session.regenAll = true; this.regenAll = true;
} }
yield this._processFields(); yield this._processFields();
try { try {
yield this._session.handleRetractedItems(); yield this.handleRetractedItems();
} }
catch (e) { catch (e) {
Zotero.debug('Retracted item handling failed', 2); Zotero.debug('Retracted item handling failed', 2);
Zotero.logError(e); Zotero.logError(e);
} }
this._session.regenAll = false; this.regenAll = false;
var updateTime = timer.stop(); var updateTime = timer.stop();
this._session.progressBar.finishSegment(); this.progressBar.finishSegment();
Zotero.debug("Integration: Updated session data for " + this._fields.length + " fields in " Zotero.debug("Integration: Updated session data for " + this._fields.length + " fields in "
+ updateTime + "; " + this._fields.length/updateTime + " fields/second"); + updateTime + "; " + this._fields.length/updateTime + " fields/second");
if (this._session.reload) { if (this.reload) {
this._session.restoreProcessorState(); this.restoreProcessorState();
delete this._session.reload; delete this.reload;
} }
this._session._sessionUpToDate = true; this._sessionUpToDate = true;
}); });
/** /**
* Keep processing fields until all have been processed * Keep processing fields until all have been processed
*/ */
Zotero.Integration.Fields.prototype._processFields = Zotero.Promise.coroutine(function* () { Zotero.Integration.Session.prototype._processFields = async function () {
if (!this._fields) { if (!this._fields) {
throw new Error("_processFields called without fetching fields first"); throw new Error("_processFields called without fetching fields first");
} }
for (var i = 0; i < this._fields.length; i++) { for (var i = 0; i < this._fields.length; i++) {
let field = yield Zotero.Integration.Field.loadExisting(this._fields[i]); let field = await Zotero.Integration.Field.loadExisting(this._fields[i]);
if (field.type === INTEGRATION_TYPE_ITEM) { if (field.type === INTEGRATION_TYPE_ITEM) {
var noteIndex = yield field.getNoteIndex(), var noteIndex = await field.getNoteIndex(),
data = yield field.unserialize(), data = await field.unserialize(),
citation = new Zotero.Integration.Citation(field, data, noteIndex); citation = new Zotero.Integration.Citation(field, data, noteIndex);
yield this._session.addCitation(i, noteIndex, citation); await this.addCitation(i, noteIndex, citation);
} else if (field.type === INTEGRATION_TYPE_BIBLIOGRAPHY) { } else if (field.type === INTEGRATION_TYPE_BIBLIOGRAPHY) {
if (this.ignoreEmptyBibliography && (yield field.getText()).trim() === "") { if (this.ignoreEmptyBibliography && (await field.getText()).trim() === "") {
this._removeCodeFields[i] = true; this._removeCodeFields[i] = true;
} else { } else {
field.index = i; field.index = i;
@ -965,15 +979,15 @@ Zotero.Integration.Fields.prototype._processFields = Zotero.Promise.coroutine(fu
} }
} }
if (this._bibliographyFields.length) { if (this._bibliographyFields.length) {
var data = yield this._bibliographyFields[0].unserialize() var data = await this._bibliographyFields[0].unserialize()
this._session.bibliography = new Zotero.Integration.Bibliography(this._bibliographyFields[0], data); this.bibliography = new Zotero.Integration.Bibliography(this._bibliographyFields[0], data);
yield this._session.bibliography.loadItemData(); await this.bibliography.loadItemData();
} else { } else {
delete this._session.bibliography; delete this.bibliography;
} }
// TODO: figure this out // TODO: figure this out
// Zotero.Notifier.trigger('add', 'collection', 'document'); // Zotero.Notifier.trigger('add', 'collection', 'document');
}); };
/** /**
* Updates bibliographies and fields within a document * Updates bibliographies and fields within a document
@ -983,31 +997,31 @@ Zotero.Integration.Fields.prototype._processFields = Zotero.Promise.coroutine(fu
* modified since they were created, instead of showing a warning * modified since they were created, instead of showing a warning
* @return {Promise} A promise resolved when the document is updated * @return {Promise} A promise resolved when the document is updated
*/ */
Zotero.Integration.Fields.prototype.updateDocument = Zotero.Promise.coroutine(function* (forceCitations, forceBibliography, Zotero.Integration.Session.prototype.updateDocument = Zotero.Promise.coroutine(function* (forceCitations, forceBibliography,
ignoreCitationChanges) { ignoreCitationChanges) {
this._session.timer = new Zotero.Integration.Timer(); this.timer = new Zotero.Integration.Timer();
this._session.timer.start(); this.timer.start();
this._session.progressBar.start(); this.progressBar.start();
yield this._session._updateCitations() yield this._updateCitations();
this._session.progressBar.finishSegment(); this.progressBar.finishSegment();
this._session.progressBar.start(); this.progressBar.start();
yield this._updateDocument(forceCitations, forceBibliography, ignoreCitationChanges) yield this._updateDocument(forceCitations, forceBibliography, ignoreCitationChanges)
this._session.progressBar.finishSegment(); this.progressBar.finishSegment();
var diff = this._session.timer.stop(); var diff = this.timer.stop();
this._session.timer = null; this.timer = null;
Zotero.debug(`Integration: updateDocument complete in ${diff}s`) Zotero.debug(`Integration: updateDocument complete in ${diff}s`)
// If the update takes longer than 5s suggest delaying citation updates // If the update takes longer than 5s suggest delaying citation updates
if (diff > DELAY_CITATIONS_PROMPT_TIMEOUT && !this._session.data.prefs.dontAskDelayCitationUpdates && !this._session.data.prefs.delayCitationUpdates) { if (diff > DELAY_CITATIONS_PROMPT_TIMEOUT && !this.data.prefs.dontAskDelayCitationUpdates && !this._session.data.prefs.delayCitationUpdates) {
yield this._doc.activate(); yield this._doc.activate();
var interfaceType = 'tab'; var interfaceType = 'tab';
if (['MacWord2008', 'OpenOffice'].includes(this._session.agent)) { if (['MacWord2008', 'OpenOffice'].includes(this.agent)) {
interfaceType = 'toolbar'; interfaceType = 'toolbar';
} }
var result = yield this._session.displayAlert( var result = yield this.displayAlert(
Zotero.getString('integration.delayCitationUpdates.alert.text1') Zotero.getString('integration.delayCitationUpdates.alert.text1')
+ "\n\n" + "\n\n"
+ Zotero.getString(`integration.delayCitationUpdates.alert.text2.${interfaceType}`) + Zotero.getString(`integration.delayCitationUpdates.alert.text2.${interfaceType}`)
@ -1017,11 +1031,11 @@ Zotero.Integration.Fields.prototype.updateDocument = Zotero.Promise.coroutine(fu
DIALOG_BUTTONS_YES_NO_CANCEL DIALOG_BUTTONS_YES_NO_CANCEL
); );
if (result == 2) { if (result == 2) {
this._session.data.prefs.delayCitationUpdates = true; this.data.prefs.delayCitationUpdates = true;
} }
if (result) { if (result) {
this._session.data.prefs.dontAskDelayCitationUpdates = true; this.data.prefs.dontAskDelayCitationUpdates = true;
// yield this._session.setDocPrefs(true); // yield this.setDocPrefs(true);
} }
} }
}); });
@ -1033,11 +1047,11 @@ Zotero.Integration.Fields.prototype.updateDocument = Zotero.Promise.coroutine(fu
* @param {Boolean} [ignoreCitationChanges] Whether to ignore changes to citations that have been * @param {Boolean} [ignoreCitationChanges] Whether to ignore changes to citations that have been
* modified since they were created, instead of showing a warning * modified since they were created, instead of showing a warning
*/ */
Zotero.Integration.Fields.prototype._updateDocument = async function(forceCitations, forceBibliography, Zotero.Integration.Session.prototype._updateDocument = async function(forceCitations, forceBibliography,
ignoreCitationChanges) { ignoreCitationChanges) {
if(this.progressCallback) { if(this.progressCallback) {
var nFieldUpdates = Object.keys(this._session.processIndices).length; var nFieldUpdates = Object.keys(this.processIndices).length;
if(this._session.bibliographyHasChanged || forceBibliography) { if(this.bibliographyHasChanged || forceBibliography) {
nFieldUpdates += this._bibliographyFields.length*5; nFieldUpdates += this._bibliographyFields.length*5;
} }
} }
@ -1064,12 +1078,12 @@ Zotero.Integration.Fields.prototype._updateDocument = async function(forceCitati
// Although at least for macword you don't get to interact with the document // Although at least for macword you don't get to interact with the document
// while a prompt is being displayed, so the order of these prompts is to a large // while a prompt is being displayed, so the order of these prompts is to a large
// degree a minor issue. // degree a minor issue.
var indicesToUpdate = Object.keys(this._session.processIndices); var indicesToUpdate = Object.keys(this.processIndices);
// Add bibliography indices to the above indices // Add bibliography indices to the above indices
if (this._session.bibliography // if bibliography exists if (this.bibliography // if bibliography exists
&& Object.keys(this._session.citationsByIndex).length // and doc has citations && Object.keys(this.citationsByIndex).length // and doc has citations
&& (this._session.bibliographyHasChanged // and bibliography changed && (this.bibliographyHasChanged // and bibliography changed
|| forceBibliography)) { // or if we should generate regardless of || forceBibliography)) { // or if we should generate regardless of
// changes // changes
for (let field of this._bibliographyFields) { for (let field of this._bibliographyFields) {
@ -1092,7 +1106,7 @@ Zotero.Integration.Fields.prototype._updateDocument = async function(forceCitati
// Jump to next event loop step for UI updates // Jump to next event loop step for UI updates
await Zotero.Promise.delay(); await Zotero.Promise.delay();
var citation = this._session.citationsByIndex[i]; var citation = this.citationsByIndex[i];
if (citation) { if (citation) {
let citationField = citation._field; let citationField = citation._field;
@ -1111,7 +1125,7 @@ Zotero.Integration.Fields.prototype._updateDocument = async function(forceCitati
+ "Current: " + plainCitation + "Current: " + plainCitation
); );
await citationField.select(); await citationField.select();
var result = await this._session.displayAlert( var result = await this.displayAlert(
Zotero.getString("integration.citationChanged")+"\n\n" Zotero.getString("integration.citationChanged")+"\n\n"
+ Zotero.getString("integration.citationChanged.description")+"\n\n" + Zotero.getString("integration.citationChanged.description")+"\n\n"
+ Zotero.getString("integration.citationChanged.original", citation.properties.plainCitation)+"\n" + Zotero.getString("integration.citationChanged.original", citation.properties.plainCitation)+"\n"
@ -1121,7 +1135,7 @@ Zotero.Integration.Fields.prototype._updateDocument = async function(forceCitati
citation.properties.dontUpdate = true; citation.properties.dontUpdate = true;
// Sigh. This hurts. setCode in LO forces a text reset. If the formattedText is rtf // Sigh. This hurts. setCode in LO forces a text reset. If the formattedText is rtf
// it is reinserted, however that breaks what properties.dontUpdate should do // it is reinserted, however that breaks what properties.dontUpdate should do
if (this._session.primaryFieldType == "ReferenceMark" if (this.primaryFieldType == "ReferenceMark"
&& citation.properties.formattedCitation.includes('\\')) { && citation.properties.formattedCitation.includes('\\')) {
citation.properties.formattedCitation = citation.properties.plainCitation; citation.properties.formattedCitation = citation.properties.plainCitation;
} }
@ -1141,7 +1155,7 @@ Zotero.Integration.Fields.prototype._updateDocument = async function(forceCitati
// for citations that were inserted with delay styling // for citations that were inserted with delay styling
var wasDelayed = citation.properties.formattedCitation var wasDelayed = citation.properties.formattedCitation
&& citation.properties.formattedCitation.includes(DELAYED_CITATION_RTF_STYLING); && citation.properties.formattedCitation.includes(DELAYED_CITATION_RTF_STYLING);
if (this._session.outputFormat == 'rtf' && wasDelayed) { if (this.outputFormat == 'rtf' && wasDelayed) {
isRich = await citationField.setText(`${DELAYED_CITATION_RTF_STYLING_CLEAR}{${formattedCitation}}`); isRich = await citationField.setText(`${DELAYED_CITATION_RTF_STYLING_CLEAR}{${formattedCitation}}`);
} else { } else {
isRich = await citationField.setText(formattedCitation); isRich = await citationField.setText(formattedCitation);
@ -1170,24 +1184,24 @@ Zotero.Integration.Fields.prototype._updateDocument = async function(forceCitati
throw new Error(`Attempting to update field ${i} which does not exist`); throw new Error(`Attempting to update field ${i} which does not exist`);
} }
if (forceBibliography || this._session.bibliographyDataHasChanged) { if (forceBibliography || this.bibliographyDataHasChanged) {
let code = this._session.bibliography.serialize(); let code = this.bibliography.serialize();
await bibliographyField.setCode(code); await bibliographyField.setCode(code);
} }
// get bibliography and format as RTF // get bibliography and format as RTF
var bib = this._session.bibliography.getCiteprocBibliography(this._session.style); var bib = this.bibliography.getCiteprocBibliography(this.style);
var bibliographyText = ""; var bibliographyText = "";
if (bib) { if (bib) {
if (this._session.outputFormat == 'rtf') { if (this.outputFormat == 'rtf') {
bibliographyText = bib[0].bibstart+bib[1].join("\\\r\n")+"\\\r\n"+bib[0].bibend; bibliographyText = bib[0].bibstart+bib[1].join("\\\r\n")+"\\\r\n"+bib[0].bibend;
} else { } else {
bibliographyText = bib[0].bibstart+bib[1].join("")+bib[0].bibend; bibliographyText = bib[0].bibstart+bib[1].join("")+bib[0].bibend;
} }
// if bibliography style not set, set it // if bibliography style not set, set it
if(!this._session.data.style.bibliographyStyleHasBeenSet) { if(!this.data.style.bibliographyStyleHasBeenSet) {
var bibStyle = Zotero.Cite.getBibliographyFormatParameters(bib); var bibStyle = Zotero.Cite.getBibliographyFormatParameters(bib);
// set bibliography style // set bibliography style
@ -1195,7 +1209,7 @@ Zotero.Integration.Fields.prototype._updateDocument = async function(forceCitati
bibStyle.lineSpacing, bibStyle.entrySpacing, bibStyle.tabStops, bibStyle.tabStops.length); bibStyle.lineSpacing, bibStyle.entrySpacing, bibStyle.tabStops, bibStyle.tabStops.length);
// set bibliographyStyleHasBeenSet parameter to prevent further changes // set bibliographyStyleHasBeenSet parameter to prevent further changes
this._session.data.style.bibliographyStyleHasBeenSet = true; this.data.style.bibliographyStyleHasBeenSet = true;
} }
} }
@ -1231,13 +1245,15 @@ Zotero.Integration.Fields.prototype._updateDocument = async function(forceCitati
for (var i=(deleteFields.length-1); i>=0; i--) { for (var i=(deleteFields.length-1); i>=0; i--) {
this._fields[deleteFields[i]].delete(); this._fields[deleteFields[i]].delete();
} }
this._session.processIndices = {} this.processIndices = {}
} }
/** /**
* Brings up the addCitationDialog, prepopulated if a citation is provided * Insert a citation at cursor location or edit the existing one,
* display the citation dialog and perform any field/text inserts after
* the dialog edits are accepted
*/ */
Zotero.Integration.Fields.prototype.addEditCitation = async function (field) { Zotero.Integration.Session.prototype.cite = async function (field) {
var newField; var newField;
var citation; var citation;
@ -1260,10 +1276,10 @@ Zotero.Integration.Fields.prototype.addEditCitation = async function (field) {
// Preparing data to pass into CitationEditInterface // Preparing data to pass into CitationEditInterface
var fieldIndexPromise, citationsByItemIDPromise; var fieldIndexPromise, citationsByItemIDPromise;
if (!this._session.data.prefs.delayCitationUpdates if (!this.data.prefs.delayCitationUpdates
|| !Object.keys(this._session.citationsByItemID).length || !Object.keys(this.citationsByItemID).length
|| this._session._sessionUpToDate) { || this._sessionUpToDate) {
fieldIndexPromise = this.get().then(async function (fields) { fieldIndexPromise = this.getFields().then(async function (fields) {
for (var i = 0, n = fields.length; i < n; i++) { for (var i = 0, n = fields.length; i < n; i++) {
if (await fields[i].equals(field._field)) { if (await fields[i].equals(field._field)) {
// This is needed, because LibreOffice integration plugin caches the field code instead of asking // This is needed, because LibreOffice integration plugin caches the field code instead of asking
@ -1274,20 +1290,20 @@ Zotero.Integration.Fields.prototype.addEditCitation = async function (field) {
} }
return -1; return -1;
}); });
citationsByItemIDPromise = this.updateSession(FORCE_CITATIONS_FALSE).then(function() { citationsByItemIDPromise = this.updateFromDocument(FORCE_CITATIONS_FALSE).then(function() {
return this._session.citationsByItemID; return this.citationsByItemID;
}.bind(this)); }.bind(this));
} }
else { else {
fieldIndexPromise = Zotero.Promise.resolve(-1); fieldIndexPromise = Zotero.Promise.resolve(-1);
citationsByItemIDPromise = Zotero.Promise.resolve(this._session.citationsByItemID); citationsByItemIDPromise = Zotero.Promise.resolve(this.citationsByItemID);
} }
var previewFn = async function (citation) { var previewFn = async function (citation) {
let idx = await fieldIndexPromise; let idx = await fieldIndexPromise;
await citationsByItemIDPromise; await citationsByItemIDPromise;
var [citations, fieldToCitationIdxMapping, citationToFieldIdxMapping] = this._session.getCiteprocLists(); var [citations, fieldToCitationIdxMapping, citationToFieldIdxMapping] = this.getCiteprocLists();
for (var prevIdx = idx-1; prevIdx >= 0; prevIdx--) { for (var prevIdx = idx-1; prevIdx >= 0; prevIdx--) {
if (prevIdx in fieldToCitationIdxMapping) break; if (prevIdx in fieldToCitationIdxMapping) break;
} }
@ -1299,7 +1315,7 @@ Zotero.Integration.Fields.prototype.addEditCitation = async function (field) {
let citationsPost = citations.slice(sliceIdx); let citationsPost = citations.slice(sliceIdx);
let citationID = citation.citationID; let citationID = citation.citationID;
try { try {
var result = this._session.style.previewCitationCluster(citation, citationsPre, citationsPost, "rtf"); var result = this.style.previewCitationCluster(citation, citationsPre, citationsPost, "rtf");
} catch(e) { } catch(e) {
throw e; throw e;
} finally { } finally {
@ -1311,7 +1327,7 @@ Zotero.Integration.Fields.prototype.addEditCitation = async function (field) {
}.bind(this); }.bind(this);
var io = new Zotero.Integration.CitationEditInterface( var io = new Zotero.Integration.CitationEditInterface(
citation, this._session.style.opt.sort_citations, citation, this.style.opt.sort_citations,
fieldIndexPromise, citationsByItemIDPromise, previewFn fieldIndexPromise, citationsByItemIDPromise, previewFn
); );
Zotero.debug(`Editing citation:`); Zotero.debug(`Editing citation:`);
@ -1425,25 +1441,6 @@ Zotero.Integration.CitationEditInterface.prototype = {
}, },
} }
/**
* Keeps track of all session-specific variables
*/
Zotero.Integration.Session = function(doc, app) {
this.embeddedItems = {};
this.embeddedZoteroItems = {};
this.embeddedItemsByURI = {};
this.citationsByIndex = {};
this.resetRequest(doc);
this.primaryFieldType = app.primaryFieldType;
this.secondaryFieldType = app.secondaryFieldType;
this.outputFormat = app.outputFormat || 'rtf';
this._sessionUpToDate = false;
this._app = app;
this.sessionID = Zotero.randomString();
Zotero.Integration.sessions[this.sessionID] = this;
}
/** /**
* Resets per-request variables in the CitationSet * Resets per-request variables in the CitationSet
*/ */
@ -1459,7 +1456,7 @@ Zotero.Integration.Session.prototype.resetRequest = function(doc) {
this.oldCitations = new Set(); this.oldCitations = new Set();
for (let i in this.citationsByIndex) { for (let i in this.citationsByIndex) {
// But ignore indices from this.newIndices. If any are present it means that the last // But ignore indices from this.newIndices. If any are present it means that the last
// call to this.updateSession() was never followed up with this.updateDocument() // call to this.updateFromDocument() was never followed up with this.updateDocument()
// i.e. the operation was user cancelled // i.e. the operation was user cancelled
if (i in this.newIndices) continue; if (i in this.newIndices) continue;
this.oldCitations.add(this.citationsByIndex[i].citationID); this.oldCitations.add(this.citationsByIndex[i].citationID);
@ -1579,7 +1576,7 @@ Zotero.Integration.Session.prototype.setData = async function (data, resetStyle)
* if there wasn't, or rejected with Zotero.Exception.UserCancelled if the dialog was * if there wasn't, or rejected with Zotero.Exception.UserCancelled if the dialog was
* cancelled. * cancelled.
*/ */
Zotero.Integration.Session.prototype.setDocPrefs = Zotero.Promise.coroutine(function* (showImportExport=false) { Zotero.Integration.Session.prototype.setDocPrefs = async function (showImportExport=false) {
var io = new function() { this.wrappedJSObject = this; }; var io = new function() { this.wrappedJSObject = this; };
io.primaryFieldType = this.primaryFieldType; io.primaryFieldType = this.primaryFieldType;
io.secondaryFieldType = this.secondaryFieldType; io.secondaryFieldType = this.secondaryFieldType;
@ -1599,8 +1596,8 @@ Zotero.Integration.Session.prototype.setDocPrefs = Zotero.Promise.coroutine(func
} }
// Make sure styles are initialized for new docs // Make sure styles are initialized for new docs
yield Zotero.Styles.init(); await Zotero.Styles.init();
yield Zotero.Integration.displayDialog('chrome://zotero/content/integration/integrationDocPrefs.xul', '', io); await Zotero.Integration.displayDialog('chrome://zotero/content/integration/integrationDocPrefs.xul', '', io);
if (io.exportDocument) { if (io.exportDocument) {
return this.exportDocument(); return this.exportDocument();
@ -1628,7 +1625,7 @@ Zotero.Integration.Session.prototype.setDocPrefs = Zotero.Promise.coroutine(func
oldData.prefs.automaticJournalAbbreviations != data.prefs.automaticJournalAbbreviations oldData.prefs.automaticJournalAbbreviations != data.prefs.automaticJournalAbbreviations
|| oldData.style.locale != io.locale || oldData.style.locale != io.locale
); );
yield this.setData(data, forceStyleReset); await this.setData(data, forceStyleReset);
// need to do this after setting the data so that we know if it's a note style // need to do this after setting the data so that we know if it's a note style
this.data.prefs.noteType = this.style && this.styleClass == "note" ? io.useEndnotes+1 : 0; this.data.prefs.noteType = this.style && this.styleClass == "note" ? io.useEndnotes+1 : 0;
@ -1644,7 +1641,7 @@ Zotero.Integration.Session.prototype.setDocPrefs = Zotero.Promise.coroutine(func
} }
return oldData || null; return oldData || null;
}) }
Zotero.Integration.Session.prototype.exportDocument = async function() { Zotero.Integration.Session.prototype.exportDocument = async function() {
Zotero.debug("Integration: Exporting the document"); Zotero.debug("Integration: Exporting the document");
@ -1704,13 +1701,13 @@ Zotero.Integration.Session.prototype.importDocument = async function() {
var data = new Zotero.Integration.DocumentData(await this._doc.getDocumentData()); var data = new Zotero.Integration.DocumentData(await this._doc.getDocumentData());
data.prefs.fieldType = this._app.primaryFieldType; data.prefs.fieldType = this._app.primaryFieldType;
await this.setData(data, true); await this.setData(data, true);
await this.fields.get(true); await this.getFields(true);
await this.fields.updateSession(FORCE_CITATIONS_RESET_TEXT); await this.updateFromDocument(FORCE_CITATIONS_RESET_TEXT);
// Make sure we ignore the dont update flags since we do not know what the original text was // Make sure we ignore the dont update flags since we do not know what the original text was
for (let index in this.citationsByIndex) { for (let index in this.citationsByIndex) {
delete this.citationsByIndex[index].properties.dontUpdate; delete this.citationsByIndex[index].properties.dontUpdate;
} }
await this.fields.updateDocument(FORCE_CITATIONS_RESET_TEXT, true, true); await this.updateDocument(FORCE_CITATIONS_RESET_TEXT, true, true);
} finally { } finally {
Zotero.debug(`Integration: Import finished in ${timer.stop()}`); Zotero.debug(`Integration: Import finished in ${timer.stop()}`);
} }
@ -1727,18 +1724,18 @@ Zotero.Integration.Session.prototype.addCitation = Zotero.Promise.coroutine(func
if (action == Zotero.Integration.REMOVE_CODE) { if (action == Zotero.Integration.REMOVE_CODE) {
// Mark for removal and return // Mark for removal and return
this.fields._removeCodeFields[index] = true; this._removeCodeFields[index] = true;
return; return;
} else if (action == Zotero.Integration.DELETE) { } else if (action == Zotero.Integration.DELETE) {
// Mark for deletion and return // Mark for deletion and return
this.fields._deleteFields[index] = true; this._deleteFields[index] = true;
return; return;
} else if (action == Zotero.Integration.UPDATE) { } else if (action == Zotero.Integration.UPDATE) {
this.updateIndices[index] = true; this.updateIndices[index] = true;
} }
// All new fields will initially be marked for deletion because they contain no // All new fields will initially be marked for deletion because they contain no
// citationItems // citationItems
delete this.fields._deleteFields[index]; delete this._deleteFields[index];
citation.properties.zoteroIndex = index; citation.properties.zoteroIndex = index;
citation.properties.noteIndex = noteIndex; citation.properties.noteIndex = noteIndex;
@ -1773,10 +1770,8 @@ Zotero.Integration.Session.prototype.addCitation = Zotero.Promise.coroutine(func
// and either one may need to be updated // and either one may need to be updated
this.newIndices[duplicateIndex] = true; this.newIndices[duplicateIndex] = true;
} }
if (needNewID) { Zotero.debug("Integration: "+citation.citationID+" ("+index+") needs new citationID");
Zotero.debug("Integration: "+citation.citationID+" ("+index+") needs new citationID"); citation.citationID = Zotero.Utilities.randomString();
citation.citationID = Zotero.Utilities.randomString();
}
this.newIndices[index] = true; this.newIndices[index] = true;
} }
// Deal with citations that are copied into the document from somewhere else // Deal with citations that are copied into the document from somewhere else
@ -1859,7 +1854,7 @@ Zotero.Integration.Session.prototype._updateCitations = async function () {
* Restores processor state from document, without requesting citation updates * Restores processor state from document, without requesting citation updates
*/ */
Zotero.Integration.Session.prototype.restoreProcessorState = function() { Zotero.Integration.Session.prototype.restoreProcessorState = function() {
if (this.fields._bibliographyFields.length && !this.bibliography) { if (this._bibliographyFields.length && !this.bibliography) {
throw new Error ("Attempting to restore processor state without loading bibliography"); throw new Error ("Attempting to restore processor state without loading bibliography");
} }
let uncited = []; let uncited = [];
@ -1915,7 +1910,7 @@ Zotero.Integration.Session.prototype.writeDelayedCitation = Zotero.Promise.corou
// Note: fields._fields will only contain items upon first delayed insert // Note: fields._fields will only contain items upon first delayed insert
// after a full update // after a full update
if (this._sessionUpToDate) { if (this._sessionUpToDate) {
var fields = yield this.fields.get(); var fields = yield this.getFields();
for (let i = fields.length - 1; i >= 0; i--) { for (let i = fields.length - 1; i >= 0; i--) {
let field = yield Zotero.Integration.Field.loadExisting(fields[i]); let field = yield Zotero.Integration.Field.loadExisting(fields[i]);
if (field.type == INTEGRATION_TYPE_BIBLIOGRAPHY) { if (field.type == INTEGRATION_TYPE_BIBLIOGRAPHY) {
@ -2573,9 +2568,6 @@ Zotero.Integration.CitationField = class extends Zotero.Integration.Field {
} else if (result == 1) { // No } else if (result == 1) { // No
return false; return false;
} else { // Yes } else { // Yes
var fieldGetter = Zotero.Integration.currentSession.fields,
oldWindow = Zotero.Integration.currentWindow,
oldProgressCallback = this.progressCallback;
// Clear current code and subsequent addEditCitation dialog will be the reselection // Clear current code and subsequent addEditCitation dialog will be the reselection
await this.clearCode(); await this.clearCode();
return this.unserialize(); return this.unserialize();

View file

@ -692,12 +692,12 @@ describe("Zotero.Integration", function () {
doc.fields[1].code = doc.fields[0].code; doc.fields[1].code = doc.fields[0].code;
doc.fields[1].text = doc.fields[0].text; doc.fields[1].text = doc.fields[0].text;
var originalUpdateDocument = Zotero.Integration.Fields.prototype.updateDocument; var originalUpdateDocument = Zotero.Integration.Session.prototype.updateDocument;
var stubUpdateDocument = sinon.stub(Zotero.Integration.Fields.prototype, 'updateDocument'); var stubUpdateDocument = sinon.stub(Zotero.Integration.Session.prototype, 'updateDocument');
try { try {
var indicesLength; var indicesLength;
stubUpdateDocument.callsFake(function() { stubUpdateDocument.callsFake(function() {
indicesLength = Object.keys(Zotero.Integration.currentSession.newIndices).length; indicesLength = Object.keys(this.newIndices).length;
return originalUpdateDocument.apply(this, arguments); return originalUpdateDocument.apply(this, arguments);
}); });
@ -726,12 +726,12 @@ describe("Zotero.Integration", function () {
`"citationID":"${newCitationID}"`); `"citationID":"${newCitationID}"`);
doc.fields[1].text = doc.fields[0].text; doc.fields[1].text = doc.fields[0].text;
var originalUpdateDocument = Zotero.Integration.Fields.prototype.updateDocument; var originalUpdateDocument = Zotero.Integration.Session.prototype.updateDocument;
var stubUpdateDocument = sinon.stub(Zotero.Integration.Fields.prototype, 'updateDocument'); var stubUpdateDocument = sinon.stub(Zotero.Integration.Session.prototype, 'updateDocument');
try { try {
var indices; var indices;
stubUpdateDocument.callsFake(function() { stubUpdateDocument.callsFake(function() {
indices = Object.keys(Zotero.Integration.currentSession.newIndices); indices = Object.keys(this.newIndices);
return originalUpdateDocument.apply(this, arguments); return originalUpdateDocument.apply(this, arguments);
}); });