www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | README | LICENSE

commit 52fd0d992dee8e4a4f3b8e786abbbf708987897c
parent 41c93ab0349147545b4106cdae889495b1139611
Author: Adomas VenĨkauskas <adomas.ven@gmail.com>
Date:   Wed, 17 May 2017 17:15:01 +0300

Add a wrapper class for citation and bibliography fields

Diffstat:
Mchrome/content/zotero/xpcom/integration.js | 484+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
1 file changed, 276 insertions(+), 208 deletions(-)

diff --git a/chrome/content/zotero/xpcom/integration.js b/chrome/content/zotero/xpcom/integration.js @@ -744,10 +744,9 @@ Zotero.Integration.Interface.prototype.editBibliography = function() { return fieldGetter.get(); }).then(function(fields) { var haveBibliography = false; - for(var i=fields.length-1; i>=0; i--) { - var code = fields[i].getCode(); - var [type, content] = fieldGetter.getCodeTypeAndContent(code); - if(type == INTEGRATION_TYPE_BIBLIOGRAPHY) { + for (let i = fields.length-1; i >= 0; i--) { + let field = Zotero.Integration.Field.loadExisting(fields[i]); + if (field.type == INTEGRATION_TYPE_BIBLIOGRAPHY) { haveBibliography = true; break; } @@ -779,10 +778,9 @@ Zotero.Integration.Interface.prototype.addEditBibliography = Zotero.Promise.coro var fields = yield fieldGetter.get(); var haveBibliography = false; - for (var i=fields.length-1; i>=0; i--) { - var code = fields[i].getCode(); - var [type, content] = fieldGetter.getCodeTypeAndContent(code); - if (type == INTEGRATION_TYPE_BIBLIOGRAPHY) { + for (let i = fields.length-1; i >= 0; i--) { + let field = Zotero.Integration.Field.loadExisting(fields[i]); + if (field.type == INTEGRATION_TYPE_BIBLIOGRAPHY) { haveBibliography = true; break; } @@ -792,7 +790,7 @@ Zotero.Integration.Interface.prototype.addEditBibliography = Zotero.Promise.coro yield fieldGetter.updateSession(); yield session.editBibliography(this._doc); } else { - var field = yield fieldGetter.addField(); + var field = new Zotero.Integration.BibliographyField(yield fieldGetter.addField()); field.setCode("BIBL"); yield fieldGetter.updateSession(); } @@ -838,91 +836,84 @@ Zotero.Integration.Interface.prototype.removeCodes = function() { * Displays a dialog to set document preferences (style, footnotes/endnotes, etc.) * @return {Promise} */ -Zotero.Integration.Document.prototype.setDocPrefs = function() { - var me = this, - fieldGetter, +Zotero.Integration.Interface.prototype.setDocPrefs = Zotero.Promise.coroutine(function* () { + var fieldGetter, oldData; - return this._getSession(false, true).then(function(haveSession) { - fieldGetter = new Zotero.Integration.Fields(me._session, me._doc, Zotero.Integration.onFieldError); - var setDocPrefs = me._session.setDocPrefs.bind(me._session, me._doc, - me._app.primaryFieldType, me._app.secondaryFieldType); - if(!haveSession) { - // This is a brand new document; don't try to get fields - return setDocPrefs(); - } else { - // Can get fields while dialog is open - return Zotero.Promise.all([ - fieldGetter.get(), - setDocPrefs() - ]).spread(function (fields, setDocPrefs) { - // Only return value from setDocPrefs - return setDocPrefs; - }); - } - }).then(function(aOldData) { // After setDocPrefs call - oldData = aOldData; - - // Write document data to document - me._doc.setDocumentData(me._session.data.serialize()); - - // If oldData is null, then there was no document data, so we don't need to update - // fields - if(!oldData) return false; - return fieldGetter.get(); - }).then(function(fields) { - if(!fields || !fields.length) return; - - // If there are fields, we will have to convert some things; get a list of what - // we need to deal with - var convertBibliographies = oldData === true - || oldData.prefs.fieldType != me._session.data.prefs.fieldType; - var convertItems = convertBibliographies - || oldData.prefs.noteType != me._session.data.prefs.noteType; - var fieldsToConvert = new Array(); - var fieldNoteTypes = new Array(); - for(var i=0, n=fields.length; i<n; i++) { - var field = fields[i], - fieldCode = field.getCode(), - [type, content] = fieldGetter.getCodeTypeAndContent(fieldCode); + let haveSession = yield this._getSession(false, true); + + fieldGetter = new Zotero.Integration.Fields(this._session, this._doc, Zotero.Integration.onFieldError); + var setDocPrefs = this._session.setDocPrefs.bind(this._session, this._doc, + this._app.primaryFieldType, this._app.secondaryFieldType); - if(convertItems && type === INTEGRATION_TYPE_ITEM) { - var citation = me._session.unserializeCitation(fieldCode); - if(!citation.properties.dontUpdate) { - fieldsToConvert.push(field); - fieldNoteTypes.push(me._session.data.prefs.noteType); - } - } else if(convertBibliographies - && type === INTEGRATION_TYPE_BIBLIOGRAPHY) { + if(!haveSession) { + // This is a brand new document; don't try to get fields + oldData = yield setDocPrefs(); + } else { + // Can get fields while dialog is open + oldData = yield Zotero.Promise.all([ + fieldGetter.get(), + setDocPrefs() + ]).spread(function (fields, setDocPrefs) { + // Only return value from setDocPrefs + return setDocPrefs; + }); + } + + // Write document data to document + this._doc.setDocumentData(this._session.data.serialize()); + + // If oldData is null, then there was no document data, so we don't need to update + // fields + if (!oldData) return false; + + // Perform noteType or fieldType conversion + let fields = yield fieldGetter.get(); + + var convertBibliographies = oldData.prefs.fieldType != this._session.data.prefs.fieldType; + var convertItems = convertBibliographies + || oldData.prefs.noteType != this._session.data.prefs.noteType; + var fieldsToConvert = new Array(); + var fieldNoteTypes = new Array(); + for (var i=0, n=fields.length; i<n; i++) { + let field = Zotero.Integration.Field.loadExisting(fields[i]); + + if (convertItems && field.type === INTEGRATION_TYPE_ITEM) { + var citation = this._session.unserializeCitation(field.code); + if (!citation.properties.dontUpdate) { fieldsToConvert.push(field); - fieldNoteTypes.push(0); + fieldNoteTypes.push(this._session.data.prefs.noteType); } + } else if(convertBibliographies + && type === INTEGRATION_TYPE_BIBLIOGRAPHY) { + fieldsToConvert.push(field); + fieldNoteTypes.push(0); } - - if(fieldsToConvert.length) { - // Pass to conversion function - me._doc.convert(new Zotero.Integration.Document.JSEnumerator(fieldsToConvert), - me._session.data.prefs.fieldType, fieldNoteTypes, - fieldNoteTypes.length); - } - - // Refresh contents - fieldGetter = new Zotero.Integration.Fields(me._session, me._doc, Zotero.Integration.onFieldError); - fieldGetter.ignoreEmptyBibliography = false; - return fieldGetter.updateSession().then(fieldGetter.updateDocument.bind( - fieldGetter, FORCE_CITATIONS_RESET_TEXT, true, true)); - }); -} + } + + if(fieldsToConvert.length) { + // Pass to conversion function + this._doc.convert(new Zotero.Integration.JSEnumerator(fieldsToConvert), + this._session.data.prefs.fieldType, fieldNoteTypes, + fieldNoteTypes.length); + } + + // Refresh contents + fieldGetter = new Zotero.Integration.Fields(this._session, this._doc, Zotero.Integration.onFieldError); + fieldGetter.ignoreEmptyBibliography = false; + return fieldGetter.updateSession().then(fieldGetter.updateDocument.bind( + fieldGetter, FORCE_CITATIONS_RESET_TEXT, true, true)); +}); /** * An exceedingly simple nsISimpleEnumerator implementation */ -Zotero.Integration.Document.JSEnumerator = function(objArray) { +Zotero.Integration.JSEnumerator = function(objArray) { this.objArray = objArray; } -Zotero.Integration.Document.JSEnumerator.prototype.hasMoreElements = function() { +Zotero.Integration.JSEnumerator.prototype.hasMoreElements = function() { return this.objArray.length; } -Zotero.Integration.Document.JSEnumerator.prototype.getNext = function() { +Zotero.Integration.JSEnumerator.prototype.getNext = function() { return this.objArray.shift(); } @@ -985,27 +976,6 @@ Zotero.Integration.Fields.prototype.addField = function(note) { } /** - * Gets the type and content of a field object - */ -Zotero.Integration.Fields.prototype.getCodeTypeAndContent = function(rawCode) { - for (let code of ["ITEM", "CITATION"]) { - if(rawCode.substr(0, code.length) === code) { - return [INTEGRATION_TYPE_ITEM, rawCode.substr(code.length+1)]; - } - } - - if(rawCode.substr(0, 4) === "BIBL") { - return [INTEGRATION_TYPE_BIBLIOGRAPHY, rawCode.substr(5)]; - } - - if(rawCode.substr(0, 4) === "TEMP") { - return [INTEGRATION_TYPE_TEMP, rawCode.substr(5)]; - } - - return [null, rawCode]; -} - -/** * Gets all fields for a document * @return {Promise} Promise resolved with field list. */ @@ -1147,22 +1117,11 @@ Zotero.Integration.Fields.prototype._processFields = Zotero.Promise.coroutine(fu if(!i) i = 0; for(var n = this._fields.length; i<n; i++) { - var field = this._fields[i]; - - try { - var fieldCode = field.getCode(); - } catch(e) { - var corruptFieldException = new Zotero.Integration.CorruptFieldException( - "Field code not retrievable", e); - corruptFieldException.setContext(this, i); - throw corruptFieldException; - } - - var [type, content] = this.getCodeTypeAndContent(fieldCode); - if(type === INTEGRATION_TYPE_ITEM) { + let field = Zotero.Integration.Field.loadExisting(this._fields[i]); + if (field.type === INTEGRATION_TYPE_ITEM) { var noteIndex = field.getNoteIndex(); try { - yield this._session.addCitation(i, noteIndex, content); + yield this._session.addCitation(i, noteIndex, field.code); } catch(e) { var removeCode = false; @@ -1185,13 +1144,13 @@ Zotero.Integration.Fields.prototype._processFields = Zotero.Promise.coroutine(fu throw e; } } - } else if(type === INTEGRATION_TYPE_BIBLIOGRAPHY) { - if(this.ignoreEmptyBibliography && field.getText().trim() === "") { + } else if (field.type === INTEGRATION_TYPE_BIBLIOGRAPHY) { + if (this.ignoreEmptyBibliography && field.text.trim() === "") { this._removeCodeFields[i] = true; } else { this._bibliographyFields.push(field); if(!this._session.bibliographyData && !this._bibliographyData) { - this._bibliographyData = content; + this._bibliographyData = field.code; } } } @@ -1378,93 +1337,84 @@ Zotero.Integration.Fields.prototype._updateDocument = function* (forceCitations, * Brings up the addCitationDialog, prepopulated if a citation is provided */ Zotero.Integration.Fields.prototype.addEditCitation = Zotero.Promise.coroutine(function* (field) { - var newField, citation, fieldIndex, session = this._session; + var newField, citation, session = this._session; // if there's already a citation, make sure we have item IDs in addition to keys - if(field) { + if (field) { + field = Zotero.Integration.Field.loadExisting(field); + if (field.type != INTEGRATION_TYPE_ITEM) { + throw new Zotero.Exception.Alert("integration.error.notInCitation"); + } + try { - var code = field.getCode(); + citation = session.unserializeCitation(field.code); } catch(e) {} - if(code) { - var [type, content] = this.getCodeTypeAndContent(code); - if(type != INTEGRATION_TYPE_ITEM) { - throw new Zotero.Exception.Alert("integration.error.notInCitation"); - } - + if (citation) { try { - citation = session.unserializeCitation(content); - } catch(e) {} - - if(citation) { - try { - yield session.lookupItems(citation); - } catch(e) { - if(e instanceof Zotero.Integration.MissingItemException) { - citation.citationItems = []; - } else { - throw e; - } + yield session.lookupItems(citation); + } catch(e) { + if(e instanceof Zotero.Integration.MissingItemException) { + citation.citationItems = []; + } else { + throw e; } - - if(citation.properties.dontUpdate - || (citation.properties.plainCitation - && field.getText() !== citation.properties.plainCitation)) { - this._doc.activate(); - Zotero.debug("[addEditCitation] Attempting to update manually modified citation.\n" - + "citation.properties.dontUpdate: " + citation.properties.dontUpdate + "\n" - + "Original: " + citation.properties.plainCitation + "\n" - + "Current: " + field.getText() - ); - if(!this._doc.displayAlert(Zotero.getString("integration.citationChanged.edit"), - DIALOG_ICON_WARNING, DIALOG_BUTTONS_OK_CANCEL)) { - throw new Zotero.Exception.UserCancelled("editing citation"); - } + } + + if(citation.properties.dontUpdate + || (citation.properties.plainCitation + && field.getText() !== citation.properties.plainCitation)) { + this._doc.activate(); + Zotero.debug("[addEditCitation] Attempting to update manually modified citation.\n" + + "citation.properties.dontUpdate: " + citation.properties.dontUpdate + "\n" + + "Original: " + citation.properties.plainCitation + "\n" + + "Current: " + field.getText() + ); + if(!this._doc.displayAlert(Zotero.getString("integration.citationChanged.edit"), + DIALOG_ICON_WARNING, DIALOG_BUTTONS_OK_CANCEL)) { + throw new Zotero.Exception.UserCancelled("editing citation"); } - - // make sure it's going to get updated - delete citation.properties["formattedCitation"]; - delete citation.properties["plainCitation"]; - delete citation.properties["dontUpdate"]; } + + // make sure it's going to get updated + delete citation.properties["formattedCitation"]; + delete citation.properties["plainCitation"]; + delete citation.properties["dontUpdate"]; } } else { newField = true; - field = this.addField(true); + field = new Zotero.Integration.CitationField(yield this.addField(true)); } - var me = this; - return Zotero.Promise.resolve(field).then(function(field) { - if(!citation) { - field.setCode("TEMP"); - citation = {"citationItems":[], "properties":{}}; - } - - var io = new Zotero.Integration.CitationEditInterface(citation, field, me, session); - - if(Zotero.Prefs.get("integration.useClassicAddCitationDialog")) { - Zotero.Integration.displayDialog(me._doc, - 'chrome://zotero/content/integration/addCitationDialog.xul', 'alwaysRaised,resizable', - io); - } else { - var mode = (!Zotero.isMac && Zotero.Prefs.get('integration.keepAddCitationDialogRaised') - ? 'popup' : 'alwaysRaised')+',resizable=false'; - Zotero.Integration.displayDialog(me._doc, - 'chrome://zotero/content/integration/quickFormat.xul', mode, io); - } - - if(newField) { - return io.promise.catch(function(e) { - // Try to delete new field on failure - try { - field.delete(); - } catch(e) {} - throw e; - }); - } else { - return io.promise; - } - }); + if (!citation) { + field.setCode("TEMP"); + citation = {"citationItems":[], "properties":{}}; + } + + var io = new Zotero.Integration.CitationEditInterface(citation, field, this, session); + + if (Zotero.Prefs.get("integration.useClassicAddCitationDialog")) { + Zotero.Integration.displayDialog(this._doc, + 'chrome://zotero/content/integration/addCitationDialog.xul', 'alwaysRaised,resizable', + io); + } else { + var mode = (!Zotero.isMac && Zotero.Prefs.get('integration.keepAddCitationDialogRaised') + ? 'popup' : 'alwaysRaised')+',resizable=false'; + Zotero.Integration.displayDialog(this._doc, + 'chrome://zotero/content/integration/quickFormat.xul', mode, io); + } + + if (newField) { + return io.promise.catch(function(e) { + // Try to delete new field on failure + try { + field.delete(); + } catch(e) {} + throw e; + }); + } else { + return io.promise; + } }); /** @@ -2090,31 +2040,31 @@ Zotero.Integration.Session.prototype.lookupItems = Zotero.Promise.coroutine(func /** * Unserializes a JSON citation into a citation object (sans items) */ -Zotero.Integration.Session.prototype.unserializeCitation = function(arg, index) { - var firstBracket = arg.indexOf("{"); - if(firstBracket !== -1) { // JSON field - arg = arg.substr(firstBracket); +Zotero.Integration.Session.prototype.unserializeCitation = function(code, index) { + var firstBracket = code.indexOf("{"); + if (firstBracket !== -1) { // JSON field + code = code.substr(firstBracket); // fix for corrupted fields - var lastBracket = arg.lastIndexOf("}"); - if(lastBracket+1 != arg.length) { + var lastBracket = code.lastIndexOf("}"); + if(lastBracket+1 != code.length) { if(index) this.updateIndices[index] = true; - arg = arg.substr(0, lastBracket+1); + code = code.substr(0, lastBracket+1); } // get JSON try { - var citation = JSON.parse(arg); + var citation = JSON.parse(code); } catch(e) { // fix for corrupted fields (corrupted by Word, somehow) try { - var citation = JSON.parse(arg.substr(0, arg.length-1)); + var citation = JSON.parse(code.substr(0, code.length-1)); } catch(e) { // another fix for corrupted fields (corrupted by 2.1b1) try { - var citation = JSON.parse(arg.replace(/{{((?:\s*,?"unsorted":(?:true|false)|\s*,?"custom":"(?:(?:\\")?[^"]*\s*)*")*)}}/, "{$1}")); + var citation = JSON.parse(code.replace(/{{((?:\s*,?"unsorted":(?:true|false)|\s*,?"custom":"(?:(?:\\")?[^"]*\s*)*")*)}}/, "{$1}")); } catch(e) { - throw new Zotero.Integration.CorruptFieldException(arg, e); + throw new Zotero.Integration.CorruptFieldException(code, e); } } } @@ -2180,14 +2130,14 @@ Zotero.Integration.Session.prototype.unserializeCitation = function(arg, index) } if(!citation.citationID) citation.citationID = Zotero.randomString(); - citation.properties.field = arg; + citation.properties.field = code; } else { // ye olde style field - var underscoreIndex = arg.indexOf("_"); - var itemIDs = arg.substr(0, underscoreIndex).split("|"); + var underscoreIndex = code.indexOf("_"); + var itemIDs = code.substr(0, underscoreIndex).split("|"); - var lastIndex = arg.lastIndexOf("_"); + var lastIndex = code.lastIndexOf("_"); if(lastIndex != underscoreIndex+1) { - var locatorString = arg.substr(underscoreIndex+1, lastIndex-underscoreIndex-1); + var locatorString = code.substr(underscoreIndex+1, lastIndex-underscoreIndex-1); var locators = locatorString.split("|"); } @@ -2871,3 +2821,121 @@ Zotero.Integration.URIMap.prototype.getZoteroItemForURIs = Zotero.Promise.corout return [zoteroItem, needUpdate]; }); +Zotero.Integration.Field = function(field) { + this.dirty = false; + this._field = field; + this.type = INTEGRATION_TYPE_TEMP; + + return new Proxy(this, Zotero.Integration.Field.proxyHandler); +}; + +// Proxy properties through to the integration field +Zotero.Integration.Field.proxyHandler = { + get: function(target, name) { + if (name in target) { + return target[name]; + } + return target._field[name]; + } +}; + + +/** + * Load existing field in document and return correct instance of field type + * @param docField + * @param rawCode + * @param idx + * @returns {Zotero.Integration.Field|Zotero.Integration.CitationField|Zotero.Integration.BibliographyField} + */ +Zotero.Integration.Field.loadExisting = function(docField) { + var field; + let rawCode = docField.getCode(); + + // ITEM/CITATION CSL_ITEM {json: 'data'} + for (let type of ["ITEM", "CITATION"]) { + if (rawCode.substr(0, type.length) === type) { + field = new Zotero.Integration.CitationField(docField); + } + } + // BIBL {json: 'data'} CSL_BIBLIOGRAPHY + if (rawCode.substr(0, 4) === "BIBL") { + field = new Zotero.Integration.BibliographyField(docField); + } + if (field) { + let start = rawCode.indexOf('{'); + if (start != -1) { + field._code = rawCode.substr(start, start + rawCode.lastIndexOf('}')); + } else { + field._code = rawCode.substr(rawCode.indexOf(' ')+1); + } + } + + if (rawCode.substr(0, 4) === "TEMP") { + field = new Zotero.Integration.Field(docField); + field._code = rawCode.substr(5); + } + + return field; +}; + +Zotero.defineProperty(Zotero.Integration.Field.prototype, 'text', { + get: () => this._text = this._text ? this._text : this.getText(), + set: (v) => {this._text = v; this.dirty = true} +}); + +Zotero.defineProperty(Zotero.Integration.Field.prototype, 'code', { + get: () => this._code = this._code ? this._code : this.getCode(), + set: (v) => {this._code = v; this.dirty = true;} +}); + +Zotero.Integration.Field.prototype = { + writeToDoc: function(doc) { + this.dirty = false; + + let text = this._text; + let isRich = false; + // If RTF wrap with RTF tags + if (text.indexOf("\\") !== -1) { + text = "{\\rtf "+text+"}"; + isRich = true; + } + this._field.setText(text, isRich); + + // Boo. Inconsistent. + if (this.type == INTEGRATION_TYPE_ITEM) { + this._field.setCode(`ITEM CSL_CITATION ${JSON.stringify(this._data)}`); + } else if (this.type == INTEGRATION_TYPE_BIBLIOGRAPHY) { + this._field.setCode(`BIBL ${JSON.stringify(this._data)} CSL_BIBLIOGRAPHY`); + } + + // Retrigger retrieval from doc. + this._text = null; + this._code = null; + }, + + getCode: function() { + let code = this._field.getCode(); + let start = code.indexOf('{'); + if (start == -1) { + return '{}'; + } + return code.substr(start, start + code.indexOf('}')); + } +}; + + +Zotero.Integration.CitationField = function(field) { + Zotero.Integration.Field.call(this, field); + this.type = INTEGRATION_TYPE_ITEM; + + return new Proxy(this, Zotero.Integration.Field.proxyHandler); +}; +Zotero.extendClass(Zotero.Integration.Field, Zotero.Integration.CitationField); + +Zotero.Integration.BibliographyField = function(field) { + Zotero.Integration.Field.call(this, field); + this.type = INTEGRATION_TYPE_BIBLIOGRAPHY; + + return new Proxy(this, Zotero.Integration.Field.proxyHandler); +}; +Zotero.extendClass(Zotero.Integration.Field, Zotero.Integration.BibliographyField);