commit e2993b94a4c31c90eacbe7aa11f80f534ced678f
parent 167806a5bd2b3ace614a0d20354dafe1b0dc6baa
Author: Simon Kornblith <simon@simonster.com>
Date: Fri, 8 Jul 2011 03:42:26 +0000
- Use jsdom in node.js for unescaping HTML
- Add itemToExportFormat and itemToServerJSON utility functions
- Support asynchronous translator loading (this will only be used in node.js)
- Fix cases where Safari/Chrome code was incorrectly applied in node.js
- Add Zotero.ItemFields.getItemTypeFields() to connector cachedTypes.js
Diffstat:
7 files changed, 301 insertions(+), 258 deletions(-)
diff --git a/chrome/content/zotero/xpcom/connector/cachedTypes.js b/chrome/content/zotero/xpcom/connector/cachedTypes.js
@@ -48,7 +48,7 @@ Zotero.Connector_Types = new function() {
this[schemaType][entry.name] = entry;
}
}
- }
+ };
/**
* Passes schema to a callback
@@ -56,24 +56,24 @@ Zotero.Connector_Types = new function() {
*/
this.getSchema = function(callback) {
callback(Zotero.Connector_Types.schema);
- }
+ };
}
Zotero.CachedTypes = function() {
this.getID = function(idOrName) {
if(!Zotero.Connector_Types[this.schemaType][idOrName]) return false;
return Zotero.Connector_Types[this.schemaType][idOrName].id;
- }
+ };
this.getName = function(idOrName) {
if(!Zotero.Connector_Types[this.schemaType][idOrName]) return false;
return Zotero.Connector_Types[this.schemaType][idOrName].name;
- }
+ };
this.getLocalizedString = function(idOrName) {
if(!Zotero.Connector_Types[this.schemaType][idOrName]) return false;
return Zotero.Connector_Types[this.schemaType][idOrName].localizedString;
- }
+ };
}
Zotero.ItemTypes = new function() {
@@ -90,7 +90,7 @@ Zotero.ItemTypes = new function() {
} else if(Zotero.isSafari) {
return safari.extension.baseURI+"images/itemTypes/"+Zotero.Connector_Types["itemTypes"][idOrName].icon;
}
- }
+ };
}
Zotero.CreatorTypes = new function() {
@@ -105,7 +105,7 @@ Zotero.CreatorTypes = new function() {
creatorTypes.push(Zotero.Connector_Types["creatorTypes"][itemType.creatorTypes[i]]);
}
return creatorTypes;
- }
+ };
}
Zotero.ItemFields = new function() {
@@ -119,7 +119,7 @@ Zotero.ItemFields = new function() {
return Zotero.Connector_Types["itemTypes"][typeIdOrName].fields.indexOf(
Zotero.Connector_Types["fields"][fieldIdOrName].id) !== -1;
- }
+ };
this.getFieldIDFromTypeAndBase = function(itemType, baseField) {
if(!Zotero.Connector_Types["fields"][baseField]
@@ -138,5 +138,9 @@ Zotero.ItemFields = new function() {
}
return false;
- }
+ };
+
+ this.getItemTypeFields = function(idOrName) {
+ return Zotero.Connector_Types["itemTypes"][typeIdOrName].fields.slice();
+ };
}
\ No newline at end of file
diff --git a/chrome/content/zotero/xpcom/connector/translate_item.js b/chrome/content/zotero/xpcom/connector/translate_item.js
@@ -56,120 +56,9 @@ Zotero.Translate.ItemSaver.prototype = {
* Saves items to server
*/
"_saveToServer":function(items, callback) {
- const IGNORE_FIELDS = ["seeAlso", "attachments", "complete"];
-
var newItems = [];
for(var i in items) {
- var item = items[i];
-
- var newItem = {};
- newItems.push(newItem);
-
- var typeID = Zotero.ItemTypes.getID(item.itemType);
- if(!typeID) {
- Zotero.debug("Translate: Invalid itemType "+item.itemType+"; saving as webpage");
- item.itemType = "webpage";
- typeID = Zotero.ItemTypes.getID(item.itemType);
- }
-
- var fieldID;
- for(var field in item) {
- if(IGNORE_FIELDS.indexOf(field) !== -1) continue;
-
- var val = item[field];
-
- if(field === "itemType") {
- newItem[field] = val;
- } else if(field === "creators") {
- // normalize creators
- var newCreators = newItem.creators = [];
- for(var j in val) {
- var creator = val[j];
-
- // Single-field mode
- if (!creator.firstName || (creator.fieldMode && creator.fieldMode == 1)) {
- var newCreator = {
- name: creator.lastName
- };
- }
- // Two-field mode
- else {
- var newCreator = {
- firstName: creator.firstName,
- lastName: creator.lastName
- };
- }
-
- // ensure creatorType is present and valid
- newCreator.creatorType = "author";
- if(creator.creatorType) {
- if(Zotero.CreatorTypes.getID(creator.creatorType)) {
- newCreator.creatorType = creator.creatorType;
- } else {
- Zotero.debug("Translate: Invalid creator type "+creator.creatorType+"; falling back to author");
- }
- }
-
- newCreators.push(newCreator);
- }
- } else if(field === "tags") {
- // normalize tags
- var newTags = newItem.tags = [];
- for(var j in val) {
- var tag = val[j];
- if(typeof tag === "object") {
- if(tag.tag) {
- tag = tag.tag;
- } else if(tag.name) {
- tag = tag.name;
- } else {
- Zotero.debug("Translate: Discarded invalid tag");
- continue;
- }
- }
- newTags.push({"tag":tag.toString(), "type":1})
- }
- } else if(field === "notes") {
- // normalize notes
- var newNotes = newItem.notes = [];
- for(var j in val) {
- var note = val[j];
- if(typeof note === "object") {
- if(!note.note) {
- Zotero.debug("Translate: Discarded invalid note");
- continue;
- }
- note = note.note;
- }
- newNotes.push({"itemType":"note", "note":note.toString()});
- }
- } else if(fieldID = Zotero.ItemFields.getID(field)) {
- // if content is not a string, either stringify it or delete it
- if(typeof val !== "string") {
- if(val || val === 0) {
- val = val.toString();
- } else {
- continue;
- }
- }
-
- // map from base field if possible
- var itemFieldID = Zotero.ItemFields.getFieldIDFromTypeAndBase(typeID, fieldID);
- if(itemFieldID) {
- newItem[Zotero.ItemFields.getName(itemFieldID)] = val;
- continue; // already know this is valid
- }
-
- // if field is valid for this type, set field
- if(Zotero.ItemFields.isValidForType(fieldID, typeID)) {
- newItem[field] = val;
- } else {
- Zotero.debug("Translate: Discarded field "+field+": field not valid for type "+item.itemType, 3);
- }
- } else if(field !== "complete") {
- Zotero.debug("Translate: Discarded unknown field "+field, 3);
- }
- }
+ newItems.push(Zotero.Utilities.itemToServerJSON(items[i]));
}
var url = 'users/%%USERID%%/items?key=%%APIKEY%%';
diff --git a/chrome/content/zotero/xpcom/connector/translator.js b/chrome/content/zotero/xpcom/connector/translator.js
@@ -42,7 +42,7 @@ Zotero.Translators = new function() {
this.init = function(translators) {
if(!translators) {
translators = [];
- if(!Zotero.isFx && localStorage["translatorMetadata"]) {
+ if((Zotero.isChrome || Zotero.isSafari) && localStorage["translatorMetadata"]) {
try {
translators = JSON.parse(localStorage["translatorMetadata"]);
if(typeof translators !== "object") {
@@ -186,17 +186,17 @@ Zotero.Translators = new function() {
if(j === 0) {
converterFunctions.push(null);
- } else if(Zotero.isFx) {
+ } else if(Zotero.isChrome || Zotero.isSafari) {
+ // in Chrome/Safari, the converterFunction needs to be passed as JSON, so
+ // just push an array with the proper and proxyHosts
+ converterFunctions.push([properHosts[j-1], proxyHosts[j-1]]);
+ } else {
// in Firefox, push the converterFunction
converterFunctions.push(new function() {
var re = new RegExp('^https?://(?:[^/]\\.)?'+Zotero.Utilities.quotemeta(properHosts[j-1]), "gi");
var proxyHost = proxyHosts[j-1].replace(/\$/g, "$$$$");
return function(uri) { return uri.replace(re, "$&."+proxyHost) };
});
- } else {
- // in Chrome/Safari, the converterFunction needs to be passed as JSON, so
- // just push an array with the proper and proxyHosts
- converterFunctions.push([properHosts[j-1], proxyHosts[j-1]]);
}
// don't add translator more than once
@@ -244,17 +244,6 @@ Zotero.Translators = new function() {
if(reset) {
var serializedTranslators = newMetadata;
- if(!Zotero.isFx) {
- // clear cached translatorCode
- Zotero.debug("Translators: Resetting translators");
- // XXX this is only to clear localStorage for people who installed yesterday and
- // should disappear soon
- for(var i in localStorage) {
- if(i.substr(0, TRANSLATOR_CODE_PREFIX.length) === TRANSLATOR_CODE_PREFIX) {
- delete localStorage[i];
- }
- }
- }
} else {
var serializedTranslators = [];
var hasChanged = false;
@@ -300,7 +289,7 @@ Zotero.Translators = new function() {
}
// Store
- if(!Zotero.isFx) {
+ if(Zotero.isChrome || Zotero.isSafari) {
localStorage["translatorMetadata"] = JSON.stringify(serializedTranslators);
}
@@ -440,7 +429,7 @@ Zotero.Translator.prototype.init = function(info) {
}
if(info.code) {
- this.code = preprocessCode(info.code);
+ this.code = Zotero.Translators.preprocessCode(info.code);
} else if(this.hasOwnProperty("code")) {
delete this.code;
}
diff --git a/chrome/content/zotero/xpcom/translation/translate.js b/chrome/content/zotero/xpcom/translation/translate.js
@@ -283,48 +283,48 @@ Zotero.Translate.Sandbox = {
var sandbox;
var haveTranslatorFunction = function(translator) {
translation.translator[0] = translator;
- if(!translation._loadTranslator(translator)) throw new Error("Translator could not be loaded");
-
- if(Zotero.isFx) {
- // do same origin check
- var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
- .getService(Components.interfaces.nsIScriptSecurityManager);
- var ioService = Components.classes["@mozilla.org/network/io-service;1"]
- .getService(Components.interfaces.nsIIOService);
-
- var outerSandboxURI = ioService.newURI(typeof translate._sandboxLocation === "object" ?
- translate._sandboxLocation.location : translate._sandboxLocation, null, null);
- var innerSandboxURI = ioService.newURI(typeof translation._sandboxLocation === "object" ?
- translation._sandboxLocation.location : translation._sandboxLocation, null, null);
+ translation._loadTranslator(translator, function() {
+ if(Zotero.isFx) {
+ // do same origin check
+ var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
+ .getService(Components.interfaces.nsIScriptSecurityManager);
+ var ioService = Components.classes["@mozilla.org/network/io-service;1"]
+ .getService(Components.interfaces.nsIIOService);
+
+ var outerSandboxURI = ioService.newURI(typeof translate._sandboxLocation === "object" ?
+ translate._sandboxLocation.location : translate._sandboxLocation, null, null);
+ var innerSandboxURI = ioService.newURI(typeof translation._sandboxLocation === "object" ?
+ translation._sandboxLocation.location : translation._sandboxLocation, null, null);
+
+ Zotero.debug(outerSandboxURI.spec);
+ Zotero.debug(innerSandboxURI.spec);
+
+ try {
+ secMan.checkSameOriginURI(outerSandboxURI, innerSandboxURI, false);
+ } catch(e) {
+ throw new Error("getTranslatorObject() may not be called from web or search "+
+ "translators to web or search translators from different origins.");
+ }
+ }
- Zotero.debug(outerSandboxURI.spec);
- Zotero.debug(innerSandboxURI.spec);
+ translation._prepareTranslation();
+ setDefaultHandlers(translate, translation);
+ sandbox = translation._sandboxManager.sandbox;
+ if(sandbox.Export) {
+ sandbox.Export.Zotero = sandbox.Zotero;
+ sandbox = sandbox.Export;
+ } else {
+ translate._debug("COMPAT WARNING: "+translate.translator[0].label+" does "+
+ "not export any properties. Only detect"+translate._entryFunctionSuffix+
+ " and do"+translate._entryFunctionSuffix+" will be available in "+
+ "connectors.");
+ }
- try {
- secMan.checkSameOriginURI(outerSandboxURI, innerSandboxURI, false);
- } catch(e) {
- throw new Error("getTranslatorObject() may not be called from web or search "+
- "translators to web or search translators from different origins.");
+ if(callback) {
+ callback(sandbox);
+ translate.decrementAsyncProcesses();
}
- }
-
- translation._prepareTranslation();
- setDefaultHandlers(translate, translation);
- sandbox = translation._sandboxManager.sandbox;
- if(sandbox.Export) {
- sandbox.Export.Zotero = sandbox.Zotero;
- sandbox = sandbox.Export;
- } else {
- translate._debug("COMPAT WARNING: "+translate.translator[0].label+" does "+
- "not export any properties. Only detect"+translate._entryFunctionSuffix+
- " and do"+translate._entryFunctionSuffix+" will be available in "+
- "connectors.");
- }
-
- if(callback) {
- callback(sandbox);
- translate.decrementAsyncProcesses();
- }
+ });
};
if(typeof translation.translator[0] === "object") {
@@ -929,27 +929,24 @@ Zotero.Translate.Base.prototype = {
this._libraryID = libraryID;
this._saveAttachments = saveAttachments === undefined || saveAttachments;
+ var me = this;
if(typeof this.translator[0] === "object") {
// already have a translator object, so use it
- this._translateHaveTranslator();
+ this._loadTranslator(this.translator[0], function() { me._translateTranslatorLoaded() });
} else {
// need to get translator first
- var me = this;
Zotero.Translators.get(this.translator[0],
function(translator) {
me.translator[0] = translator;
- me._translateHaveTranslator();
+ me._loadTranslator(translator, function() { me._translateTranslatorLoaded() });
});
}
},
/**
- * Called when translator has been retrieved
+ * Called when translator has been retrieved and loaded
*/
- "_translateHaveTranslator":function() {
- // load translators
- if(!this._loadTranslator(this.translator[0])) return;
-
+ "_translateTranslatorLoaded":function() {
// set display options to default if they don't exist
if(!this._displayOptions) this._displayOptions = this.translator[0].displayOptions;
@@ -1091,7 +1088,7 @@ Zotero.Translate.Base.prototype = {
},
/**
- * Runs detect code for a translator
+ * Begins running detect code for a translator, first loading it
*/
"_detect":function() {
// there won't be any translators if we need an RPC call
@@ -1100,7 +1097,15 @@ Zotero.Translate.Base.prototype = {
return;
}
- if(!this._loadTranslator(this._potentialTranslators[0])) return
+ var me = this;
+ this._loadTranslator(this._potentialTranslators[0],
+ function() { me._detectTranslatorLoaded() });
+ },
+
+ /**
+ * Runs detect code for a translator
+ */
+ "_detectTranslatorLoaded":function() {
this._prepareDetection();
this.incrementAsyncProcesses();
@@ -1130,7 +1135,7 @@ Zotero.Translate.Base.prototype = {
* @param {Zotero.Translator} translator
* @return {Boolean} Whether the translator could be successfully loaded
*/
- "_loadTranslator":function(translator) {
+ "_loadTranslator":function(translator, callback) {
var sandboxLocation = this._getSandboxLocation();
if(!this._sandboxLocation || sandboxLocation != this._sandboxLocation) {
this._sandboxLocation = sandboxLocation;
@@ -1155,10 +1160,9 @@ Zotero.Translate.Base.prototype = {
}
this.complete(false, "parse error");
- return false;
}
- return true;
+ if(callback) callback();
},
/**
@@ -1494,9 +1498,13 @@ Zotero.Translate.Import.prototype.complete = function(returnValue, error) {
* Get all potential import translators, ordering translators with the right file extension first
*/
Zotero.Translate.Import.prototype._getTranslatorsGetPotentialTranslators = function() {
- var me = this;
- Zotero.Translators.getImportTranslatorsForLocation(this.location,
- function(translators) { me._getTranslatorsTranslatorsReceived(translators) });
+ if(this.location) {
+ var me = this;
+ Zotero.Translators.getImportTranslatorsForLocation(this.location,
+ function(translators) { me._getTranslatorsTranslatorsReceived(translators) });
+ } else {
+ Zotero.Translate.Base.prototype._getTranslatorsGetPotentialTranslators.call(this);
+ }
}
/**
@@ -1507,10 +1515,13 @@ Zotero.Translate.Import.prototype.getTranslators = function() {
if(!this._string && !this.location) {
if(this._currentState === "detect") throw new Error("getTranslators: detection is already running");
this._currentState = "detect";
- this._foundTranslators = Zotero.Translators.getAllForType(this.type);
- this._potentialTranslators = [];
- this.complete(true);
- return this._foundTranslators;
+ var me = this;
+ Zotero.Translators.getAllForType(this.type, function(translators) {
+ me._potentialTranslators = [];
+ me._foundTranslators = translators;
+ me.complete(true);
+ });
+ if(this._currentState === null) return this._foundTranslators;
} else {
Zotero.Translate.Base.prototype.getTranslators.call(this);
}
@@ -1519,46 +1530,61 @@ Zotero.Translate.Import.prototype.getTranslators = function() {
/**
* Overload {@link Zotero.Translate.Base#_loadTranslator} to prepare translator IO
*/
-Zotero.Translate.Import.prototype._loadTranslator = function(translator) {
+Zotero.Translate.Import.prototype._loadTranslator = function(translator, callback) {
// call super
- var returnVal = Zotero.Translate.Base.prototype._loadTranslator.call(this, translator);
- if(!returnVal) return returnVal;
+ var me = this;
+ Zotero.Translate.Base.prototype._loadTranslator.call(this, translator, function() {
+ me._loadTranslatorPrepareIO(translator, callback);
+ });
+}
+/**
+ * Prepare translator IO
+ */
+Zotero.Translate.Import.prototype._loadTranslatorPrepareIO = function(translator, callback) {
var dataMode = (translator ? translator : this._potentialTranslators[0]).configOptions["dataMode"];
- var err = false;
- if(this._io) {
- try {
- this._io.reset(dataMode);
- } catch(e) {
- err = e;
+ var me = this;
+ var initCallback = function(status, err) {
+ if(!status) {
+ me.complete(false, err);
+ } else {
+ me._sandboxManager.importObject(me._io);
+ if(callback) callback();
}
- } else {
+ };
+
+ var err = false;
+ if(!this._io) {
if(Zotero.Translate.IO.Read && this.location && this.location instanceof Components.interfaces.nsIFile) {
try {
- this._io = new Zotero.Translate.IO.Read(this.location, dataMode);
+ this._io = new Zotero.Translate.IO.Read(this.location);
} catch(e) {
err = e;
}
} else {
try {
- this._io = new Zotero.Translate.IO.String(this._string, this.path ? this.path : "", dataMode);
+ this._io = new Zotero.Translate.IO.String(this._string, this.path ? this.path : "");
} catch(e) {
err = e;
}
}
+
+ if(err) {
+ this.complete(false, err);
+ return;
+ }
}
+ try {
+ this._io.init(dataMode, initCallback);
+ } catch(e) {
+ err = e;
+ }
if(err) {
- Zotero.debug("Translate: Preparing IO for "+translator.label+" failed: ");
- Zotero.debug(err);
this.complete(false, err);
- return false;
+ return;
}
-
- this._sandboxManager.importObject(this._io);
-
- return true;
}
/**
@@ -1853,10 +1879,6 @@ Zotero.Translate.IO.String = function(string, uri, mode) {
}
this._stringPointer = 0;
this._uri = uri;
-
- if(mode) {
- this.reset(mode);
- }
}
Zotero.Translate.IO.String.prototype = {
@@ -1868,16 +1890,16 @@ Zotero.Translate.IO.String.prototype = {
"_getXML":"r"
},
- "_initRDF":function() {
+ "_initRDF":function(callback) {
Zotero.debug("Translate: Initializing RDF data store");
this._dataStore = new Zotero.RDF.AJAW.RDFIndexedFormula();
+ this.RDF = new Zotero.Translate.IO._RDFSandbox(this._dataStore);
if(this._string.length) {
var parser = new Zotero.RDF.AJAW.RDFParser(this._dataStore);
parser.parse(Zotero.Translate.IO.parseDOMXML(this._string), this._uri);
+ callback(true);
}
-
- this.RDF = new Zotero.Translate.IO._RDFSandbox(this._dataStore);
},
"setCharacterSet":function(charset) {},
@@ -1936,12 +1958,14 @@ Zotero.Translate.IO.String.prototype = {
}
},
- "reset":function(newMode) {
+ "init":function(newMode, callback) {
this._stringPointer = 0;
this._mode = newMode;
if(Zotero.Translate.IO.rdfDataModes.indexOf(this._mode) !== -1) {
- this._initRDF();
+ this._initRDF(callback);
+ } else {
+ callback(true);
}
},
diff --git a/chrome/content/zotero/xpcom/translation/translate_firefox.js b/chrome/content/zotero/xpcom/translation/translate_firefox.js
@@ -325,9 +325,6 @@ Zotero.Translate.IO.Read = function(file, mode) {
}
Zotero.debug("Translate: Detected file charset as "+this._charset);
-
- // We know the charset now. Open a converter stream.
- if(mode) this.reset(mode);
}
Zotero.Translate.IO.Read.prototype = {
@@ -437,7 +434,7 @@ Zotero.Translate.IO.Read.prototype = {
}
},
- "reset":function(newMode) {
+ "init":function(newMode, callback) {
if(Zotero.Translate.IO.maintainedInstances.indexOf(this) === -1) {
Zotero.Translate.IO.maintainedInstances.push(this);
}
@@ -447,6 +444,8 @@ Zotero.Translate.IO.Read.prototype = {
if(Zotero.Translate.IO.rdfDataModes.indexOf(this._mode) !== -1 && !this.RDF) {
this._initRDF();
}
+
+ callback(true);
},
"close":function() {
diff --git a/chrome/content/zotero/xpcom/translation/translate_item.js b/chrome/content/zotero/xpcom/translation/translate_item.js
@@ -664,33 +664,7 @@ Zotero.Translate.ItemGetter.prototype = {
returnItemArray.date = Zotero.Date.multipartToStr(returnItemArray.date);
}
- returnItemArray.uniqueFields = {};
-
- // get base fields, not just the type-specific ones
- var itemTypeID = returnItem.itemTypeID;
- var allFields = Zotero.ItemFields.getItemTypeFields(itemTypeID);
- for each(var field in allFields) {
- var fieldName = Zotero.ItemFields.getName(field);
-
- if(returnItemArray[fieldName] !== undefined) {
- var baseField = Zotero.ItemFields.getBaseIDFromTypeAndField(itemTypeID, field);
-
- var baseName = null;
- if(baseField && baseField != field) {
- baseName = Zotero.ItemFields.getName(baseField);
- }
-
- if(baseName) {
- returnItemArray[baseName] = returnItemArray[fieldName];
- returnItemArray.uniqueFields[baseName] = returnItemArray[fieldName];
- } else {
- returnItemArray.uniqueFields[fieldName] = returnItemArray[fieldName];
- }
- }
- }
-
- // preserve notes
- if(returnItemArray.note) returnItemArray.uniqueFields.note = returnItemArray.note;
+ var returnItemArray = Zotero.Utilities.itemToExportFormat(returnItemArray);
// TODO: Change tag.tag references in translators to tag.name
// once translators are 1.5-only
diff --git a/chrome/content/zotero/xpcom/utilities.js b/chrome/content/zotero/xpcom/utilities.js
@@ -222,6 +222,17 @@ Zotero.Utilities = {
var nsISUHTML = Components.classes["@mozilla.org/feed-unescapehtml;1"]
.getService(Components.interfaces.nsIScriptableUnescapeHTML);
return nsISUHTML.unescape(str);
+ } else if(Zotero.isNode) {
+ var doc = require('jsdom').jsdom(str, null, {
+ "features":{
+ "FetchExternalResources":false,
+ "ProcessExternalResources":false,
+ "MutationEvents":false,
+ "QuerySelector":false
+ }
+ });
+ if(!doc.documentElement) return str;
+ return doc.documentElement.textContent;
} else {
var node = document.createElement("div");
node.innerHTML = str;
@@ -796,6 +807,159 @@ Zotero.Utilities = {
dumped_text = "===>"+arr+"<===("+typeof(arr)+")";
}
return dumped_text;
+ },
+
+ /**
+ * Adds all fields to an item in toArray() format and adds a unique (base) fields to
+ * uniqueFields array
+ */
+ "itemToExportFormat":function(item) {
+ item.uniqueFields = {};
+
+ // get base fields, not just the type-specific ones
+ var itemTypeID = (item.itemTypeID ? item.itemTypeID : Zotero.ItemTypes.getID(item.itemType));
+ var allFields = Zotero.ItemFields.getItemTypeFields(itemTypeID);
+ for(var i in allFields) {
+ var field = allFields[i];
+ var fieldName = Zotero.ItemFields.getName(field);
+
+ if(item[fieldName] !== undefined) {
+ var baseField = Zotero.ItemFields.getBaseIDFromTypeAndField(itemTypeID, field);
+
+ var baseName = null;
+ if(baseField && baseField != field) {
+ baseName = Zotero.ItemFields.getName(baseField);
+ }
+
+ if(baseName) {
+ item[baseName] = item[fieldName];
+ item.uniqueFields[baseName] = item[fieldName];
+ } else {
+ item.uniqueFields[fieldName] = item[fieldName];
+ }
+ }
+ }
+
+ // preserve notes
+ if(item.note) item.uniqueFields.note = item.note;
+
+ return item;
+ },
+
+ /**
+ * Converts an item from toArray() format to content=json format used by the server
+ */
+ "itemToServerJSON":function(item) {
+ const IGNORE_FIELDS = ["seeAlso", "attachments", "complete"];
+ var newItem = {};
+
+ var typeID = Zotero.ItemTypes.getID(item.itemType);
+ if(!typeID) {
+ Zotero.debug("Translate: Invalid itemType "+item.itemType+"; saving as webpage");
+ item.itemType = "webpage";
+ typeID = Zotero.ItemTypes.getID(item.itemType);
+ }
+
+ var fieldID;
+ for(var field in item) {
+ if(IGNORE_FIELDS.indexOf(field) !== -1) continue;
+
+ var val = item[field];
+
+ if(field === "itemType") {
+ newItem[field] = val;
+ } else if(field === "creators") {
+ // normalize creators
+ var newCreators = newItem.creators = [];
+ for(var j in val) {
+ var creator = val[j];
+
+ // Single-field mode
+ if (!creator.firstName || (creator.fieldMode && creator.fieldMode == 1)) {
+ var newCreator = {
+ name: creator.lastName
+ };
+ }
+ // Two-field mode
+ else {
+ var newCreator = {
+ firstName: creator.firstName,
+ lastName: creator.lastName
+ };
+ }
+
+ // ensure creatorType is present and valid
+ newCreator.creatorType = "author";
+ if(creator.creatorType) {
+ if(Zotero.CreatorTypes.getID(creator.creatorType)) {
+ newCreator.creatorType = creator.creatorType;
+ } else {
+ Zotero.debug("Translate: Invalid creator type "+creator.creatorType+"; falling back to author");
+ }
+ }
+
+ newCreators.push(newCreator);
+ }
+ } else if(field === "tags") {
+ // normalize tags
+ var newTags = newItem.tags = [];
+ for(var j in val) {
+ var tag = val[j];
+ if(typeof tag === "object") {
+ if(tag.tag) {
+ tag = tag.tag;
+ } else if(tag.name) {
+ tag = tag.name;
+ } else {
+ Zotero.debug("Translate: Discarded invalid tag");
+ continue;
+ }
+ }
+ newTags.push({"tag":tag.toString(), "type":1})
+ }
+ } else if(field === "notes") {
+ // normalize notes
+ var newNotes = newItem.notes = [];
+ for(var j in val) {
+ var note = val[j];
+ if(typeof note === "object") {
+ if(!note.note) {
+ Zotero.debug("Translate: Discarded invalid note");
+ continue;
+ }
+ note = note.note;
+ }
+ newNotes.push({"itemType":"note", "note":note.toString()});
+ }
+ } else if(fieldID = Zotero.ItemFields.getID(field)) {
+ // if content is not a string, either stringify it or delete it
+ if(typeof val !== "string") {
+ if(val || val === 0) {
+ val = val.toString();
+ } else {
+ continue;
+ }
+ }
+
+ // map from base field if possible
+ var itemFieldID = Zotero.ItemFields.getFieldIDFromTypeAndBase(typeID, fieldID);
+ if(itemFieldID) {
+ newItem[Zotero.ItemFields.getName(itemFieldID)] = val;
+ continue; // already know this is valid
+ }
+
+ // if field is valid for this type, set field
+ if(Zotero.ItemFields.isValidForType(fieldID, typeID)) {
+ newItem[field] = val;
+ } else {
+ Zotero.debug("Translate: Discarded field "+field+": field not valid for type "+item.itemType, 3);
+ }
+ } else if(field !== "complete") {
+ Zotero.debug("Translate: Discarded unknown field "+field, 3);
+ }
+ }
+
+ return newItem;
}
}