Option to delay updating citation in document.

The checkbox in doc prefs is hidden until an update takes 5s or longer
after which the user is prompted to enable delaying.
This commit is contained in:
Adomas Venčkauskas 2017-06-06 12:07:12 +03:00
parent 6d05c3472b
commit 2827f70daa
6 changed files with 308 additions and 70 deletions

View file

@ -167,10 +167,23 @@ var Zotero_File_Interface_Bibliography = new function() {
document.getElementById("automaticJournalAbbreviations-checkbox").checked = true;
}
}
if (document.getElementById("delayCitationUpdates-checkbox")) {
if (_io.delayCitationUpdates) {
document.getElementById("delayCitationUpdates-checkbox").checked = true;
}
}
// set style to false, in case this is cancelled
_io.style = false;
});
this.openHelpLink = function() {
var url = "https://www.zotero.org/support/word_processor_plugin_usage";
var win = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
Zotero.launchURL(url);
};
/*
* Called when locale is changed
@ -211,6 +224,13 @@ var Zotero_File_Interface_Bibliography = new function() {
document.getElementById("automaticJournalAbbreviations-vbox").hidden =
!selectedStyleObj.usesAbbreviation;
}
// Hide the delayCitationUpdates checkbox before the prompt is shown
document.getElementById("delayCitationUpdates-vbox").hidden = _io.dontAskDelayCitationUpdates == undefined;
// Highlight delay citations checkbox after displaying the alert
// NOTE: Currently unused
if (_io.highlightDelayCitations) {
document.getElementById("delayCitationUpdates-vbox").style.border = "1px dashed #e52e2e"
}
//
// For bibliography.xul
@ -266,6 +286,7 @@ var Zotero_File_Interface_Bibliography = new function() {
}
_io.useEndnotes = document.getElementById("displayAs").selectedIndex;
_io.fieldType = (document.getElementById("formatUsing").selectedIndex == 0 ? _io.primaryFieldType : _io.secondaryFieldType);
_io.delayCitationUpdates = document.getElementById("delayCitationUpdates-checkbox").checked;
}
// remember style and locale if user selected these explicitly

View file

@ -31,10 +31,11 @@
<dialog
id="zotero-doc-prefs-dialog"
orient="vertical"
buttons="accept,cancel"
buttons="accept,cancel,help"
title="&zotero.integration.docPrefs.title;"
onload="Zotero_File_Interface_Bibliography.init();"
ondialogaccept="Zotero_File_Interface_Bibliography.acceptSelection();"
ondialoghelp="Zotero_File_Interface_Bibliography.openHelpLink();"
onclose="document.documentElement.cancelDialog(); event.preventDefault(); event.stopPropagation();"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
persist="screenX screenY"
@ -84,5 +85,10 @@
<checkbox id="automaticJournalAbbreviations-checkbox" label="&zotero.integration.prefs.automaticJournalAbbeviations.label;"/>
<description class="radioDescription">&zotero.integration.prefs.automaticJournalAbbeviations.caption;</description>
</vbox>
<vbox id="delayCitationUpdates-vbox">
<checkbox id="delayCitationUpdates-checkbox" label="&zotero.integration.prefs.delayCitationUpdates.label;" tooltiptext="&zotero.integration.prefs.delayCitationUpdates.tooltip;"/>
<description class="radioDescription">&zotero.integration.prefs.delayCitationUpdates.description;</description>
</vbox>
</vbox>
</dialog>

View file

@ -51,6 +51,8 @@ const INTEGRATION_TYPE_ITEM = 1;
const INTEGRATION_TYPE_BIBLIOGRAPHY = 2;
const INTEGRATION_TYPE_TEMP = 3;
const DELAY_CITATIONS_PROMPT_TIMEOUT = 5/*seconds*/;
Zotero.Integration = new function() {
Components.utils.import("resource://gre/modules/Services.jsm");
@ -212,18 +214,25 @@ Zotero.Integration = new function() {
return;
}
inProgress = true;
var application = Zotero.Integration.getApplication(agent, command, docId);
Zotero.debug(`Integration: ${agent}-${command}${docId ? `:'${docId}'` : ''} invoked`)
var startTime = (new Date()).getTime();
// Try to execute the command; otherwise display an error in alert service or word processor
// (depending on what is possible)
Zotero.Integration.currentDoc = document = (application.getDocument && docId ? application.getDocument(docId) : application.getActiveDocument());
Zotero.Integration.currentSession = session = yield Zotero.Integration.getSession(application, document);
// TODO: this is pretty awful
session.fields = new Zotero.Integration.Fields(session, document);
session._doc = document;
try {
// Word for windows throws RPC_E_CANTCALLOUT_ININPUTSYNCCALL if we invoke an OLE call in the
// current event loop (which.. who would have guessed would be the case?)
yield Zotero.Promise.delay();
var application = Zotero.Integration.getApplication(agent, command, docId);
Zotero.Integration.currentDoc = document = (application.getDocument && docId ? application.getDocument(docId) : application.getActiveDocument());
Zotero.Integration.currentSession = session = yield Zotero.Integration.getSession(application, document);
// TODO: this is pretty awful
session.fields = new Zotero.Integration.Fields(session, document);
session._doc = document;
yield (new Zotero.Integration.Interface(application, document, session))[command]();
document.setDocumentData(session.data.serialize());
}
catch (e) {
if(!(e instanceof Zotero.Exception.UserCancelled)) {
@ -262,12 +271,16 @@ Zotero.Integration = new function() {
} finally {
Zotero.logError(e);
}
} else {
// If user cancels we should still write the currently assigned session ID
document.setDocumentData(session.data.serialize());
}
}
finally {
var diff = ((new Date()).getTime() - startTime)/1000;
Zotero.debug(`Integration: ${agent}-${command}${docId ? `:'${docId}'` : ''} complete in ${diff}s`)
if (document) {
try {
document.setDocumentData(session.data.serialize());
document.cleanup();
document.activate();
@ -377,19 +390,20 @@ Zotero.Integration = new function() {
throw new Zotero.Exception.Alert("integration.error.fieldTypeMismatch",
[], "integration.error.title");
}
if (Zotero.Integration.sessions[data.sessionID]) {
// If communication occured with this document since restart
return Zotero.Integration.sessions[data.sessionID];
}
session = Zotero.Integration.sessions[data.sessionID];
}
if (!session) {
session = new Zotero.Integration.Session(doc, app);
session.reload = true;
}
session = new Zotero.Integration.Session(doc, app);
try {
yield session.setData(data);
} catch(e) {
// make sure style is defined
if (e instanceof Zotero.Exception.Alert && e.name === "integration.error.invalidStyle") {
if (data.style.styleID) {
session.reload = true;
let trustedSource = /^https?:\/\/(www\.)?(zotero\.org|citationstyles\.org)/.test(data.style.styleID);
let errorString = Zotero.getString("integration.error.styleMissing", data.style.styleID);
if (trustedSource ||
@ -422,7 +436,6 @@ Zotero.Integration = new function() {
throw e;
}
}
session.reload = true;
return session;
});
@ -461,7 +474,12 @@ Zotero.Integration.Interface.prototype.addCitation = Zotero.Promise.coroutine(fu
let [idx, field, citation] = yield this._session.fields.addEditCitation(null);
yield this._session.addCitation(idx, field.noteIndex, citation);
return this._session.fields.updateDocument(FORCE_CITATIONS_FALSE, false, false);
if (this._session.data.prefs.delayCitationUpdates) {
return this._session.writeDelayedCitation(idx, field, citation);
} else {
return this._session.fields.updateDocument(FORCE_CITATIONS_FALSE, false, false);
}
});
/**
@ -487,7 +505,11 @@ Zotero.Integration.Interface.prototype.addEditCitation = Zotero.Promise.coroutin
let [idx, field, citation] = yield this._session.fields.addEditCitation(docField);
yield this._session.addCitation(idx, field.noteIndex, citation);
return this._session.fields.updateDocument(FORCE_CITATIONS_FALSE, false, false);
if (this._session.data.prefs.delayCitationUpdates) {
return this._session.writeDelayedCitation(idx, field, citation);
} else {
return this._session.fields.updateDocument(FORCE_CITATIONS_FALSE, false, false);
}
});
/**
@ -588,14 +610,12 @@ Zotero.Integration.Interface.prototype.addEditBibliography = Zotero.Promise.coro
* Updates the citation data for all citations and bibliography entries.
* @return {Promise}
*/
Zotero.Integration.Interface.prototype.refresh = function() {
var me = this;
return this._session.init(true, false).then(function() {
// Send request, forcing update of citations and bibliography
return me._session.fields.updateSession(FORCE_CITATIONS_REGENERATE).then(function() {
return me._session.fields.updateDocument(FORCE_CITATIONS_REGENERATE, true, false);
});
});
Zotero.Integration.Interface.prototype.refresh = async function() {
await this._session.init(true, false)
this._session.reload = this._session.data.prefs.delayCitationUpdates;
await this._session.fields.updateSession(FORCE_CITATIONS_REGENERATE)
await this._session.fields.updateDocument(FORCE_CITATIONS_REGENERATE, true, false);
}
/**
@ -898,11 +918,26 @@ Zotero.Integration.Fields.prototype._processFields = Zotero.Promise.coroutine(fu
*/
Zotero.Integration.Fields.prototype.updateDocument = Zotero.Promise.coroutine(function* (forceCitations, forceBibliography,
ignoreCitationChanges) {
// Iterate through citations, yielding for UI updates
yield Zotero.Promise.each(this._session._updateCitations(), () => {});
var startTime = (new Date()).getTime();
yield Zotero.Promise.each(
this._updateDocument(forceCitations, forceBibliography, ignoreCitationChanges), () => {});
yield this._session._updateCitations()
yield this._updateDocument(forceCitations, forceBibliography, ignoreCitationChanges)
var diff = ((new Date()).getTime() - startTime)/1000;
Zotero.debug(`Integration: updateDocument complete in ${diff}s`)
// 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) {
this._doc.activate();
var result = this._doc.displayAlert(Zotero.getString('integration.delayCitationUpdates.alert'),
DIALOG_ICON_WARNING, DIALOG_BUTTONS_YES_NO_CANCEL);
if (result == 2) {
this._session.data.prefs.delayCitationUpdates = true;
}
if (result) {
this._session.data.prefs.dontAskDelayCitationUpdates = true;
// yield this._session.setDocPrefs(true);
}
}
});
/**
@ -912,7 +947,7 @@ Zotero.Integration.Fields.prototype.updateDocument = Zotero.Promise.coroutine(fu
* @param {Boolean} [ignoreCitationChanges] Whether to ignore changes to citations that have been
* modified since they were created, instead of showing a warning
*/
Zotero.Integration.Fields.prototype._updateDocument = function* (forceCitations, forceBibliography,
Zotero.Integration.Fields.prototype._updateDocument = async function(forceCitations, forceBibliography,
ignoreCitationChanges) {
if(this.progressCallback) {
var nFieldUpdates = Object.keys(this._session.updateIndices).length;
@ -929,34 +964,37 @@ Zotero.Integration.Fields.prototype._updateDocument = function* (forceCitations,
} catch(e) {
Zotero.logError(e);
}
yield;
}
// Jump to next event loop step for UI updates
await Zotero.Promise.delay();
var citation = this._session.citationsByIndex[i];
let citationField = citation._field;
if(!citation.properties.dontUpdate) {
var formattedCitation = citation.properties.custom
if (!citation.properties.dontUpdate) {
var formattedCitation = citation.properties.formattedCitation && citation.properties.custom
? citation.properties.custom : citation.text;
var plainCitation = citation.properties.plainCitation && citationField.text;
if(forceCitations === FORCE_CITATIONS_RESET_TEXT
|| citation.properties.formattedCitation !== formattedCitation) {
// Check if citation has been manually modified
if(!ignoreCitationChanges && citation.properties.plainCitation) {
var plainCitation = citationField.text;
if(plainCitation !== citation.properties.plainCitation) {
// Citation manually modified; ask user if they want to save changes
Zotero.debug("[_updateDocument] Attempting to update manually modified citation.\n"
+ "Original: " + citation.properties.plainCitation + "\n"
+ "Current: " + plainCitation
);
citationField.select();
var result = this._doc.displayAlert(
Zotero.getString("integration.citationChanged")+"\n\n"+Zotero.getString("integration.citationChanged.description"),
DIALOG_ICON_CAUTION, DIALOG_BUTTONS_YES_NO);
if(result) {
citation.properties.dontUpdate = true;
}
// If we're not specifically *not* trying to regen text
if (forceCitations != FORCE_CITATIONS_FALSE
// Or metadata has changed thus changing the formatted citation
|| (citation.properties.formattedCitation !== formattedCitation)
// Or we shouldn't ignore citation changes and the citation text has changed
|| (!ignoreCitationChanges && plainCitation !== citation.properties.plainCitation)) {
if (plainCitation !== citation.properties.plainCitation) {
// Citation manually modified; ask user if they want to save changes
Zotero.debug("[_updateDocument] Attempting to update manually modified citation.\n"
+ "Original: " + citation.properties.plainCitation + "\n"
+ "Current: " + plainCitation
);
citationField.select();
var result = this._doc.displayAlert(
Zotero.getString("integration.citationChanged")+"\n\n"+Zotero.getString("integration.citationChanged.description"),
DIALOG_ICON_CAUTION, DIALOG_BUTTONS_YES_NO);
if (result) {
citation.properties.dontUpdate = true;
}
}
@ -972,8 +1010,10 @@ Zotero.Integration.Fields.prototype._updateDocument = function* (forceCitations,
citation.properties.formattedCitation = formattedCitation;
citation.properties.plainCitation = citationField.getText();
// But we still need writeToDoc to trigger RTF write for LO (see comment in writeToDoc())
citationField.text = formattedCitation;
if (isRich) {
// But we still need writeToDoc to trigger RTF write for LO (see comment in writeToDoc())
citationField.text = formattedCitation;
}
}
}
}
@ -1027,8 +1067,9 @@ Zotero.Integration.Fields.prototype._updateDocument = function* (forceCitations,
} catch(e) {
Zotero.logError(e);
}
yield;
}
// Jump to next event loop step for UI updates
await Zotero.Promise.delay();
if (bibliographyText) {
field.text = bibliographyText;
@ -1081,14 +1122,17 @@ Zotero.Integration.Fields.prototype.addEditCitation = Zotero.Promise.coroutine(f
}
});
var citationsByItemIDPromise = fieldIndexPromise.then(function() {
return this.updateSession(FORCE_CITATIONS_FALSE);
}.bind(this)).then(function() {
return this._session.citationsByItemID;
}.bind(this));
var citationsByItemIDPromise;
if (this._session.data.prefs.delayCitationUpdates) {
citationsByItemIDPromise = Zotero.Promise.resolve(this._session.citationsByItemID);
} else {
citationsByItemIDPromise = fieldIndexPromise.then(function() {
return this.updateSession(FORCE_CITATIONS_FALSE);
}.bind(this)).then(function() {
return this._session.citationsByItemID;
}.bind(this));
}
// Required for preview fn
citation.properties.noteIndex = field.noteIndex;
var previewFn = Zotero.Promise.coroutine(function* (citation) {
let idx = yield fieldIndexPromise;
yield citationsByItemIDPromise;
@ -1352,7 +1396,7 @@ Zotero.Integration.Session.prototype.setData = Zotero.Promise.coroutine(function
* if there wasn't, or rejected with Zotero.Exception.UserCancelled if the dialog was
* cancelled.
*/
Zotero.Integration.Session.prototype.setDocPrefs = Zotero.Promise.coroutine(function* () {
Zotero.Integration.Session.prototype.setDocPrefs = Zotero.Promise.coroutine(function* (highlightDelayCitations=false) {
var io = new function() { this.wrappedJSObject = this; };
io.primaryFieldType = this.primaryFieldType;
io.secondaryFieldType = this.secondaryFieldType;
@ -1362,6 +1406,9 @@ Zotero.Integration.Session.prototype.setDocPrefs = Zotero.Promise.coroutine(func
io.locale = this.data.style.locale;
io.useEndnotes = this.data.prefs.noteType == 0 ? 0 : this.data.prefs.noteType-1;
io.fieldType = this.data.prefs.fieldType;
io.delayCitationUpdates = this.data.prefs.delayCitationUpdates;
io.dontAskDelayCitationUpdates = this.data.prefs.dontAskDelayCitationUpdates;
io.highlightDelayCitations = highlightDelayCitations;
io.automaticJournalAbbreviations = this.data.prefs.automaticJournalAbbreviations;
io.requireStoreReferences = !Zotero.Utilities.isEmpty(this.embeddedItems);
}
@ -1380,8 +1427,10 @@ Zotero.Integration.Session.prototype.setDocPrefs = Zotero.Promise.coroutine(func
data.sessionID = oldData.sessionID;
data.style.styleID = io.style;
data.style.locale = io.locale;
data.prefs = oldData ? Object.assign({}, oldData.prefs) : {};
data.prefs.fieldType = io.fieldType;
data.prefs.automaticJournalAbbreviations = io.automaticJournalAbbreviations;
data.prefs.delayCitationUpdates = io.delayCitationUpdates
var forceStyleReset = oldData
&& (
@ -1396,9 +1445,11 @@ Zotero.Integration.Session.prototype.setDocPrefs = Zotero.Promise.coroutine(func
if (!oldData || oldData.style.styleID != data.style.styleID
|| oldData.prefs.noteType != data.prefs.noteType
|| oldData.prefs.fieldType != data.prefs.fieldType
|| (!data.prefs.delayCitationUpdates && oldData.prefs.delayCitationUpdates != data.prefs.delayCitationUpdates)
|| oldData.prefs.automaticJournalAbbreviations != data.prefs.automaticJournalAbbreviations) {
// This will cause us to regenerate all citations
this.regenAll = true;
this.reload = true;
}
return oldData || null;
@ -1450,7 +1501,6 @@ Zotero.Integration.Session.prototype.addCitation = Zotero.Promise.coroutine(func
Zotero.debug("Integration: "+citation.citationID+" ("+index+") needs new citationID");
citation.citationID = Zotero.randomString();
}
this.newIndices[index] = true;
this.updateIndices[index] = true;
}
Zotero.debug("Integration: Adding citationID "+citation.citationID);
@ -1468,7 +1518,7 @@ Zotero.Integration.Session.prototype.getCiteprocLists = function() {
/**
* Updates the list of citations to be serialized to the document
*/
Zotero.Integration.Session.prototype._updateCitations = function* () {
Zotero.Integration.Session.prototype._updateCitations = async function () {
Zotero.debug("Integration: Indices of new citations");
Zotero.debug(Object.keys(this.newIndices));
Zotero.debug("Integration: Indices of updated citations");
@ -1478,6 +1528,8 @@ Zotero.Integration.Session.prototype._updateCitations = function* () {
for (let indexList of [this.newIndices, this.updateIndices]) {
for (let index in indexList) {
// Jump to next event loop step for UI updates
await Zotero.Promise.delay();
index = parseInt(index);
var citation = this.citationsByIndex[index];
@ -1503,9 +1555,7 @@ Zotero.Integration.Session.prototype._updateCitations = function* () {
this.citationsByIndex[idx].text = text;
}
// Yield for UI updates
delete this.newIndices[index];
yield;
}
}
}
@ -1531,6 +1581,32 @@ Zotero.Integration.Session.prototype.restoreProcessorState = function() {
this.style.rebuildProcessorState(citations, 'rtf', uncited);
}
Zotero.Integration.Session.prototype.writeDelayedCitation = Zotero.Promise.coroutine(function* (idx, field, citation) {
try {
var text = citation.properties.custom || this.style.previewCitationCluster(citation, [], [], "rtf");
} catch(e) {
throw e;
}
text = `\\uldash{${text}}`;
// Make sure we'll prompt for manually edited citations
if(!citation.properties.dontUpdate) {
// setText and getText here bypass the setter/getter abstraction
text = '{\\rtf' + text + '}';
field.setText(text, true);
field.text = text;
citation.properties.formattedCitation = text;
citation.properties.plainCitation = field.getText();
}
field.code = citation.serialize();
field.text = text;
field.writeToDoc();
});
/**
* Edits integration bibliography
* @param {Zotero.Integration.Bibliography} bibliography
@ -1681,7 +1757,8 @@ Zotero.Integration.DocumentData.prototype.serialize = function() {
}
// Otherwise default to XML for now
var prefs = "";
for(var pref in this.prefs) {
for (var pref in this.prefs) {
if (!this.prefs[pref]) continue;
prefs += `<pref name="${Zotero.Utilities.htmlSpecialChars(pref)}" `+
`value="${Zotero.Utilities.htmlSpecialChars(this.prefs[pref].toString())}"/>`;
}

View file

@ -231,6 +231,10 @@
<!ENTITY zotero.integration.prefs.bookmarks.label "Bookmarks">
<!ENTITY zotero.integration.prefs.bookmarks.caption "Bookmarks can be shared between Word and LibreOffice, but may cause errors if accidentally modified and cannot be inserted into footnotes.">
<!ENTITY zotero.integration.prefs.delayCitationUpdates.label "Delay citation updates until manual refresh">
<!ENTITY zotero.integration.prefs.delayCitationUpdates.tooltip "Citations with pending updates are visually highlighted">
<!ENTITY zotero.integration.prefs.delayCitationUpdates.description "Delaying updates can speed up citation insertion in large documents.">
<!ENTITY zotero.integration.prefs.automaticJournalAbbeviations.label "Use MEDLINE journal abbreviations">
<!ENTITY zotero.integration.prefs.automaticJournalAbbeviations.caption "The “Journal Abbr” field will be ignored.">

View file

@ -870,6 +870,7 @@ integration.corruptBibliography.description = All items cited in the text will a
integration.citationChanged = You have modified this citation since Zotero generated it. Do you want to keep your modifications and prevent future updates?
integration.citationChanged.description = Clicking "Yes" will prevent Zotero from updating this citation if you add additional citations, switch styles, or modify the item to which it refers. Clicking "No" will erase your changes.
integration.citationChanged.edit = You have modified this citation since Zotero generated it. Editing will clear your modifications. Do you want to continue?
integration.delayCitationUpdates.alert = Updating citations in this document is taking a long time. Would you like to delay citation updates until manual refresh?\n\nYou can change this setting later in the document preferences.
styles.install.title = Install Style
styles.install.unexpectedError = An unexpected error occurred while installing "%1$S"

View file

@ -292,7 +292,10 @@ describe("Zotero.Integration", function () {
testItems = [];
for (let i = 0; i < 5; i++) {
testItems.push(yield createDataObject('item', {libraryID: Zotero.Libraries.userLibraryID}));
let testItem = yield createDataObject('item', {libraryID: Zotero.Libraries.userLibraryID});
testItem.setField('title', `title${1}`);
testItem.setCreator(0, {creatorType: 'author', name: `Author No${i}`});
testItems.push(testItem);
}
setAddEditItems(testItems[0]);
@ -492,6 +495,132 @@ describe("Zotero.Integration", function () {
getCiteprocBibliographySpy.restore();
});
describe('when original citation text has been modified', function() {
var displayAlertStub;
before(function* () {
displayAlertStub = sinon.stub(DocumentPluginDummy.Document.prototype, 'displayAlert').returns(0);
});
beforeEach(function() {
displayAlertStub.reset();
});
after(function() {
displayAlertStub.restore();
});
it('should keep modification if "Cancel" selected in editCitation triggered alert', async function () {
await insertMultipleCitations.call(this);
var docID = this.test.fullTitle();
var doc = applications[docID].doc;
doc.fields[0].text = "modified";
sinon.stub(doc, 'cursorInField').returns(doc.fields[0]);
sinon.stub(doc, 'canInsertField').returns(false);
await execCommand('addEditCitation', docID);
assert.equal(doc.fields.length, 2);
assert.equal(doc.fields[0].text, "modified");
});
it('should display citation dialog if "OK" selected in editCitation triggered alert', async function () {
await insertMultipleCitations.call(this);
var docID = this.test.fullTitle();
var doc = applications[docID].doc;
let origText = doc.fields[0].text;
doc.fields[0].text = "modified";
// Return OK
displayAlertStub.returns(1);
sinon.stub(doc, 'cursorInField').returns(doc.fields[0]);
sinon.stub(doc, 'canInsertField').returns(false);
setAddEditItems(testItems[0]);
await execCommand('addEditCitation', docID);
assert.isTrue(displayAlertStub.called);
assert.equal(doc.fields.length, 2);
assert.equal(doc.fields[0].text, origText);
});
it('should set dontUpdate: true if "yes" selected in refresh prompt', async function() {
await insertMultipleCitations.call(this);
var docID = this.test.fullTitle();
var doc = applications[docID].doc;
var citation = (new Zotero.Integration.CitationField(doc.fields[0])).unserialize();
assert.isNotOk(citation.properties.dontUpdate);
doc.fields[0].text = "modified";
// Return Yes
displayAlertStub.returns(1);
await execCommand('refresh', docID);
assert.isTrue(displayAlertStub.called);
assert.equal(doc.fields.length, 2);
assert.equal(doc.fields[0].text, "modified");
var citation = (new Zotero.Integration.CitationField(doc.fields[0])).unserialize();
assert.isOk(citation.properties.dontUpdate);
});
it('should reset citation text if "no" selected in refresh prompt', async function() {
await insertMultipleCitations.call(this);
var docID = this.test.fullTitle();
var doc = applications[docID].doc;
var citation = (new Zotero.Integration.CitationField(doc.fields[0])).unserialize();
assert.isNotOk(citation.properties.dontUpdate);
let origText = doc.fields[0].text;
doc.fields[0].text = "modified";
// Return No
displayAlertStub.returns(0);
await execCommand('refresh', docID);
assert.isTrue(displayAlertStub.called);
assert.equal(doc.fields.length, 2);
assert.equal(doc.fields[0].text, origText);
var citation = (new Zotero.Integration.CitationField(doc.fields[0])).unserialize();
assert.isNotOk(citation.properties.dontUpdate);
});
});
describe('when delayCitationUpdates is set', function() {
it('should insert a citation with wave underlining', function* (){
yield insertMultipleCitations.call(this);
var docID = this.test.fullTitle();
var doc = applications[docID].doc;
var data = new Zotero.Integration.DocumentData(doc.data);
data.prefs.delayCitationUpdates = true;
doc.data = data.serialize();
var setTextSpy = sinon.spy(DocumentPluginDummy.Field.prototype, 'setText');
setAddEditItems(testItems[3]);
yield execCommand('addEditCitation', docID);
assert.isTrue(setTextSpy.lastCall.args[0].includes('\\uldash'));
setTextSpy.restore();
});
it('should not write to any other fields besides the one being updated', function* () {
yield insertMultipleCitations.call(this);
var docID = this.test.fullTitle();
var doc = applications[docID].doc;
var data = new Zotero.Integration.DocumentData(doc.data);
data.prefs.delayCitationUpdates = true;
doc.data = data.serialize();
var setTextSpy = sinon.spy(DocumentPluginDummy.Field.prototype, 'setText');
var setCodeSpy = sinon.spy(DocumentPluginDummy.Field.prototype, 'setCode');
setAddEditItems(testItems[3]);
yield execCommand('addEditCitation', docID);
var field = setTextSpy.firstCall.thisValue;
for (let i = 0; i < setTextSpy.callCount; i++) {
assert.isTrue(field.equals(setTextSpy.getCall(i).thisValue));
}
for (let i = 0; i < setCodeSpy.callCount; i++) {
assert.isTrue(field.equals(setCodeSpy.getCall(i).thisValue));
}
setTextSpy.restore();
setCodeSpy.restore();
})
});
});
describe('#addEditBibliography', function() {