updateBundledStyles() asyncification and related changes
- Use async DB and OS.File for bundled file updates - Remove support for translator/style ZIP files -- the two options are now non-unpacked XPIs with subfolders or unpacked source installations - Now that we have async file access, don't store translator code in database cache -- just store metadata so that it's available without reading each translator file - Change the (previously partially asyncified) Zotero.Styles/Translators APIs a bit -- while the getAll/getVisible methods are asynchronous and will wait for loading, the get() methods are synchronous and require styles/translators to be initialized before they're called. Most places that end up calling get() probably call getAll/getVisible first and should therefore be async, but if there's any way to trigger a get() first, that will need to be adjusted. - Asyncify various other style/translator-related code XPI support is untested, as is style/translator usage, so there are almost certainly bugs. The latter depends on updated export format support (#659), since toArray() no longer exists on this branch. Addresses #529 and #520
This commit is contained in:
parent
842082f818
commit
d8f3be4bee
11 changed files with 1030 additions and 946 deletions
|
@ -134,8 +134,7 @@ Zotero_Preferences.Advanced = {
|
||||||
if (Zotero_Preferences.Export) {
|
if (Zotero_Preferences.Export) {
|
||||||
Zotero_Preferences.Export.populateQuickCopyList();
|
Zotero_Preferences.Export.populateQuickCopyList();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
.done();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -160,8 +159,7 @@ Zotero_Preferences.Advanced = {
|
||||||
if (Zotero_Preferences.Export) {
|
if (Zotero_Preferences.Export) {
|
||||||
Zotero_Preferences.Export.populateQuickCopyList();
|
Zotero_Preferences.Export.populateQuickCopyList();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
.done();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -186,8 +184,7 @@ Zotero_Preferences.Advanced = {
|
||||||
if (Zotero_Preferences.Export) {
|
if (Zotero_Preferences.Export) {
|
||||||
Zotero_Preferences.Export.populateQuickCopyList();
|
Zotero_Preferences.Export.populateQuickCopyList();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
.done();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -26,10 +26,10 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
Zotero_Preferences.Cite = {
|
Zotero_Preferences.Cite = {
|
||||||
init: function () {
|
init: Zotero.Promise.coroutine(function* () {
|
||||||
this.updateWordProcessorInstructions();
|
this.updateWordProcessorInstructions();
|
||||||
this.refreshStylesList();
|
yield this.refreshStylesList();
|
||||||
},
|
}),
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,8 +48,9 @@ Zotero_Preferences.Cite = {
|
||||||
/**
|
/**
|
||||||
* Refreshes the list of styles in the styles pane
|
* Refreshes the list of styles in the styles pane
|
||||||
* @param {String} cslID Style to select
|
* @param {String} cslID Style to select
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
refreshStylesList: function (cslID) {
|
refreshStylesList: Zotero.Promise.coroutine(function* (cslID) {
|
||||||
Zotero.debug("Refreshing styles list");
|
Zotero.debug("Refreshing styles list");
|
||||||
|
|
||||||
var treechildren = document.getElementById('styleManager-rows');
|
var treechildren = document.getElementById('styleManager-rows');
|
||||||
|
@ -57,11 +58,9 @@ Zotero_Preferences.Cite = {
|
||||||
treechildren.removeChild(treechildren.firstChild);
|
treechildren.removeChild(treechildren.firstChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
var styles = Zotero.Styles.getVisible();
|
var styles = yield Zotero.Styles.getVisible();
|
||||||
|
|
||||||
var selectIndex = false;
|
var selectIndex = false;
|
||||||
var i = 0;
|
styles.forEach(function (style, i) {
|
||||||
for each(var style in styles) {
|
|
||||||
var treeitem = document.createElement('treeitem');
|
var treeitem = document.createElement('treeitem');
|
||||||
var treerow = document.createElement('treerow');
|
var treerow = document.createElement('treerow');
|
||||||
var titleCell = document.createElement('treecell');
|
var titleCell = document.createElement('treecell');
|
||||||
|
@ -86,9 +85,8 @@ Zotero_Preferences.Cite = {
|
||||||
if (cslID == style.styleID) {
|
if (cslID == style.styleID) {
|
||||||
document.getElementById('styleManager').view.selection.select(i);
|
document.getElementById('styleManager').view.selection.select(i);
|
||||||
}
|
}
|
||||||
i++;
|
});
|
||||||
}
|
}),
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -112,7 +110,7 @@ Zotero_Preferences.Cite = {
|
||||||
/**
|
/**
|
||||||
* Deletes selected styles from the styles pane
|
* Deletes selected styles from the styles pane
|
||||||
**/
|
**/
|
||||||
deleteStyle: function () {
|
deleteStyle: Zotero.Promise.coroutine(function* () {
|
||||||
// get selected cslIDs
|
// get selected cslIDs
|
||||||
var tree = document.getElementById('styleManager');
|
var tree = document.getElementById('styleManager');
|
||||||
var treeItems = tree.lastChild.childNodes;
|
var treeItems = tree.lastChild.childNodes;
|
||||||
|
@ -141,17 +139,17 @@ Zotero_Preferences.Cite = {
|
||||||
if(ps.confirm(null, '', text)) {
|
if(ps.confirm(null, '', text)) {
|
||||||
// delete if requested
|
// delete if requested
|
||||||
if(cslIDs.length == 1) {
|
if(cslIDs.length == 1) {
|
||||||
selectedStyle.remove();
|
yield selectedStyle.remove();
|
||||||
} else {
|
} else {
|
||||||
for(var i=0; i<cslIDs.length; i++) {
|
for(var i=0; i<cslIDs.length; i++) {
|
||||||
Zotero.Styles.get(cslIDs[i]).remove();
|
yield Zotero.Styles.get(cslIDs[i]).remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.refreshStylesList();
|
yield this.refreshStylesList();
|
||||||
document.getElementById('styleManager-delete').disabled = true;
|
document.getElementById('styleManager-delete').disabled = true;
|
||||||
}
|
}
|
||||||
},
|
}),
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -41,24 +41,24 @@ Zotero_Preferences.Export = {
|
||||||
/*
|
/*
|
||||||
* Builds the main Quick Copy drop-down from the current global pref
|
* Builds the main Quick Copy drop-down from the current global pref
|
||||||
*/
|
*/
|
||||||
populateQuickCopyList: function () {
|
populateQuickCopyList: Zotero.Promise.coroutine(function* () {
|
||||||
// Initialize default format drop-down
|
// Initialize default format drop-down
|
||||||
var format = Zotero.Prefs.get("export.quickCopy.setting");
|
var format = Zotero.Prefs.get("export.quickCopy.setting");
|
||||||
var menulist = document.getElementById("zotero-quickCopy-menu");
|
var menulist = document.getElementById("zotero-quickCopy-menu");
|
||||||
menulist.setAttribute('preference', "pref-quickCopy-setting");
|
menulist.setAttribute('preference', "pref-quickCopy-setting");
|
||||||
this.buildQuickCopyFormatDropDown(menulist, Zotero.QuickCopy.getContentType(format), format);
|
yield this.buildQuickCopyFormatDropDown(menulist, Zotero.QuickCopy.getContentType(format), format);
|
||||||
this.updateQuickCopyHTMLCheckbox(document);
|
this.updateQuickCopyHTMLCheckbox(document);
|
||||||
|
|
||||||
if (!Zotero.isStandalone) {
|
if (!Zotero.isStandalone) {
|
||||||
this.refreshQuickCopySiteList();
|
yield this.refreshQuickCopySiteList();
|
||||||
}
|
}
|
||||||
},
|
}),
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Builds a Quick Copy drop-down
|
* Builds a Quick Copy drop-down
|
||||||
*/
|
*/
|
||||||
buildQuickCopyFormatDropDown: function (menulist, contentType, currentFormat) {
|
buildQuickCopyFormatDropDown: Zotero.Promise.coroutine(function* (menulist, contentType, currentFormat) {
|
||||||
if (!currentFormat) {
|
if (!currentFormat) {
|
||||||
currentFormat = menulist.value;
|
currentFormat = menulist.value;
|
||||||
}
|
}
|
||||||
|
@ -84,8 +84,8 @@ Zotero_Preferences.Export = {
|
||||||
popup.appendChild(itemNode);
|
popup.appendChild(itemNode);
|
||||||
|
|
||||||
// add styles to list
|
// add styles to list
|
||||||
var styles = Zotero.Styles.getVisible();
|
var styles = yield Zotero.Styles.getVisible();
|
||||||
for each(var style in styles) {
|
styles.forEach(function (style) {
|
||||||
var baseVal = 'bibliography=' + style.styleID;
|
var baseVal = 'bibliography=' + style.styleID;
|
||||||
var val = 'bibliography' + (contentType == 'html' ? '/html' : '') + '=' + style.styleID;
|
var val = 'bibliography' + (contentType == 'html' ? '/html' : '') + '=' + style.styleID;
|
||||||
var itemNode = document.createElement("menuitem");
|
var itemNode = document.createElement("menuitem");
|
||||||
|
@ -97,7 +97,7 @@ Zotero_Preferences.Export = {
|
||||||
if (baseVal == currentFormat) {
|
if (baseVal == currentFormat) {
|
||||||
menulist.selectedItem = itemNode;
|
menulist.selectedItem = itemNode;
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
var itemNode = document.createElement("menuitem");
|
var itemNode = document.createElement("menuitem");
|
||||||
itemNode.setAttribute("label", Zotero.getString('zotero.preferences.export.quickCopy.exportFormats'));
|
itemNode.setAttribute("label", Zotero.getString('zotero.preferences.export.quickCopy.exportFormats'));
|
||||||
|
@ -106,31 +106,28 @@ Zotero_Preferences.Export = {
|
||||||
|
|
||||||
// add export formats to list
|
// add export formats to list
|
||||||
var translation = new Zotero.Translate("export");
|
var translation = new Zotero.Translate("export");
|
||||||
translation.getTranslators()
|
var translators = yield translation.getTranslators();
|
||||||
.then(function (translators) {
|
translators.forEach(function (translator) {
|
||||||
for (var i=0; i<translators.length; i++) {
|
// Skip RDF formats
|
||||||
// Skip RDF formats
|
switch (translator.translatorID) {
|
||||||
switch (translators[i].translatorID) {
|
case '6e372642-ed9d-4934-b5d1-c11ac758ebb7':
|
||||||
case '6e372642-ed9d-4934-b5d1-c11ac758ebb7':
|
case '14763d24-8ba0-45df-8f52-b8d1108e7ac9':
|
||||||
case '14763d24-8ba0-45df-8f52-b8d1108e7ac9':
|
return;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var val = 'export=' + translators[i].translatorID;
|
|
||||||
var itemNode = document.createElement("menuitem");
|
|
||||||
itemNode.setAttribute("value", val);
|
|
||||||
itemNode.setAttribute("label", translators[i].label);
|
|
||||||
itemNode.setAttribute("oncommand", 'Zotero_Preferences.Export.updateQuickCopyHTMLCheckbox(document)');
|
|
||||||
popup.appendChild(itemNode);
|
|
||||||
|
|
||||||
if (val == currentFormat) {
|
|
||||||
menulist.selectedItem = itemNode;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
var val = 'export=' + translator.translatorID;
|
||||||
|
var itemNode = document.createElement("menuitem");
|
||||||
|
itemNode.setAttribute("value", val);
|
||||||
|
itemNode.setAttribute("label", translator.label);
|
||||||
|
itemNode.setAttribute("oncommand", 'Zotero_Preferences.Export.updateQuickCopyHTMLCheckbox(document)');
|
||||||
|
popup.appendChild(itemNode);
|
||||||
|
|
||||||
menulist.click();
|
if (val == currentFormat) {
|
||||||
})
|
menulist.selectedItem = itemNode;
|
||||||
.done();
|
}
|
||||||
},
|
});
|
||||||
|
|
||||||
|
menulist.click();
|
||||||
|
}),
|
||||||
|
|
||||||
|
|
||||||
updateQuickCopyHTMLCheckbox: function (doc) {
|
updateQuickCopyHTMLCheckbox: function (doc) {
|
||||||
|
@ -219,7 +216,7 @@ Zotero_Preferences.Export = {
|
||||||
var domainPath = treeitem.firstChild.firstChild.getAttribute('label');
|
var domainPath = treeitem.firstChild.firstChild.getAttribute('label');
|
||||||
yield Zotero.DB.queryAsync("DELETE FROM settings WHERE setting='quickCopySite' AND key=?", [domainPath]);
|
yield Zotero.DB.queryAsync("DELETE FROM settings WHERE setting='quickCopySite' AND key=?", [domainPath]);
|
||||||
yield Zotero.QuickCopy.loadSiteSettings();
|
yield Zotero.QuickCopy.loadSiteSettings();
|
||||||
this.refreshQuickCopySiteList();
|
yield this.refreshQuickCopySiteList();
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -39,27 +39,24 @@ Zotero_Preferences.General = {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
updateTranslators: function () {
|
updateTranslators: Zotero.Promise.coroutine(function* () {
|
||||||
Zotero.Schema.updateFromRepository(true)
|
var updated = yield Zotero.Schema.updateFromRepository(true);
|
||||||
.then(function (updated) {
|
var button = document.getElementById('updateButton');
|
||||||
var button = document.getElementById('updateButton');
|
if (button) {
|
||||||
if (button) {
|
if (updated===-1) {
|
||||||
if (updated===-1) {
|
var label = Zotero.getString('zotero.preferences.update.upToDate');
|
||||||
var label = Zotero.getString('zotero.preferences.update.upToDate');
|
|
||||||
}
|
|
||||||
else if (updated) {
|
|
||||||
var label = Zotero.getString('zotero.preferences.update.updated');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var label = Zotero.getString('zotero.preferences.update.error');
|
|
||||||
}
|
|
||||||
button.setAttribute('label', label);
|
|
||||||
|
|
||||||
if (updated && Zotero_Preferences.Cite) {
|
|
||||||
Zotero_Preferences.Cite.refreshStylesList();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
else if (updated) {
|
||||||
.done();
|
var label = Zotero.getString('zotero.preferences.update.updated');
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
var label = Zotero.getString('zotero.preferences.update.error');
|
||||||
|
}
|
||||||
|
button.setAttribute('label', label);
|
||||||
|
|
||||||
|
if (updated && Zotero_Preferences.Cite) {
|
||||||
|
yield Zotero_Preferences.Cite.refreshStylesList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
* @namespace
|
* @namespace
|
||||||
*/
|
*/
|
||||||
Zotero.File = new function(){
|
Zotero.File = new function(){
|
||||||
//Components.utils.import("resource://zotero/bluebird.js");
|
|
||||||
Components.utils.import("resource://gre/modules/NetUtil.jsm");
|
Components.utils.import("resource://gre/modules/NetUtil.jsm");
|
||||||
Components.utils.import("resource://gre/modules/FileUtils.jsm");
|
Components.utils.import("resource://gre/modules/FileUtils.jsm");
|
||||||
|
|
||||||
|
@ -44,15 +43,20 @@ Zotero.File = new function(){
|
||||||
|
|
||||||
this.pathToFile = function (pathOrFile) {
|
this.pathToFile = function (pathOrFile) {
|
||||||
if (typeof pathOrFile == 'string') {
|
if (typeof pathOrFile == 'string') {
|
||||||
let nsIFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
|
return new FileUtils.File(pathOrFile);
|
||||||
nsIFile.initWithPath(pathOrFile);
|
|
||||||
return nsIFile;
|
|
||||||
}
|
}
|
||||||
else if (pathOrFile instanceof Ci.nsIFile) {
|
else if (pathOrFile instanceof Ci.nsIFile) {
|
||||||
return pathOrFile;
|
return pathOrFile;
|
||||||
}
|
}
|
||||||
|
throw new Error('Unexpected value provided to Zotero.File.pathToFile() (' + pathOrFile + ')');
|
||||||
throw new Error('Unexpected value provided to Zotero.MIME.pathToFile() (' + pathOrFile + ')');
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.pathToFileURI = function (path) {
|
||||||
|
var file = new FileUtils.File(path);
|
||||||
|
var ios = Components.classes["@mozilla.org/network/io-service;1"]
|
||||||
|
.getService(Components.interfaces.nsIIOService);
|
||||||
|
return ios.newFileURI(file).spec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -133,7 +137,7 @@ Zotero.File = new function(){
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the contents of a file or input stream
|
* Get the contents of a file or input stream
|
||||||
* @param {nsIFile|nsIInputStream} file The file to read
|
* @param {nsIFile|nsIInputStream|string path} file The file to read
|
||||||
* @param {String} [charset] The character set; defaults to UTF-8
|
* @param {String} [charset] The character set; defaults to UTF-8
|
||||||
* @param {Integer} [maxLength] The maximum number of bytes to read
|
* @param {Integer} [maxLength] The maximum number of bytes to read
|
||||||
* @return {String} The contents of the file
|
* @return {String} The contents of the file
|
||||||
|
@ -141,6 +145,11 @@ Zotero.File = new function(){
|
||||||
*/
|
*/
|
||||||
this.getContents = function (file, charset, maxLength){
|
this.getContents = function (file, charset, maxLength){
|
||||||
var fis;
|
var fis;
|
||||||
|
|
||||||
|
if (typeof file == 'string') {
|
||||||
|
file = new FileUtils.File(file);
|
||||||
|
}
|
||||||
|
|
||||||
if(file instanceof Components.interfaces.nsIInputStream) {
|
if(file instanceof Components.interfaces.nsIInputStream) {
|
||||||
fis = file;
|
fis = file;
|
||||||
} else if(file instanceof Components.interfaces.nsIFile) {
|
} else if(file instanceof Components.interfaces.nsIFile) {
|
||||||
|
@ -282,7 +291,7 @@ Zotero.File = new function(){
|
||||||
* Return a promise for the contents of a URL as a string
|
* Return a promise for the contents of a URL as a string
|
||||||
*/
|
*/
|
||||||
this.getContentsFromURLAsync = function (url) {
|
this.getContentsFromURLAsync = function (url) {
|
||||||
return Zotero.HTTP.promise("GET", url, { responseType: "text" })
|
return Zotero.HTTP.request("GET", url, { responseType: "text" })
|
||||||
.then(function (xmlhttp) {
|
.then(function (xmlhttp) {
|
||||||
return xmlhttp.response;
|
return xmlhttp.response;
|
||||||
});
|
});
|
||||||
|
@ -364,16 +373,16 @@ Zotero.File = new function(){
|
||||||
/**
|
/**
|
||||||
* Delete a file if it exists, asynchronously
|
* Delete a file if it exists, asynchronously
|
||||||
*
|
*
|
||||||
* @return {Promise<Boolean>} A Q promise for TRUE if file was deleted,
|
* @return {Promise<Boolean>} A promise for TRUE if file was deleted, FALSE if missing
|
||||||
* FALSE if missing
|
|
||||||
*/
|
*/
|
||||||
this.deleteIfExists = function deleteIfExists(path) {
|
this.removeIfExists = function (path) {
|
||||||
return Zotero.Promise.resolve(OS.File.remove(path))
|
return Zotero.Promise.resolve(OS.File.remove(path))
|
||||||
.thenResolve(true)
|
.return(true)
|
||||||
.catch(function (e) {
|
.catch(function (e) {
|
||||||
if (e instanceof OS.File.Error && e.becauseNoSuchFile) {
|
if (e instanceof OS.File.Error && e.becauseNoSuchFile) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
Zotero.debug(path, 1);
|
||||||
throw e;
|
throw e;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -575,6 +584,19 @@ Zotero.File = new function(){
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.createDirectoryIfMissingAsync = function (path) {
|
||||||
|
return Zotero.Promise.resolve(
|
||||||
|
OS.File.makeDir(
|
||||||
|
path,
|
||||||
|
{
|
||||||
|
ignoreExisting: true,
|
||||||
|
unixMode: 0755
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether a directory is an ancestor directory of another directory/file
|
* Check whether a directory is an ancestor directory of another directory/file
|
||||||
*/
|
*/
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -30,28 +30,50 @@
|
||||||
*/
|
*/
|
||||||
Zotero.Styles = new function() {
|
Zotero.Styles = new function() {
|
||||||
var _initialized = false;
|
var _initialized = false;
|
||||||
var _styles, _visibleStyles, _cacheTranslatorData;
|
var _styles, _visibleStyles;
|
||||||
|
|
||||||
var _renamedStyles = null;
|
var _renamedStyles = null;
|
||||||
|
|
||||||
//Components.utils.import("resource://zotero/bluebird.js");
|
|
||||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||||
|
Components.utils.import("resource://gre/modules/FileUtils.jsm");
|
||||||
|
|
||||||
this.xsltProcessor = null;
|
this.xsltProcessor = null;
|
||||||
this.ios = Components.classes["@mozilla.org/network/io-service;1"].
|
|
||||||
getService(Components.interfaces.nsIIOService);
|
|
||||||
|
|
||||||
this.ns = {
|
this.ns = {
|
||||||
"csl":"http://purl.org/net/xbiblio/csl"
|
"csl":"http://purl.org/net/xbiblio/csl"
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEMP
|
|
||||||
// Until we get asynchronous style loading, load renamed styles at startup, since the
|
/**
|
||||||
// synchronous call we were using breaks the first drag of the session (on OS X, at least)
|
* Initializes styles cache, loading metadata for styles into memory
|
||||||
this.preinit = function () {
|
*/
|
||||||
|
this.reinit = Zotero.Promise.coroutine(function* () {
|
||||||
|
Zotero.debug("Initializing styles");
|
||||||
|
var start = new Date;
|
||||||
|
_initialized = true;
|
||||||
|
|
||||||
|
_styles = {};
|
||||||
|
_visibleStyles = [];
|
||||||
|
this.lastCSL = null;
|
||||||
|
|
||||||
|
// main dir
|
||||||
|
var dir = Zotero.getStylesDirectory().path;
|
||||||
|
var num = yield _readStylesFromDirectory(dir, false);
|
||||||
|
|
||||||
|
// hidden dir
|
||||||
|
var hiddenDir = OS.Path.join(dir, 'hidden');
|
||||||
|
if (yield OS.File.exists(hiddenDir)) {
|
||||||
|
num += yield _readStylesFromDirectory(hiddenDir, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Zotero.debug("Cached " + num + " styles in " + (new Date - start) + " ms");
|
||||||
|
|
||||||
_renamedStyles = {};
|
_renamedStyles = {};
|
||||||
Zotero.HTTP.promise(
|
yield Zotero.HTTP.request(
|
||||||
"GET", "resource://zotero/schema/renamed-styles.json", { responseType: 'json' }
|
"GET",
|
||||||
|
"resource://zotero/schema/renamed-styles.json",
|
||||||
|
{
|
||||||
|
responseType: 'json'
|
||||||
|
}
|
||||||
)
|
)
|
||||||
.then(function (xmlhttp) {
|
.then(function (xmlhttp) {
|
||||||
// Map some obsolete styles to current ones
|
// Map some obsolete styles to current ones
|
||||||
|
@ -59,87 +81,70 @@ Zotero.Styles = new function() {
|
||||||
_renamedStyles = xmlhttp.response;
|
_renamedStyles = xmlhttp.response;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.done();
|
});
|
||||||
}
|
this.init = Zotero.lazy(this.reinit);
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes styles cache, loading metadata for styles into memory
|
|
||||||
*/
|
|
||||||
this.init = function() {
|
|
||||||
_initialized = true;
|
|
||||||
|
|
||||||
var start = (new Date()).getTime()
|
|
||||||
|
|
||||||
_styles = {};
|
|
||||||
_visibleStyles = [];
|
|
||||||
_cacheTranslatorData = Zotero.Prefs.get("cacheTranslatorData");
|
|
||||||
this.lastCSL = null;
|
|
||||||
|
|
||||||
// main dir
|
|
||||||
var dir = Zotero.getStylesDirectory();
|
|
||||||
var i = _readStylesFromDirectory(dir, false);
|
|
||||||
|
|
||||||
// hidden dir
|
|
||||||
dir.append("hidden");
|
|
||||||
if(dir.exists()) i += _readStylesFromDirectory(dir, true);
|
|
||||||
|
|
||||||
Zotero.debug("Cached "+i+" styles in "+((new Date()).getTime() - start)+" ms");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads all styles from a given directory and caches their metadata
|
* Reads all styles from a given directory and caches their metadata
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function _readStylesFromDirectory(dir, hidden) {
|
var _readStylesFromDirectory = Zotero.Promise.coroutine(function* (dir, hidden) {
|
||||||
var i = 0;
|
var numCached = 0;
|
||||||
var contents = dir.directoryEntries;
|
|
||||||
while(contents.hasMoreElements()) {
|
var iterator = new OS.File.DirectoryIterator(dir);
|
||||||
var file = contents.getNext().QueryInterface(Components.interfaces.nsIFile),
|
try {
|
||||||
filename = file.leafName;
|
while (true) {
|
||||||
if(!filename || filename[0] === "."
|
let entries = yield iterator.nextBatch(10); // TODO: adjust as necessary
|
||||||
|| filename.substr(-4).toLowerCase() !== ".csl"
|
if (!entries.length) break;
|
||||||
|| file.isDirectory()) continue;
|
|
||||||
|
for (let i = 0; i < entries.length; i++) {
|
||||||
try {
|
let entry = entries[i];
|
||||||
var style = new Zotero.Style(file);
|
let path = entry.path;
|
||||||
}
|
let fileName = entry.name;
|
||||||
catch (e) {
|
if (!fileName || fileName[0] === "."
|
||||||
Zotero.log(
|
|| fileName.substr(-4).toLowerCase() !== ".csl"
|
||||||
"Error loading style '" + file.leafName + "': " + e.message,
|
|| entry.isDir) continue;
|
||||||
"error",
|
|
||||||
file.path,
|
try {
|
||||||
null,
|
let code = yield Zotero.File.getContentsAsync(path);
|
||||||
e.lineNumber
|
var style = new Zotero.Style(code, path);
|
||||||
);
|
}
|
||||||
continue;
|
catch (e) {
|
||||||
}
|
Components.utils.reportError(e);
|
||||||
if(style.styleID) {
|
Zotero.debug(e, 1);
|
||||||
if(_styles[style.styleID]) {
|
continue;
|
||||||
// same style is already cached
|
}
|
||||||
Zotero.log('Style with ID '+style.styleID+' already loaded from "'+
|
if(style.styleID) {
|
||||||
_styles[style.styleID].file.leafName+'"', "error",
|
// same style is already cached
|
||||||
Zotero.Styles.ios.newFileURI(style.file).spec);
|
if (_styles[style.styleID]) {
|
||||||
} else {
|
Components.utils.reportError('Style with ID ' + style.styleID
|
||||||
// add to cache
|
+ ' already loaded from ' + _styles[style.styleID].fileName);
|
||||||
_styles[style.styleID] = style;
|
} else {
|
||||||
_styles[style.styleID].hidden = hidden;
|
// add to cache
|
||||||
if(!hidden) _visibleStyles.push(style);
|
_styles[style.styleID] = style;
|
||||||
|
_styles[style.styleID].hidden = hidden;
|
||||||
|
if(!hidden) _visibleStyles.push(style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
numCached++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
return i;
|
finally {
|
||||||
}
|
iterator.close();
|
||||||
|
}
|
||||||
|
return numCached;
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a style with a given ID
|
* Gets a style with a given ID
|
||||||
* @param {String} id
|
* @param {String} id
|
||||||
* @param {Boolean} skipMappings Don't automatically return renamed style
|
* @param {Boolean} skipMappings Don't automatically return renamed style
|
||||||
*/
|
*/
|
||||||
this.get = function(id, skipMappings) {
|
this.get = function (id, skipMappings) {
|
||||||
if(!_initialized) this.init();
|
if (!_initialized) {
|
||||||
|
throw new Zotero.Exception.UnloadedDataException("Styles not yet loaded", 'styles');
|
||||||
// TODO: With asynchronous style loading, move renamedStyles call back here
|
}
|
||||||
|
|
||||||
if(!skipMappings) {
|
if(!skipMappings) {
|
||||||
var prefix = "http://www.zotero.org/styles/";
|
var prefix = "http://www.zotero.org/styles/";
|
||||||
|
@ -156,20 +161,24 @@ Zotero.Styles = new function() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all visible styles
|
* Gets all visible styles
|
||||||
* @return {Zotero.Style[]} An array of Zotero.Style objects
|
* @return {Promise<Zotero.Style[]>} A promise for an array of Zotero.Style objects
|
||||||
*/
|
*/
|
||||||
this.getVisible = function() {
|
this.getVisible = function () {
|
||||||
if(!_initialized || !_cacheTranslatorData) this.init();
|
return this.init().then(function () {
|
||||||
return _visibleStyles.slice(0);
|
return _visibleStyles.slice(0);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all styles
|
* Gets all styles
|
||||||
* @return {Object} An object whose keys are style IDs, and whose values are Zotero.Style objects
|
*
|
||||||
|
* @return {Promise<Object>} A promise for an object with style IDs for keys and
|
||||||
|
* Zotero.Style objects for values
|
||||||
*/
|
*/
|
||||||
this.getAll = function() {
|
this.getAll = function () {
|
||||||
if(!_initialized || !_cacheTranslatorData) this.init();
|
return this.init().then(function () {
|
||||||
return _styles;
|
return _styles;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -200,8 +209,9 @@ Zotero.Styles = new function() {
|
||||||
* @param {String} origin The origin of the style, either a filename or URL, to be
|
* @param {String} origin The origin of the style, either a filename or URL, to be
|
||||||
* displayed in dialogs referencing the style
|
* displayed in dialogs referencing the style
|
||||||
*/
|
*/
|
||||||
this.install = function(style, origin) {
|
this.install = Zotero.Promise.coroutine(function* (style, origin) {
|
||||||
var styleInstalled;
|
var styleInstalled;
|
||||||
|
|
||||||
if(style instanceof Components.interfaces.nsIFile) {
|
if(style instanceof Components.interfaces.nsIFile) {
|
||||||
// handle nsIFiles
|
// handle nsIFiles
|
||||||
var url = Services.io.newFileURI(style);
|
var url = Services.io.newFileURI(style);
|
||||||
|
@ -224,7 +234,7 @@ Zotero.Styles = new function() {
|
||||||
origin, "styles.install.title", error)).present();
|
origin, "styles.install.title", error)).present();
|
||||||
}
|
}
|
||||||
}).done();
|
}).done();
|
||||||
}
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Installs a style
|
* Installs a style
|
||||||
|
@ -234,176 +244,183 @@ Zotero.Styles = new function() {
|
||||||
* @param {Boolean} [hidden] Whether style is to be hidden.
|
* @param {Boolean} [hidden] Whether style is to be hidden.
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
function _install(style, origin, hidden) {
|
var _install = Zotero.Promise.coroutine(function* (style, origin, hidden) {
|
||||||
if(!_initialized || !_cacheTranslatorData) Zotero.Styles.init();
|
if (!_initialized) yield Zotero.Styles.init();
|
||||||
|
|
||||||
var existingFile, destFile, source, styleID
|
var existingFile, destFile, source, styleID
|
||||||
return Zotero.Promise.try(function() {
|
|
||||||
// First, parse style and make sure it's valid XML
|
// First, parse style and make sure it's valid XML
|
||||||
var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
|
var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
|
||||||
.createInstance(Components.interfaces.nsIDOMParser),
|
.createInstance(Components.interfaces.nsIDOMParser),
|
||||||
doc = parser.parseFromString(style, "application/xml");
|
doc = parser.parseFromString(style, "application/xml");
|
||||||
|
|
||||||
styleID = Zotero.Utilities.xpathText(doc, '/csl:style/csl:info[1]/csl:id[1]',
|
styleID = Zotero.Utilities.xpathText(doc, '/csl:style/csl:info[1]/csl:id[1]',
|
||||||
Zotero.Styles.ns),
|
Zotero.Styles.ns),
|
||||||
// Get file name from URL
|
// Get file name from URL
|
||||||
m = /[^\/]+$/.exec(styleID),
|
m = /[^\/]+$/.exec(styleID),
|
||||||
fileName = Zotero.File.getValidFileName(m ? m[0] : styleID),
|
fileName = Zotero.File.getValidFileName(m ? m[0] : styleID),
|
||||||
title = Zotero.Utilities.xpathText(doc, '/csl:style/csl:info[1]/csl:title[1]',
|
title = Zotero.Utilities.xpathText(doc, '/csl:style/csl:info[1]/csl:title[1]',
|
||||||
Zotero.Styles.ns);
|
|
||||||
|
|
||||||
if(!styleID || !title) {
|
|
||||||
// If it's not valid XML, we'll return a promise that immediately resolves
|
|
||||||
// to an error
|
|
||||||
throw new Zotero.Exception.Alert("styles.installError", origin,
|
|
||||||
"styles.install.title", "Style is not valid XML, or the styleID or title is missing");
|
|
||||||
}
|
|
||||||
|
|
||||||
// look for a parent
|
|
||||||
source = Zotero.Utilities.xpathText(doc,
|
|
||||||
'/csl:style/csl:info[1]/csl:link[@rel="source" or @rel="independent-parent"][1]/@href',
|
|
||||||
Zotero.Styles.ns);
|
Zotero.Styles.ns);
|
||||||
if(source == styleID) {
|
|
||||||
throw new Zotero.Exception.Alert("styles.installError", origin,
|
if(!styleID || !title) {
|
||||||
"styles.install.title", "Style references itself as source");
|
// If it's not valid XML, we'll return a promise that immediately resolves
|
||||||
|
// to an error
|
||||||
|
throw new Zotero.Exception.Alert("styles.installError", origin,
|
||||||
|
"styles.install.title", "Style is not valid XML, or the styleID or title is missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
// look for a parent
|
||||||
|
source = Zotero.Utilities.xpathText(doc,
|
||||||
|
'/csl:style/csl:info[1]/csl:link[@rel="source" or @rel="independent-parent"][1]/@href',
|
||||||
|
Zotero.Styles.ns);
|
||||||
|
if(source == styleID) {
|
||||||
|
throw new Zotero.Exception.Alert("styles.installError", origin,
|
||||||
|
"styles.install.title", "Style references itself as source");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure csl extension
|
||||||
|
if(fileName.substr(-4).toLowerCase() != ".csl") fileName += ".csl";
|
||||||
|
|
||||||
|
destFile = Zotero.getStylesDirectory();
|
||||||
|
var destFileHidden = destFile.clone();
|
||||||
|
destFile.append(fileName);
|
||||||
|
destFileHidden.append("hidden");
|
||||||
|
if(hidden) Zotero.File.createDirectoryIfMissing(destFileHidden);
|
||||||
|
destFileHidden.append(fileName);
|
||||||
|
|
||||||
|
// look for an existing style with the same styleID or filename
|
||||||
|
var existingTitle;
|
||||||
|
if(_styles[styleID]) {
|
||||||
|
existingFile = _styles[styleID].file;
|
||||||
|
existingTitle = _styles[styleID].title;
|
||||||
|
} else {
|
||||||
|
if(destFile.exists()) {
|
||||||
|
existingFile = destFile;
|
||||||
|
} else if(destFileHidden.exists()) {
|
||||||
|
existingFile = destFileHidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure csl extension
|
if(existingFile) {
|
||||||
if(fileName.substr(-4).toLowerCase() != ".csl") fileName += ".csl";
|
// find associated style
|
||||||
|
for each(var existingStyle in _styles) {
|
||||||
destFile = Zotero.getStylesDirectory();
|
if(destFile.equals(existingStyle.file)) {
|
||||||
var destFileHidden = destFile.clone();
|
|
||||||
destFile.append(fileName);
|
|
||||||
destFileHidden.append("hidden");
|
|
||||||
if(hidden) Zotero.File.createDirectoryIfMissing(destFileHidden);
|
|
||||||
destFileHidden.append(fileName);
|
|
||||||
|
|
||||||
// look for an existing style with the same styleID or filename
|
|
||||||
var existingTitle;
|
|
||||||
if(_styles[styleID]) {
|
|
||||||
existingFile = _styles[styleID].file;
|
|
||||||
existingTitle = _styles[styleID].title;
|
|
||||||
} else {
|
|
||||||
if(destFile.exists()) {
|
|
||||||
existingFile = destFile;
|
|
||||||
} else if(destFileHidden.exists()) {
|
|
||||||
existingFile = destFileHidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(existingFile) {
|
|
||||||
// find associated style
|
|
||||||
for each(var existingStyle in _styles) {
|
|
||||||
if(destFile.equals(existingStyle.file)) {
|
|
||||||
existingTitle = existingStyle.title;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// also look for an existing style with the same title
|
|
||||||
if(!existingFile) {
|
|
||||||
for each(var existingStyle in Zotero.Styles.getAll()) {
|
|
||||||
if(title === existingStyle.title) {
|
|
||||||
existingFile = existingStyle.file;
|
|
||||||
existingTitle = existingStyle.title;
|
existingTitle = existingStyle.title;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// display a dialog to tell the user we're about to install the style
|
|
||||||
if(hidden) {
|
|
||||||
destFile = destFileHidden;
|
|
||||||
} else {
|
|
||||||
if(existingTitle) {
|
|
||||||
var text = Zotero.getString('styles.updateStyle', [existingTitle, title, origin]);
|
|
||||||
} else {
|
|
||||||
var text = Zotero.getString('styles.installStyle', [title, origin]);
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = Services.prompt.confirmEx(null, Zotero.getString('styles.install.title'),
|
|
||||||
text,
|
|
||||||
((Services.prompt.BUTTON_POS_0) * (Services.prompt.BUTTON_TITLE_IS_STRING)
|
|
||||||
+ (Services.prompt.BUTTON_POS_1) * (Services.prompt.BUTTON_TITLE_CANCEL)),
|
|
||||||
Zotero.getString('general.install'), null, null, null, {}
|
|
||||||
);
|
|
||||||
|
|
||||||
if(index !== 0) {
|
|
||||||
throw new Zotero.Exception.UserCancelled("style installation");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Zotero.Styles.validate(style).catch(function(validationErrors) {
|
// also look for an existing style with the same title
|
||||||
Zotero.logError("Style from "+origin+" failed to validate:\n\n"+validationErrors);
|
if(!existingFile) {
|
||||||
|
let styles = yield Zotero.Styles.getAll();
|
||||||
// If validation fails on the parent of a dependent style, ignore it (for now)
|
for (let i in styles) {
|
||||||
if(hidden) return;
|
let existingStyle = styles[i];
|
||||||
|
if(title === existingStyle.title) {
|
||||||
// If validation fails on a different style, we ask the user if s/he really
|
existingFile = existingStyle.file;
|
||||||
// wants to install it
|
existingTitle = existingStyle.title;
|
||||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
break;
|
||||||
var shouldInstall = Services.prompt.confirmEx(null,
|
|
||||||
Zotero.getString('styles.install.title'),
|
|
||||||
Zotero.getString('styles.validationWarning', origin),
|
|
||||||
(Services.prompt.BUTTON_POS_0) * (Services.prompt.BUTTON_TITLE_OK)
|
|
||||||
+ (Services.prompt.BUTTON_POS_1) * (Services.prompt.BUTTON_TITLE_CANCEL)
|
|
||||||
+ Services.prompt.BUTTON_POS_1_DEFAULT + Services.prompt.BUTTON_DELAY_ENABLE,
|
|
||||||
null, null, null, null, {}
|
|
||||||
);
|
|
||||||
if(shouldInstall !== 0) {
|
|
||||||
throw new Zotero.Exception.UserCancelled("style installation");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).then(function() {
|
|
||||||
// User wants to install/update
|
|
||||||
if(source && !_styles[source]) {
|
|
||||||
// Need to fetch source
|
|
||||||
if(source.substr(0, 7) === "http://" || source.substr(0, 8) === "https://") {
|
|
||||||
return Zotero.HTTP.promise("GET", source).then(function(xmlhttp) {
|
|
||||||
return _install(xmlhttp.responseText, origin, true);
|
|
||||||
}).catch(function(error) {
|
|
||||||
if(typeof error === "object" && error instanceof Zotero.Exception.Alert) {
|
|
||||||
throw new Zotero.Exception.Alert("styles.installSourceError", [origin, source],
|
|
||||||
"styles.install.title", error);
|
|
||||||
} else {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
throw new Zotero.Exception.Alert("styles.installSourceError", [origin, source],
|
|
||||||
"styles.install.title", "Source CSL URI is invalid");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).then(function() {
|
}
|
||||||
// Dependent style has been retrieved if there was one, so we're ready to
|
|
||||||
// continue
|
// display a dialog to tell the user we're about to install the style
|
||||||
|
if(hidden) {
|
||||||
|
destFile = destFileHidden;
|
||||||
|
} else {
|
||||||
|
if(existingTitle) {
|
||||||
|
var text = Zotero.getString('styles.updateStyle', [existingTitle, title, origin]);
|
||||||
|
} else {
|
||||||
|
var text = Zotero.getString('styles.installStyle', [title, origin]);
|
||||||
|
}
|
||||||
|
|
||||||
// Remove any existing file with a different name
|
var index = Services.prompt.confirmEx(null, Zotero.getString('styles.install.title'),
|
||||||
if(existingFile) existingFile.remove(false);
|
text,
|
||||||
|
((Services.prompt.BUTTON_POS_0) * (Services.prompt.BUTTON_TITLE_IS_STRING)
|
||||||
|
+ (Services.prompt.BUTTON_POS_1) * (Services.prompt.BUTTON_TITLE_CANCEL)),
|
||||||
|
Zotero.getString('general.install'), null, null, null, {}
|
||||||
|
);
|
||||||
|
|
||||||
return Zotero.File.putContentsAsync(destFile, style);
|
if(index !== 0) {
|
||||||
}).then(function() {
|
throw new Zotero.Exception.UserCancelled("style installation");
|
||||||
// Cache
|
}
|
||||||
Zotero.Styles.init();
|
}
|
||||||
|
|
||||||
|
yield Zotero.Styles.validate(style)
|
||||||
|
.catch(function(validationErrors) {
|
||||||
|
Zotero.logError("Style from " + origin + " failed to validate:\n\n" + validationErrors);
|
||||||
|
|
||||||
// Refresh preferences windows
|
// If validation fails on the parent of a dependent style, ignore it (for now)
|
||||||
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
|
if(hidden) return;
|
||||||
getService(Components.interfaces.nsIWindowMediator);
|
|
||||||
var enumerator = wm.getEnumerator("zotero:pref");
|
// If validation fails on a different style, we ask the user if s/he really
|
||||||
while(enumerator.hasMoreElements()) {
|
// wants to install it
|
||||||
var win = enumerator.getNext();
|
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||||
if(win.Zotero_Preferences.Cite) {
|
var shouldInstall = Services.prompt.confirmEx(null,
|
||||||
win.Zotero_Preferences.Cite.refreshStylesList(styleID);
|
Zotero.getString('styles.install.title'),
|
||||||
}
|
Zotero.getString('styles.validationWarning', origin),
|
||||||
|
(Services.prompt.BUTTON_POS_0) * (Services.prompt.BUTTON_TITLE_OK)
|
||||||
|
+ (Services.prompt.BUTTON_POS_1) * (Services.prompt.BUTTON_TITLE_CANCEL)
|
||||||
|
+ Services.prompt.BUTTON_POS_1_DEFAULT + Services.prompt.BUTTON_DELAY_ENABLE,
|
||||||
|
null, null, null, null, {}
|
||||||
|
);
|
||||||
|
if(shouldInstall !== 0) {
|
||||||
|
throw new Zotero.Exception.UserCancelled("style installation");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
// User wants to install/update
|
||||||
|
if(source && !_styles[source]) {
|
||||||
|
// Need to fetch source
|
||||||
|
if(source.substr(0, 7) === "http://" || source.substr(0, 8) === "https://") {
|
||||||
|
try {
|
||||||
|
let xmlhttp = yield Zotero.HTTP.request("GET", source);
|
||||||
|
yield _install(xmlhttp.responseText, origin, true);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
if (typeof e === "object" && e instanceof Zotero.Exception.Alert) {
|
||||||
|
throw new Zotero.Exception.Alert(
|
||||||
|
"styles.installSourceError",
|
||||||
|
[origin, source],
|
||||||
|
"styles.install.title",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Zotero.Exception.Alert("styles.installSourceError", [origin, source],
|
||||||
|
"styles.install.title", "Source CSL URI is invalid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dependent style has been retrieved if there was one, so we're ready to
|
||||||
|
// continue
|
||||||
|
|
||||||
|
// Remove any existing file with a different name
|
||||||
|
if(existingFile) existingFile.remove(false);
|
||||||
|
|
||||||
|
yield Zotero.File.putContentsAsync(destFile, style);
|
||||||
|
|
||||||
|
yield Zotero.Styles.reinit();
|
||||||
|
|
||||||
|
// Refresh preferences windows
|
||||||
|
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
|
||||||
|
getService(Components.interfaces.nsIWindowMediator);
|
||||||
|
var enumerator = wm.getEnumerator("zotero:pref");
|
||||||
|
while(enumerator.hasMoreElements()) {
|
||||||
|
var win = enumerator.getNext();
|
||||||
|
if(win.Zotero_Preferences.Cite) {
|
||||||
|
yield win.Zotero_Preferences.Cite.refreshStylesList(styleID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class Represents a style file and its metadata
|
* @class Represents a style file and its metadata
|
||||||
* @property {nsIFile} file The path to the style file
|
* @property {String} path The path to the style file
|
||||||
|
* @property {String} fileName The name of the style file
|
||||||
* @property {String} styleID
|
* @property {String} styleID
|
||||||
* @property {String} url The URL where the style can be found (rel="self")
|
* @property {String} url The URL where the style can be found (rel="self")
|
||||||
* @property {String} type "csl" for CSL styles
|
* @property {String} type "csl" for CSL styles
|
||||||
|
@ -416,25 +433,25 @@ Zotero.Styles = new function() {
|
||||||
* @property {Boolean} hidden True if this style is hidden in style selection dialogs, false if it
|
* @property {Boolean} hidden True if this style is hidden in style selection dialogs, false if it
|
||||||
* is not
|
* is not
|
||||||
*/
|
*/
|
||||||
Zotero.Style = function(arg) {
|
Zotero.Style = function (style, path) {
|
||||||
if(typeof arg === "string") {
|
if (typeof style != "string") {
|
||||||
this.string = arg;
|
throw new Error("Style code must be a string");
|
||||||
} else if(typeof arg === "object") {
|
|
||||||
this.file = arg;
|
|
||||||
} else {
|
|
||||||
throw "Invalid argument passed to Zotero.Style";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.type = "csl";
|
this.type = "csl";
|
||||||
|
|
||||||
var style = typeof arg === "string" ? arg : Zotero.File.getContents(arg),
|
var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
|
||||||
parser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
|
|
||||||
.createInstance(Components.interfaces.nsIDOMParser),
|
.createInstance(Components.interfaces.nsIDOMParser),
|
||||||
doc = parser.parseFromString(style, "application/xml");
|
doc = parser.parseFromString(style, "application/xml");
|
||||||
if(doc.documentElement.localName === "parsererror") {
|
if(doc.documentElement.localName === "parsererror") {
|
||||||
throw new Error("File is not valid XML");
|
throw new Error("File is not valid XML");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (path) {
|
||||||
|
this.path = path;
|
||||||
|
this.fileName = OS.Path.basename(path);
|
||||||
|
}
|
||||||
|
|
||||||
this.styleID = Zotero.Utilities.xpathText(doc, '/csl:style/csl:info[1]/csl:id[1]',
|
this.styleID = Zotero.Utilities.xpathText(doc, '/csl:style/csl:info[1]/csl:id[1]',
|
||||||
Zotero.Styles.ns);
|
Zotero.Styles.ns);
|
||||||
this.url = Zotero.Utilities.xpathText(doc,
|
this.url = Zotero.Utilities.xpathText(doc,
|
||||||
|
@ -494,8 +511,10 @@ Zotero.Style.prototype.getCiteProc = function(automaticJournalAbbreviations) {
|
||||||
if(this.source) {
|
if(this.source) {
|
||||||
var parentStyle = Zotero.Styles.get(this.source);
|
var parentStyle = Zotero.Styles.get(this.source);
|
||||||
if(!parentStyle) {
|
if(!parentStyle) {
|
||||||
throw(new Error('Style references '+this.source+', but this style is not installed',
|
throw new Error(
|
||||||
Zotero.Styles.ios.newFileURI(this.file).spec, null));
|
'Style references ' + this.source + ', but this style is not installed',
|
||||||
|
Zotero.Utilities.pathToFileURI(this.path)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
var version = parentStyle._version;
|
var version = parentStyle._version;
|
||||||
|
|
||||||
|
@ -552,11 +571,6 @@ Zotero.Style.prototype.getCiteProc = function(automaticJournalAbbreviations) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Zotero.Style.prototype.__defineGetter__("csl", function() {
|
|
||||||
Zotero.logError("Zotero.Style.csl is deprecated. Use Zotero.Style.getCiteProc()");
|
|
||||||
return this.getCiteProc();
|
|
||||||
});
|
|
||||||
|
|
||||||
Zotero.Style.prototype.__defineGetter__("class",
|
Zotero.Style.prototype.__defineGetter__("class",
|
||||||
/**
|
/**
|
||||||
* Retrieves the style class, either from the metadata that's already loaded or by loading the file
|
* Retrieves the style class, either from the metadata that's already loaded or by loading the file
|
||||||
|
@ -578,8 +592,7 @@ function() {
|
||||||
// use hasBibliography from source style
|
// use hasBibliography from source style
|
||||||
var parentStyle = Zotero.Styles.get(this.source);
|
var parentStyle = Zotero.Styles.get(this.source);
|
||||||
if(!parentStyle) {
|
if(!parentStyle) {
|
||||||
throw(new Error('Style references '+this.source+', but this style is not installed',
|
throw new Error('Style references missing parent ' + this.source);
|
||||||
Zotero.Styles.ios.newFileURI(this.file).spec, null));
|
|
||||||
}
|
}
|
||||||
return parentStyle.hasBibliography;
|
return parentStyle.hasBibliography;
|
||||||
}
|
}
|
||||||
|
@ -610,12 +623,11 @@ function() {
|
||||||
// parent/child
|
// parent/child
|
||||||
var formatCSL = Zotero.Styles.get(this.source);
|
var formatCSL = Zotero.Styles.get(this.source);
|
||||||
if(!formatCSL) {
|
if(!formatCSL) {
|
||||||
throw(new Error('Style references '+this.source+', but this style is not installed',
|
throw new Error('Style references missing parent ' + this.source);
|
||||||
Zotero.Styles.ios.newFileURI(this.file).spec, null));
|
|
||||||
}
|
}
|
||||||
return formatCSL.file;
|
return formatCSL.path;
|
||||||
} else if(this.file) {
|
} else if (this.path) {
|
||||||
return this.file;
|
return this.path;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
@ -633,15 +645,16 @@ Zotero.Style.prototype.getXML = function() {
|
||||||
/**
|
/**
|
||||||
* Deletes a style
|
* Deletes a style
|
||||||
*/
|
*/
|
||||||
Zotero.Style.prototype.remove = function() {
|
Zotero.Style.prototype.remove = Zotero.Promise.coroutine(function* () {
|
||||||
if(!this.file) {
|
if (!this.path) {
|
||||||
throw "Cannot delete a style with no associated file."
|
throw new Error("Cannot delete a style with no associated file")
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure no styles depend on this one
|
// make sure no styles depend on this one
|
||||||
var dependentStyles = false;
|
var dependentStyles = false;
|
||||||
var styles = Zotero.Styles.getAll();
|
var styles = yield Zotero.Styles.getAll();
|
||||||
for each(var style in styles) {
|
for (let i in styles) {
|
||||||
|
let style = styles[i];
|
||||||
if(style.source == this.styleID) {
|
if(style.source == this.styleID) {
|
||||||
dependentStyles = true;
|
dependentStyles = true;
|
||||||
break;
|
break;
|
||||||
|
@ -650,13 +663,12 @@ Zotero.Style.prototype.remove = function() {
|
||||||
|
|
||||||
if(dependentStyles) {
|
if(dependentStyles) {
|
||||||
// copy dependent styles to hidden directory
|
// copy dependent styles to hidden directory
|
||||||
var hiddenDir = Zotero.getStylesDirectory();
|
let hiddenDir = OS.Path.join(Zotero.getStylesDirectory().path, 'hidden');
|
||||||
hiddenDir.append("hidden");
|
yield Zotero.File.createDirectoryIfMissingAsync(hiddenDir);
|
||||||
Zotero.File.createDirectoryIfMissing(hiddenDir);
|
yield OS.File.move(this.path, OS.Path.join(hiddenDir, OS.Path.basename(this.path)));
|
||||||
this.file.moveTo(hiddenDir, null);
|
|
||||||
} else {
|
} else {
|
||||||
// remove defunct files
|
// remove defunct files
|
||||||
this.file.remove(false);
|
yield OS.File.remove(this.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check to see if this style depended on a hidden one
|
// check to see if this style depended on a hidden one
|
||||||
|
@ -666,7 +678,9 @@ Zotero.Style.prototype.remove = function() {
|
||||||
var deleteSource = true;
|
var deleteSource = true;
|
||||||
|
|
||||||
// check to see if any other styles depend on the hidden one
|
// check to see if any other styles depend on the hidden one
|
||||||
for each(var style in Zotero.Styles.getAll()) {
|
let styles = yield Zotero.Styles.getAll();
|
||||||
|
for (let i in styles) {
|
||||||
|
let style = styles[i];
|
||||||
if(style.source == this.source && style.styleID != this.styleID) {
|
if(style.source == this.source && style.styleID != this.styleID) {
|
||||||
deleteSource = false;
|
deleteSource = false;
|
||||||
break;
|
break;
|
||||||
|
@ -675,10 +689,10 @@ Zotero.Style.prototype.remove = function() {
|
||||||
|
|
||||||
// if it was only this style with the dependency, delete the source
|
// if it was only this style with the dependency, delete the source
|
||||||
if(deleteSource) {
|
if(deleteSource) {
|
||||||
source.remove();
|
yield source.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.Styles.init();
|
return Zotero.Styles.reinit();
|
||||||
}
|
});
|
||||||
|
|
|
@ -61,7 +61,8 @@ var TRANSLATOR_SAVE_PROPERTIES = TRANSLATOR_REQUIRED_PROPERTIES.concat(["browser
|
||||||
* @property {String} lastUpdated SQL-style date and time of translator's last update
|
* @property {String} lastUpdated SQL-style date and time of translator's last update
|
||||||
* @property {String} code The executable JavaScript for the translator
|
* @property {String} code The executable JavaScript for the translator
|
||||||
* @property {Boolean} cacheCode Whether to cache code for this session (non-connector only)
|
* @property {Boolean} cacheCode Whether to cache code for this session (non-connector only)
|
||||||
* @property {nsIFile} [file] File corresponding to this translator (non-connector only)
|
* @property {String} [path] File path corresponding to this translator (non-connector only)
|
||||||
|
* @property {String} [fileName] File name corresponding to this translator (non-connector only)
|
||||||
*/
|
*/
|
||||||
Zotero.Translator = function(info) {
|
Zotero.Translator = function(info) {
|
||||||
this.init(info);
|
this.init(info);
|
||||||
|
@ -119,7 +120,10 @@ Zotero.Translator.prototype.init = function(info) {
|
||||||
delete this.webRegexp;
|
delete this.webRegexp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(info.file) this.file = info.file;
|
if (info.path) {
|
||||||
|
this.path = info.path;
|
||||||
|
this.fileName = OS.Path.basename(info.path);
|
||||||
|
}
|
||||||
if(info.code && this.cacheCode) {
|
if(info.code && this.cacheCode) {
|
||||||
this.code = info.code;
|
this.code = info.code;
|
||||||
} else if(this.hasOwnProperty("code")) {
|
} else if(this.hasOwnProperty("code")) {
|
||||||
|
@ -148,7 +152,7 @@ Zotero.Translator.prototype.getCode = function() {
|
||||||
return code;
|
return code;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
var promise = Zotero.File.getContentsAsync(this.file);
|
var promise = Zotero.File.getContentsAsync(this.path);
|
||||||
if(this.cacheCode) {
|
if(this.cacheCode) {
|
||||||
// Cache target-less web translators for session, since we
|
// Cache target-less web translators for session, since we
|
||||||
// will use them a lot
|
// will use them a lot
|
||||||
|
@ -168,7 +172,7 @@ Zotero.Translator.prototype.serialize = function(properties) {
|
||||||
var info = {};
|
var info = {};
|
||||||
for(var i in properties) {
|
for(var i in properties) {
|
||||||
var property = properties[i];
|
var property = properties[i];
|
||||||
info[property] = translator[property];
|
info[property] = this[property];
|
||||||
}
|
}
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
@ -182,10 +186,12 @@ Zotero.Translator.prototype.serialize = function(properties) {
|
||||||
* @param {Integer} colNumber
|
* @param {Integer} colNumber
|
||||||
*/
|
*/
|
||||||
Zotero.Translator.prototype.logError = function(message, type, line, lineNumber, colNumber) {
|
Zotero.Translator.prototype.logError = function(message, type, line, lineNumber, colNumber) {
|
||||||
if(Zotero.isFx && this.file) {
|
if (Zotero.isFx && this.path) {
|
||||||
|
Components.utils.import("resource://gre/modules/FileUtils.jsm");
|
||||||
|
var file = new FileUtils.File(this.path);
|
||||||
var ios = Components.classes["@mozilla.org/network/io-service;1"].
|
var ios = Components.classes["@mozilla.org/network/io-service;1"].
|
||||||
getService(Components.interfaces.nsIIOService);
|
getService(Components.interfaces.nsIIOService);
|
||||||
Zotero.log(message, type ? type : "error", ios.newFileURI(this.file).spec);
|
Zotero.log(message, type ? type : "error", ios.newFileURI(file).spec);
|
||||||
} else {
|
} else {
|
||||||
Zotero.logError(message);
|
Zotero.logError(message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,85 +35,113 @@ Zotero.Translators = new function() {
|
||||||
var _initialized = false;
|
var _initialized = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes translator cache, loading all relevant translators into memory
|
* Initializes translator cache, loading all translator metadata into memory
|
||||||
*/
|
*/
|
||||||
this.reinit = Zotero.Promise.coroutine(function* () {
|
this.reinit = Zotero.Promise.coroutine(function* () {
|
||||||
var start = (new Date()).getTime();
|
if (_initialized) {
|
||||||
var transactionStarted = false;
|
Zotero.debug("Translators already initialized", 2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Zotero.debug("Initializing translators");
|
||||||
|
var start = new Date;
|
||||||
|
_initialized = true;
|
||||||
|
|
||||||
_cache = {"import":[], "export":[], "web":[], "search":[]};
|
_cache = {"import":[], "export":[], "web":[], "search":[]};
|
||||||
_translators = {};
|
_translators = {};
|
||||||
|
|
||||||
var dbCacheResults = yield Zotero.DB.queryAsync("SELECT leafName, translatorJSON, "+
|
var sql = "SELECT fileName, metadataJSON, lastModifiedTime FROM translatorCache";
|
||||||
"code, lastModifiedTime FROM translatorCache");
|
var dbCacheResults = yield Zotero.DB.queryAsync(sql);
|
||||||
var dbCache = {};
|
var dbCache = {};
|
||||||
for each(var cacheEntry in dbCacheResults) {
|
for (let i = 0; i < dbCacheResults.length; i++) {
|
||||||
dbCache[cacheEntry.leafName] = cacheEntry;
|
let entry = dbCacheResults[i];
|
||||||
|
dbCache[entry.fileName] = entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
var i = 0;
|
var numCached = 0;
|
||||||
var filesInCache = {};
|
var filesInCache = {};
|
||||||
var contents = Zotero.getTranslatorsDirectory().directoryEntries;
|
var translatorsDir = Zotero.getTranslatorsDirectory().path;
|
||||||
while(contents.hasMoreElements()) {
|
var iterator = new OS.File.DirectoryIterator(translatorsDir);
|
||||||
var file = contents.getNext().QueryInterface(Components.interfaces.nsIFile);
|
try {
|
||||||
var leafName = file.leafName;
|
while (true) {
|
||||||
if(!(/^[^.].*\.js$/.test(leafName))) continue;
|
let entries = yield iterator.nextBatch(5); // TODO: adjust as necessary
|
||||||
var lastModifiedTime = file.lastModifiedTime;
|
if (!entries.length) break;
|
||||||
|
for (let i = 0; i < entries.length; i++) {
|
||||||
var dbCacheEntry = false;
|
let entry = entries[i];
|
||||||
if(dbCache[leafName]) {
|
let path = entry.path;
|
||||||
filesInCache[leafName] = true;
|
let fileName = entry.name;
|
||||||
if(dbCache[leafName].lastModifiedTime == lastModifiedTime) {
|
|
||||||
dbCacheEntry = dbCache[file.leafName];
|
if (!(/^[^.].*\.js$/.test(fileName))) continue;
|
||||||
}
|
|
||||||
}
|
let lastModifiedTime;
|
||||||
|
if ('winLastWriteDate' in entry) {
|
||||||
if(dbCacheEntry) {
|
lastModifiedTime = entry.winLastWriteDate.getTime();
|
||||||
// get JSON from cache if possible
|
}
|
||||||
var translator = Zotero.Translators.load(file, dbCacheEntry.translatorJSON, dbCacheEntry.code);
|
else {
|
||||||
filesInCache[leafName] = true;
|
lastModifiedTime = (yield OS.File.stat(path)).lastModificationDate.getTime();
|
||||||
} else {
|
}
|
||||||
// otherwise, load from file
|
let lastModified
|
||||||
var translator = yield Zotero.Translators.loadFromDisk(file);
|
|
||||||
}
|
var dbCacheEntry = false;
|
||||||
|
if (dbCache[fileName]) {
|
||||||
if(translator.translatorID) {
|
filesInCache[fileName] = true;
|
||||||
if(_translators[translator.translatorID]) {
|
if (dbCache[fileName].lastModifiedTime == lastModifiedTime) {
|
||||||
// same translator is already cached
|
dbCacheEntry = dbCache[fileName];
|
||||||
translator.logError('Translator with ID '+
|
|
||||||
translator.translatorID+' already loaded from "'+
|
|
||||||
_translators[translator.translatorID].file.leafName+'"');
|
|
||||||
} else {
|
|
||||||
// add to cache
|
|
||||||
_translators[translator.translatorID] = translator;
|
|
||||||
for(var type in TRANSLATOR_TYPES) {
|
|
||||||
if(translator.translatorType & TRANSLATOR_TYPES[type]) {
|
|
||||||
_cache[type].push(translator);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!dbCacheEntry) {
|
if(dbCacheEntry) {
|
||||||
var code = yield translator.getCode();
|
// get JSON from cache if possible
|
||||||
yield Zotero.Translators.cacheInDB(
|
var translator = Zotero.Translators.load(dbCacheEntry.metadataJSON, path);
|
||||||
leafName,
|
filesInCache[fileName] = true;
|
||||||
translator.serialize(TRANSLATOR_REQUIRED_PROPERTIES.
|
} else {
|
||||||
concat(TRANSLATOR_OPTIONAL_PROPERTIES)),
|
// otherwise, load from file
|
||||||
translator.cacheCode ? translator.code : null,
|
var translator = yield Zotero.Translators.loadFromFile(path);
|
||||||
lastModifiedTime
|
|
||||||
);
|
|
||||||
delete translator.metadataString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When can this happen?
|
||||||
|
if (!translator.translatorID) {
|
||||||
|
Zotero.debug("Translator ID for " + path + " not found");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_translators[translator.translatorID]) {
|
||||||
|
// same translator is already cached
|
||||||
|
translator.logError('Translator with ID '+
|
||||||
|
translator.translatorID+' already loaded from "'+
|
||||||
|
_translators[translator.translatorID].fileName + '"');
|
||||||
|
} else {
|
||||||
|
// add to cache
|
||||||
|
_translators[translator.translatorID] = translator;
|
||||||
|
for(var type in TRANSLATOR_TYPES) {
|
||||||
|
if(translator.translatorType & TRANSLATOR_TYPES[type]) {
|
||||||
|
_cache[type].push(translator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dbCacheEntry) {
|
||||||
|
yield Zotero.Translators.cacheInDB(
|
||||||
|
fileName,
|
||||||
|
translator.serialize(TRANSLATOR_REQUIRED_PROPERTIES.
|
||||||
|
concat(TRANSLATOR_OPTIONAL_PROPERTIES)),
|
||||||
|
lastModifiedTime
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
numCached++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
i++;
|
finally {
|
||||||
|
iterator.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove translators from DB as necessary
|
// Remove translators from DB as necessary
|
||||||
for(var leafName in dbCache) {
|
for (let fileName in dbCache) {
|
||||||
if(!filesInCache[leafName]) {
|
if (!filesInCache[fileName]) {
|
||||||
yield Zotero.DB.queryAsync(
|
yield Zotero.DB.queryAsync(
|
||||||
"DELETE FROM translatorCache WHERE leafName = ?", [leafName]
|
"DELETE FROM translatorCache WHERE fileName = ?", fileName
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,56 +161,55 @@ Zotero.Translators = new function() {
|
||||||
_cache[type].sort(cmp);
|
_cache[type].sort(cmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.debug("Cached "+i+" translators in "+((new Date()).getTime() - start)+" ms");
|
Zotero.debug("Cached " + numCached + " translators in " + ((new Date) - start) + " ms");
|
||||||
});
|
});
|
||||||
this.init = Zotero.lazy(this.reinit);
|
this.init = Zotero.lazy(this.reinit);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a translator from JSON, with optional code
|
* Loads a translator from JSON, with optional code
|
||||||
*/
|
*/
|
||||||
this.load = function(file, json, code) {
|
this.load = function (json, path, code) {
|
||||||
var info = JSON.parse(json);
|
var info = JSON.parse(json);
|
||||||
info.file = file;
|
info.path = path;
|
||||||
info.code = code;
|
info.code = code;
|
||||||
return new Zotero.Translator(info);
|
return new Zotero.Translator(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a translator from the disk
|
* Loads a translator from the disk
|
||||||
|
*
|
||||||
|
* @param {String} file - Path to translator file
|
||||||
*/
|
*/
|
||||||
this.loadFromDisk = function(file) {
|
this.loadFromFile = function(path) {
|
||||||
const infoRe = /^\s*{[\S\s]*?}\s*?[\r\n]/;
|
const infoRe = /^\s*{[\S\s]*?}\s*?[\r\n]/;
|
||||||
return Zotero.File.getContentsAsync(file)
|
return Zotero.File.getContentsAsync(path)
|
||||||
.then(function(source) {
|
.then(function(source) {
|
||||||
return Zotero.Translators.load(file, infoRe.exec(source)[0], source);
|
return Zotero.Translators.load(infoRe.exec(source)[0], path, source);
|
||||||
})
|
})
|
||||||
.catch(function() {
|
.catch(function() {
|
||||||
throw "Invalid or missing translator metadata JSON object in " + file.leafName;
|
throw "Invalid or missing translator metadata JSON object in " + OS.Path.basename(path);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the translator that corresponds to a given ID
|
* Gets the translator that corresponds to a given ID
|
||||||
|
*
|
||||||
* @param {String} id The ID of the translator
|
* @param {String} id The ID of the translator
|
||||||
* @param {Function} [callback] An optional callback to be executed when translators have been
|
|
||||||
* retrieved. If no callback is specified, translators are
|
|
||||||
* returned.
|
|
||||||
*/
|
*/
|
||||||
this.get = function(id) {
|
this.get = function(id) {
|
||||||
return this.init().then(function() {
|
if (!_initialized) {
|
||||||
return _translators[id] ? _translators[id] : false
|
throw new Zotero.Exception.UnloadedDataException("Translators not yet loaded", 'translators');
|
||||||
});
|
}
|
||||||
|
return _translators[id] ? _translators[id] : false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all translators for a specific type of translation
|
* Gets all translators for a specific type of translation
|
||||||
|
*
|
||||||
* @param {String} type The type of translators to get (import, export, web, or search)
|
* @param {String} type The type of translators to get (import, export, web, or search)
|
||||||
* @param {Function} [callback] An optional callback to be executed when translators have been
|
|
||||||
* retrieved. If no callback is specified, translators are
|
|
||||||
* returned.
|
|
||||||
*/
|
*/
|
||||||
this.getAllForType = function(type) {
|
this.getAllForType = function(type) {
|
||||||
return this.init().then(function() {
|
return this.init().then(function () {
|
||||||
return _cache[type].slice();
|
return _cache[type].slice();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -191,21 +218,16 @@ Zotero.Translators = new function() {
|
||||||
* Gets all translators for a specific type of translation
|
* Gets all translators for a specific type of translation
|
||||||
*/
|
*/
|
||||||
this.getAll = function() {
|
this.getAll = function() {
|
||||||
return this.init().then(function() {
|
return this.init().then(function () {
|
||||||
return [translator for each(translator in _translators)];
|
return Object.keys(_translators);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets web translators for a specific location
|
* Gets web translators for a specific location
|
||||||
* @param {String} uri The URI for which to look for translators
|
* @param {String} uri The URI for which to look for translators
|
||||||
* @param {Function} [callback] An optional callback to be executed when translators have been
|
|
||||||
* retrieved. If no callback is specified, translators are
|
|
||||||
* returned. The callback is passed a set of functions for
|
|
||||||
* converting URLs from proper to proxied forms as the second
|
|
||||||
* argument.
|
|
||||||
*/
|
*/
|
||||||
this.getWebTranslatorsForLocation = function(uri, callback) {
|
this.getWebTranslatorsForLocation = function(uri) {
|
||||||
return this.getAllForType("web").then(function(allTranslators) {
|
return this.getAllForType("web").then(function(allTranslators) {
|
||||||
var potentialTranslators = [];
|
var potentialTranslators = [];
|
||||||
|
|
||||||
|
@ -415,10 +437,10 @@ Zotero.Translators = new function() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cacheInDB = function(fileName, metadataJSON, code, lastModifiedTime) {
|
this.cacheInDB = function(fileName, metadataJSON, lastModifiedTime) {
|
||||||
return Zotero.DB.queryAsync(
|
return Zotero.DB.queryAsync(
|
||||||
"REPLACE INTO translatorCache VALUES (?, ?, ?, ?)",
|
"REPLACE INTO translatorCache VALUES (?, ?, ?)",
|
||||||
[fileName, metadataJSON, code, lastModifiedTime]
|
[fileName, JSON.stringify(metadataJSON), lastModifiedTime]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -485,7 +485,7 @@ Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||||
|
|
||||||
// Recreate database with no quick start guide
|
// Recreate database with no quick start guide
|
||||||
Zotero.Schema.skipDefaultData = true;
|
Zotero.Schema.skipDefaultData = true;
|
||||||
Zotero.Schema.updateSchema();
|
yield Zotero.Schema.updateSchema();
|
||||||
|
|
||||||
Zotero.restoreFromServer = true;
|
Zotero.restoreFromServer = true;
|
||||||
}
|
}
|
||||||
|
@ -577,7 +577,6 @@ Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||||
Zotero.locked = false;
|
Zotero.locked = false;
|
||||||
|
|
||||||
// Initialize various services
|
// Initialize various services
|
||||||
Zotero.Styles.preinit();
|
|
||||||
Zotero.Integration.init();
|
Zotero.Integration.init();
|
||||||
|
|
||||||
if(Zotero.Prefs.get("httpServer.enabled")) {
|
if(Zotero.Prefs.get("httpServer.enabled")) {
|
||||||
|
@ -605,8 +604,8 @@ Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||||
Zotero.Searches.init();
|
Zotero.Searches.init();
|
||||||
Zotero.Groups.init();
|
Zotero.Groups.init();
|
||||||
|
|
||||||
// TODO: Delay until after UI is shown
|
|
||||||
yield Zotero.QuickCopy.init();
|
yield Zotero.QuickCopy.init();
|
||||||
|
|
||||||
Zotero.Items.startEmptyTrashTimer();
|
Zotero.Items.startEmptyTrashTimer();
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
|
|
|
@ -411,8 +411,7 @@ CREATE INDEX customBaseFieldMappings_baseFieldID ON customBaseFieldMappings(base
|
||||||
CREATE INDEX customBaseFieldMappings_customFieldID ON customBaseFieldMappings(customFieldID);
|
CREATE INDEX customBaseFieldMappings_customFieldID ON customBaseFieldMappings(customFieldID);
|
||||||
|
|
||||||
CREATE TABLE translatorCache (
|
CREATE TABLE translatorCache (
|
||||||
leafName TEXT PRIMARY KEY,
|
fileName TEXT PRIMARY KEY,
|
||||||
translatorJSON TEXT,
|
metadataJSON TEXT,
|
||||||
code TEXT,
|
lastModifiedTime INT
|
||||||
lastModifiedTime INT
|
|
||||||
);
|
);
|
Loading…
Reference in a new issue