Move citation unserialization

Using new es6 class syntax because getters/setters don't
retain `this` context with Zotero.extendClass and we're building
with at least FX45 on every platform now where the syntax is supported
This commit is contained in:
Adomas Venčkauskas 2017-05-17 17:12:05 +03:00
parent 52fd0d992d
commit 41db61ecb9

View file

@ -878,7 +878,7 @@ Zotero.Integration.Interface.prototype.setDocPrefs = Zotero.Promise.coroutine(fu
let field = Zotero.Integration.Field.loadExisting(fields[i]);
if (convertItems && field.type === INTEGRATION_TYPE_ITEM) {
var citation = this._session.unserializeCitation(field.code);
var citation = field.unserialize();
if (!citation.properties.dontUpdate) {
fieldsToConvert.push(field);
fieldNoteTypes.push(this._session.data.prefs.noteType);
@ -1121,7 +1121,7 @@ Zotero.Integration.Fields.prototype._processFields = Zotero.Promise.coroutine(fu
if (field.type === INTEGRATION_TYPE_ITEM) {
var noteIndex = field.getNoteIndex();
try {
yield this._session.addCitation(i, noteIndex, field.code);
yield this._session.addCitation(i, noteIndex, field.unserialize());
} catch(e) {
var removeCode = false;
@ -1347,7 +1347,7 @@ Zotero.Integration.Fields.prototype.addEditCitation = Zotero.Promise.coroutine(f
}
try {
citation = session.unserializeCitation(field.code);
citation = field.unserialize();
} catch(e) {}
if (citation) {
@ -1869,17 +1869,9 @@ Zotero.Integration._oldCitationLocatorMap = {
/**
* Adds a citation to the arrays representing the document
*/
Zotero.Integration.Session.prototype.addCitation = Zotero.Promise.coroutine(function* (index, noteIndex, arg) {
Zotero.Integration.Session.prototype.addCitation = Zotero.Promise.coroutine(function* (index, noteIndex, citation) {
var index = parseInt(index, 10);
if(typeof(arg) == "string") { // text field
if(arg == "!" || arg == "X") return;
var citation = this.unserializeCitation(arg, index);
} else { // a citation already
var citation = arg;
}
// get items
yield this.lookupItems(citation, index);
@ -2037,127 +2029,6 @@ 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(code, index) {
var firstBracket = code.indexOf("{");
if (firstBracket !== -1) { // JSON field
code = code.substr(firstBracket);
// fix for corrupted fields
var lastBracket = code.lastIndexOf("}");
if(lastBracket+1 != code.length) {
if(index) this.updateIndices[index] = true;
code = code.substr(0, lastBracket+1);
}
// get JSON
try {
var citation = JSON.parse(code);
} catch(e) {
// fix for corrupted fields (corrupted by Word, somehow)
try {
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(code.replace(/{{((?:\s*,?"unsorted":(?:true|false)|\s*,?"custom":"(?:(?:\\")?[^"]*\s*)*")*)}}/, "{$1}"));
} catch(e) {
throw new Zotero.Integration.CorruptFieldException(code, e);
}
}
}
// fix for uppercase citation codes
if(citation.CITATIONITEMS) {
if(index) this.updateIndices[index] = true;
citation.citationItems = [];
for (var i=0; i<citation.CITATIONITEMS.length; i++) {
for (var j in citation.CITATIONITEMS[i]) {
switch (j) {
case 'ITEMID':
var field = 'itemID';
break;
// 'position', 'custom'
default:
var field = j.toLowerCase();
}
if (!citation.citationItems[i]) {
citation.citationItems[i] = {};
}
citation.citationItems[i][field] = citation.CITATIONITEMS[i][j];
}
}
}
if(!citation.properties) citation.properties = {};
for (let citationItem of citation.citationItems) {
// for upgrade from Zotero 2.0 or earlier
if(citationItem.locatorType) {
citationItem.label = citationItem.locatorType;
delete citationItem.locatorType;
} else if(citationItem.suppressAuthor) {
citationItem["suppress-author"] = citationItem["suppressAuthor"];
delete citationItem.suppressAuthor;
}
// fix for improper upgrade from Zotero 2.1 in <2.1.5
if(parseInt(citationItem.label) == citationItem.label) {
const locatorTypeTerms = ["page", "book", "chapter", "column", "figure", "folio",
"issue", "line", "note", "opus", "paragraph", "part", "section", "sub verbo",
"volume", "verse"];
citationItem.label = locatorTypeTerms[parseInt(citationItem.label)];
}
// for update from Zotero 2.1 or earlier
if(citationItem.uri) {
citationItem.uris = citationItem.uri;
delete citationItem.uri;
}
}
// for upgrade from Zotero 2.0 or earlier
if(citation.sort) {
citation.properties.unsorted = !citation.sort;
delete citation.sort;
}
if(citation.custom) {
citation.properties.custom = citation.custom;
delete citation.custom;
}
if(!citation.citationID) citation.citationID = Zotero.randomString();
citation.properties.field = code;
} else { // ye olde style field
var underscoreIndex = code.indexOf("_");
var itemIDs = code.substr(0, underscoreIndex).split("|");
var lastIndex = code.lastIndexOf("_");
if(lastIndex != underscoreIndex+1) {
var locatorString = code.substr(underscoreIndex+1, lastIndex-underscoreIndex-1);
var locators = locatorString.split("|");
}
var citationItems = new Array();
for(var i=0; i<itemIDs.length; i++) {
var citationItem = {id:itemIDs[i]};
if(locators) {
citationItem.locator = locators[i].substr(1);
citationItem.label = Zotero.Integration._oldCitationLocatorMap[locators[i][0]];
}
citationItems.push(citationItem);
}
var citation = {"citationItems":citationItems, properties:{}};
if(index) this.updateIndices[index] = true;
}
return citation;
}
/**
* Gets integration bibliography
*/
@ -2821,24 +2692,60 @@ 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];
Zotero.Integration.Field = class {
constructor(field) {
// This is not the best solution in terms of performance
for (let prop in field) {
if (!(prop in this)) this[prop] = field[prop];
}
return target._field[name];
this.dirty = false;
this._field = field;
this.type = INTEGRATION_TYPE_TEMP;
}
};
get text() {return this._text = this._text ? this._text : this.getText()}
set text(v) {return this._text = v; this.dirty = true}
get code() {return this._code = this._code ? this._code : this.getCode()}
set code(v) {return this._code = v; this.dirty = true}
clearCode() {
this.code = '{}';
};
writeToDoc(doc) {
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.code)}`);
} else if (this.type == INTEGRATION_TYPE_BIBLIOGRAPHY) {
this._field.setCode(`BIBL ${this.code} CSL_BIBLIOGRAPHY`);
}
this.dirty = false;
// Retrigger retrieval from doc.
this._text = null;
this._code = null;
};
getCode() {
let code = this._field.getCode();
let start = code.indexOf('{');
if (start == -1) {
return '{}';
}
return code.substr(start, start + code.indexOf('}'));
};
};
/**
* Load existing field in document and return correct instance of field type
@ -2878,64 +2785,150 @@ Zotero.Integration.Field.loadExisting = function(docField) {
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;
},
Zotero.Integration.CitationField = class extends Zotero.Integration.Field {
constructor(field) {
super(field);
this.type = INTEGRATION_TYPE_ITEM;
}
getCode: function() {
let code = this._field.getCode();
let start = code.indexOf('{');
if (start == -1) {
return '{}';
/**
* Don't be fooled, this should be as simple as JSON.parse().
* The schema for the code is defined @ https://raw.githubusercontent.com/citation-style-language/schema/master/csl-citation.json
*
* However, over the years and different versions of Zotero there's been changes to the schema,
* incorrect serialization, etc. Therefore this function is cruft-full and we can't get rid of it.
*
* @returns {{citationItems: Object[], properties: Object}}
*/
unserialize() {
function unserialize(code) {
try {
return JSON.parse(code);
} catch(e) {
// fix for corrupted fields (corrupted by 2.1b1)
try {
return JSON.parse(code.replace(/{{((?:\s*,?"unsorted":(?:true|false)|\s*,?"custom":"(?:(?:\\")?[^"]*\s*)*")*)}}/, "{$1}"));
} catch(e) {
throw new Zotero.Integration.CorruptFieldException(code, e);
}
}
}
return code.substr(start, start + code.indexOf('}'));
function upgradeCruft(citation, code) {
// fix for uppercase citation codes
if(citation.CITATIONITEMS) {
citation.citationItems = [];
for (var i=0; i<citation.CITATIONITEMS.length; i++) {
for (var j in citation.CITATIONITEMS[i]) {
switch (j) {
case 'ITEMID':
var field = 'itemID';
break;
// 'position', 'custom'
default:
var field = j.toLowerCase();
}
if (!citation.citationItems[i]) {
citation.citationItems[i] = {};
}
citation.citationItems[i][field] = citation.CITATIONITEMS[i][j];
}
}
}
if(!citation.properties) citation.properties = {};
for (let citationItem of citation.citationItems) {
// for upgrade from Zotero 2.0 or earlier
if(citationItem.locatorType) {
citationItem.label = citationItem.locatorType;
delete citationItem.locatorType;
} else if(citationItem.suppressAuthor) {
citationItem["suppress-author"] = citationItem["suppressAuthor"];
delete citationItem.suppressAuthor;
}
// fix for improper upgrade from Zotero 2.1 in <2.1.5
if(parseInt(citationItem.label) == citationItem.label) {
const locatorTypeTerms = ["page", "book", "chapter", "column", "figure", "folio",
"issue", "line", "note", "opus", "paragraph", "part", "section", "sub verbo",
"volume", "verse"];
citationItem.label = locatorTypeTerms[parseInt(citationItem.label)];
}
// for update from Zotero 2.1 or earlier
if(citationItem.uri) {
citationItem.uris = citationItem.uri;
delete citationItem.uri;
}
}
// for upgrade from Zotero 2.0 or earlier
if(citation.sort) {
citation.properties.unsorted = !citation.sort;
delete citation.sort;
}
if(citation.custom) {
citation.properties.custom = citation.custom;
delete citation.custom;
}
if(!citation.citationID) citation.citationID = Zotero.randomString();
citation.properties.field = code;
return citation;
}
function unserializePreZotero1_0(code) {
var underscoreIndex = code.indexOf("_");
var itemIDs = code.substr(0, underscoreIndex).split("|");
var lastIndex = code.lastIndexOf("_");
if(lastIndex != underscoreIndex+1) {
var locatorString = code.substr(underscoreIndex+1, lastIndex-underscoreIndex-1);
var locators = locatorString.split("|");
}
var citationItems = new Array();
for(var i=0; i<itemIDs.length; i++) {
var citationItem = {id:itemIDs[i]};
if(locators) {
citationItem.locator = locators[i].substr(1);
citationItem.label = Zotero.Integration._oldCitationLocatorMap[locators[i][0]];
}
citationItems.push(citationItem);
}
return {"citationItems":citationItems, properties:{}};
}
if (this.code[0] == '{') { // JSON field
return upgradeCruft(unserialize(this.code), this.code);
} else { // ye olde style field
return unserializePreZotero1_0(this.code);
}
};
clearCode() {
this.code = JSON.stringify({citationItems: [], properties: {}});
this.writeToDoc();
}
};
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.Integration.BibliographyField = class extends Zotero.Integration.Field {
constructor(field) {
super(field);
this.type = INTEGRATION_TYPE_BIBLIOGRAPHY;
this.type = INTEGRATION_TYPE_ITEM;
};
unserialize() {
try {
return JSON.parse(this.code);
} catch(e) {
throw new Zotero.Integration.CorruptFieldException(this.code, e);
}
}
};
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);