- implement Zotero.Styles.install, refine Zotero.Style.delete, and restore functionality to Styles prefpane

- allow deletion of multiple styles simultaneously
- split Zotero.Styles/Zotero.Style and Zotero.CSL into style.js and csl.js respectively
- add Zotero.File.getBinaryContents for binary-safe file reading
- add Zotero.MIMETypeHandler to provide a unified interface for registering observers and capturing MIME types with Zotero
This commit is contained in:
Simon Kornblith 2008-09-16 19:14:52 +00:00
parent 0769e0e1f8
commit bf8e4eae28
11 changed files with 852 additions and 655 deletions

View file

@ -1110,16 +1110,18 @@ function onOpenURLCustomized()
/**
* Refreshes the list of styles in the styles pane
**/
* @param {String} cslID Style to select
*/
function refreshStylesList(cslID) {
var treechildren = document.getElementById('styleManager-rows');
while (treechildren.hasChildNodes()) {
treechildren.removeChild(treechildren.firstChild);
}
var styles = Zotero.Styles.getAll();
var styles = Zotero.Styles.getVisible();
var selectIndex = false;
var i = 0;
for each(var style in styles) {
var treeitem = document.createElement('treeitem');
var treerow = document.createElement('treerow');
@ -1138,8 +1140,7 @@ function refreshStylesList(cslID) {
titleCell.setAttribute('label', style.title);
updatedCell.setAttribute('label', updatedDate);
// if not EN
if (style.styleID.length < Zotero.ENConverter.uriPrefix.length ||
style.styleID.substr(0, Zotero.ENConverter.uriPrefix.length) != Zotero.ENConverter.uriPrefix) {
if(style.type == "csl") {
cslCell.setAttribute('src', 'chrome://zotero/skin/tick.png');
}
@ -1152,6 +1153,7 @@ function refreshStylesList(cslID) {
if (cslID == style.styleID) {
document.getElementById('styleManager').view.selection.select(i);
}
i++;
}
}
@ -1169,70 +1171,52 @@ function addStyle() {
var rv = fp.show();
if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
var file = fp.file;
// read file
var iStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
.createInstance(Components.interfaces.nsIFileInputStream);
iStream.init(file, 0x01, 0664, 0);
var bStream = Components.classes["@mozilla.org/binaryinputstream;1"]
.createInstance(Components.interfaces.nsIBinaryInputStream);
bStream.setInputStream(iStream);
var read = bStream.readBytes(6);
if(read == "\x00\x08\xFF\x00\x00\x00") {
// EndNote style
// read the rest of the bytes in the file
read += bStream.readBytes(file.fileSize-6);
// get name and modification date
var name = file.leafName.replace(/\.ens$/i, "");
var date = new Date(file.lastModifiedTime);
var cslID = Zotero.Styles.install(read, false, date, name);
} else {
// This _should_ get the right charset for us automatically
var fileURI = Components.classes["@mozilla.org/network/protocol;1?name=file"]
.getService(Components.interfaces.nsIFileProtocolHandler)
.getURLSpecFromFile(file);
var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].
createInstance();
req.open("GET", fileURI, false);
req.overrideMimeType("text/plain");
try {
req.send(null);
} catch(e) {
styleImportError();
throw e;
}
var cslID = Zotero.Styles.install(req.responseText);
}
Zotero.Styles.install(fp.file);
}
if(cslID !== false) this.refreshStylesList(cslID);
}
/**
* Deletes a style from the style pane
* Deletes selected styles from the styles pane
**/
function deleteStyle() {
// get selected cslIDs
var tree = document.getElementById('styleManager');
if(tree.currentIndex == -1) return;
var treeitem = tree.lastChild.childNodes[tree.currentIndex];
var cslID = treeitem.getAttribute('id').substr(11);
var treeItems = tree.lastChild.childNodes;
var cslIDs = [];
var start = {};
var end = {};
var nRanges = tree.view.selection.getRangeCount();
for(var i=0; i<nRanges; i++) {
tree.view.selection.getRangeAt(i, start, end);
for(var j=start.value; j<=end.value; j++) {
cslIDs.push(treeItems[j].getAttribute('id').substr(11));
}
}
if(cslIDs.length == 0) {
return;
} else if(cslIDs.length == 1) {
var selectedStyle = Zotero.Styles.get(cslIDs[0])
var text = Zotero.getString('styles.deleteStyle', selectedStyle.title);
} else {
var text = Zotero.getString('styles.deleteStyles');
}
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
var text = Zotero.getString('styles.deleteStyle', [title]);
if(ps.confirm(null, '', text)) {
Zotero.Styles.get(cslID).delete();
// delete if requested
if(cslIDs.length == 1) {
selectedStyle.delete();
} else {
for(var i=0; i<cslIDs.length; i++) {
Zotero.Styles.get(cslIDs[i]).delete();
}
}
this.refreshStylesList();
document.getElementById('styleManager-delete').disabled = true;
}
document.getElementById('styleManager-delete').disabled = true;
}
/**
@ -1242,6 +1226,8 @@ function styleImportError() {
alert(Zotero.getString('styles.installError', "This"));
}
/**PROXIES**/
/**
* Adds a proxy to the proxy pane
*/

View file

@ -20,342 +20,6 @@
***** END LICENSE BLOCK *****
*/
/**
* @property {Boolean} cacheTranslatorData Whether translator data should be cached or reloaded
* every time a translator is accessed
* @property {Zotero.CSL} lastCSL
*/
Zotero.Styles = new function() {
var _initialized = false;
var _styles, _visibleStyles;
this.ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
/**
* Initializes styles cache, loading metadata for styles into memory
*/
this.init = function() {
_initialized = true;
var start = (new Date()).getTime()
_styles = {};
_visibleStyles = [];
this.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
*/
function _readStylesFromDirectory(dir, hidden) {
var i = 0;
var contents = dir.directoryEntries;
while(contents.hasMoreElements()) {
var file = contents.getNext().QueryInterface(Components.interfaces.nsIFile);
if(!file.leafName || file.leafName[0] == "." || file.isDirectory()) continue;
var style = new Zotero.Style(file);
if(style.styleID) {
if(_styles[style.styleID]) {
// same style is already cached
Zotero.log('Style with ID '+style.styleID+' already loaded from "'+
_styles[style.styleID].file.leafName+'"', "error",
Zotero.Styles.ios.newFileURI(style.file).spec);
} else {
// add to cache
_styles[style.styleID] = style;
_styles[style.styleID].hidden = hidden;
if(!hidden) _visibleStyles.push(style);
}
}
i++;
}
return i;
}
/**
* Gets a style with a given ID
* @param {String} id
*/
this.get = function(id) {
if(!_initialized) this.init();
return _styles[id] ? _styles[id] : false;
}
/**
* Gets all visible styles
*/
this.getVisible = function() {
if(!_initialized || !this.cacheTranslatorData) this.init();
return _visibleStyles.slice(0);
}
/**
* Gets all styles
*/
this.getAll = function() {
if(!_initialized || !this.cacheTranslatorData) this.init();
return _styles;
}
}
/**
* @class Represents a style file and its metadata
* @property {String} styleID
* @property {String} type "csl" for CSL styles, "ens" for legacy styles
* @property {String} title
* @property {String} updated SQL-style date updated
* @property {String} class "in-text" or "note"
* @property {String} source The CSL that contains the formatting information for this one, or null
* if this CSL contains formatting information
* @property {Zotero.CSL} csl The Zotero.CSL object used to format using this style
* @property {Boolean} hidden True if this style is hidden in style selection dialogs, false if it
* is not
*/
Zotero.Style = function(file) {
this.file = file;
var extension = file.leafName.substr(-4).toLowerCase();
if(extension == ".ens") {
this.type = "ens";
this.styleID = Zotero.Styles.ios.newFileURI(this.file).spec;
this.title = file.leafName.substr(0, file.leafName.length-4);
this.updated = Zotero.Date.dateToSQL(new Date(file.lastModifiedTime));
} else if(extension == ".csl") {
// "with ({});" needed to fix default namespace scope issue
// See https://bugzilla.mozilla.org/show_bug.cgi?id=330572
default xml namespace = "http://purl.org/net/xbiblio/csl"; with ({});
this.type = "csl";
var xml = Zotero.CSL.Global.cleanXML(Zotero.File.getContents(file));
try {
xml = new XML(xml);
} catch(e) {
Zotero.log(e.toString(), "error",
Zotero.Styles.ios.newFileURI(this.file).spec, xml.split(/\r?\n/)[e.lineNumber-1],
e.lineNumber);
return;
}
this.styleID = xml.info.id.toString();
this.title = xml.info.title.toString();
this.updated = xml.info.updated.toString().replace(/(.+)T([^\+]+)\+?.*/, "$1 $2");
this._class = xml.@class.toString();
this.source = null;
for each(var link in xml.info.link) {
if(link.@rel == "source") {
this.source = link.@href.toString();
}
}
}
}
Zotero.Style.prototype.__defineGetter__("csl",
/**
* Retrieves the Zotero.CSL object for this style
* @type Zotero.CSL
*/
function() {
// cache last style
if(Zotero.Styles.cacheTranslatorData && Zotero.Styles.lastCSL &&
Zotero.Styles.lastCSL.styleID == this.styleID) {
return Zotero.Styles.lastCSL;
}
if(this.type == "ens") {
// EN style
var iStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
.createInstance(Components.interfaces.nsIFileInputStream);
iStream.init(this.file, 0x01, 0664, 0);
var bStream = Components.classes["@mozilla.org/binaryinputstream;1"]
.createInstance(Components.interfaces.nsIBinaryInputStream);
bStream.setInputStream(iStream);
var string = bStream.readBytes(this.file.fileSize);
iStream.close();
var enConverter = new Zotero.ENConverter(string, null, this.title);
var xml = enConverter.parse();
} else {
// "with ({});" needed to fix default namespace scope issue
// See https://bugzilla.mozilla.org/show_bug.cgi?id=330572
default xml namespace = "http://purl.org/net/xbiblio/csl"; with ({});
if(this.source) {
// parent/child
var formatCSL = Zotero.Styles.get(this.source);
if(!formatCSL) {
throw(new Error('Style references '+this.source+', but this style is not installed',
Zotero.Styles.ios.newFileURI(this.file).spec, null));
}
var file = formatCSL.file;
} else {
var file = this.file;
}
var cslString = Zotero.File.getContents(file);
var xml = new XML(Zotero.CSL.Global.cleanXML(cslString));
}
return (Zotero.Styles.lastCSL = new Zotero.CSL(xml));
});
Zotero.Style.prototype.__defineGetter__("class",
/**
* Retrieves the style class, either from the metadata that's already loaded or by loading the file
* @type String
*/
function() {
if(this._class) return this._class;
return (this._class = this.csl.class);
});
/**
* Deletes a style
*/
Zotero.Style.prototype.delete = function() {
this.file.remove(false);
Zotero.Styles.init();
}
Zotero.Styles.MIMEHandler = new function () {
this.init = init;
/*
* registers URIContentListener to handle MIME types
*/
function init() {
Zotero.debug("Registering URIContentListener for text/x-csl");
var uriLoader = Components.classes["@mozilla.org/uriloader;1"]
.getService(Components.interfaces.nsIURILoader);
uriLoader.registerContentListener(Zotero.Styles.MIMEHandler.URIContentListener);
}
}
/*
* Zotero.Styles.MIMEHandler.URIContentListener: implements
* nsIURIContentListener interface to grab MIME types
*/
Zotero.Styles.MIMEHandler.URIContentListener = new function() {
// list of content types to capture
// NOTE: must be from shortest to longest length
this.desiredContentTypes = ["text/x-csl"];
this.QueryInterface = QueryInterface;
this.canHandleContent = canHandleContent;
this.doContent = doContent;
this.isPreferred = isPreferred;
this.onStartURIOpen = onStartURIOpen;
function QueryInterface(iid) {
if (iid.equals(Components.interfaces.nsISupports)
|| iid.equals(Components.interfaces.nsISupportsWeakReference)
|| iid.equals(Components.interfaces.nsIURIContentListener)) {
return this;
}
throw Components.results.NS_ERROR_NO_INTERFACE;
}
function canHandleContent(contentType, isContentPreferred, desiredContentType) {
if (this.desiredContentTypes.indexOf(contentType) != -1) {
return true;
}
return false;
}
function doContent(contentType, isContentPreferred, request, contentHandler) {
Zotero.debug("Running doContent() for " + request.name);
contentHandler.value = new Zotero.Styles.MIMEHandler.StreamListener(request, contentType);
return false;
}
function isPreferred(contentType, desiredContentType) {
if (this.desiredContentTypes.indexOf(contentType) != -1) {
return true;
}
return false;
}
function onStartURIOpen(URI) {
return true;
}
}
/*
* Zotero.Styles.MIMEHandler.StreamListener: implements nsIStreamListener and
* nsIRequestObserver interfaces to download MIME types we've grabbed
*/
Zotero.Styles.MIMEHandler.StreamListener = function(request, contentType) {
this._request = request;
this._contentType = contentType
this._readString = "";
this._scriptableStream = null;
this._scriptableStreamInput = null
Zotero.debug("Prepared to grab content type " + contentType);
}
Zotero.Styles.MIMEHandler.StreamListener.prototype.QueryInterface = function(iid) {
if (iid.equals(Components.interfaces.nsISupports)
|| iid.equals(Components.interfaces.nsIRequestObserver)
|| iid.equals(Components.interfaces.nsIStreamListener)) {
return this;
}
throw Components.results.NS_ERROR_NO_INTERFACE;
}
Zotero.Styles.MIMEHandler.StreamListener.prototype.onStartRequest = function(channel, context) {}
/*
* Called when there's data available; basically, we just want to collect this data
*/
Zotero.Styles.MIMEHandler.StreamListener.prototype.onDataAvailable = function(request, context, inputStream, offset, count) {
Zotero.debug(count + " bytes available");
if (inputStream != this._scriptableStreamInput) {
this._scriptableStream = Components.classes["@mozilla.org/scriptableinputstream;1"]
.createInstance(Components.interfaces.nsIScriptableInputStream);
this._scriptableStream.init(inputStream);
this._scriptableStreamInput = inputStream;
}
this._readString += this._scriptableStream.read(count);
}
/*
* Called when the request is done
*/
Zotero.Styles.MIMEHandler.StreamListener.prototype.onStopRequest = function(channel, context, status) {
Zotero.debug("Request finished");
var externalHelperAppService = Components.classes["@mozilla.org/uriloader/external-helper-app-service;1"]
.getService(Components.interfaces.nsIExternalHelperAppService);
if (this._request.name) {
var loadURI = this._request.name;
}
else {
var loadURI = '';
}
Zotero.Styles.install(this._readString, loadURI);
}
/*
* CSL: a class for creating bibliographies from CSL files
* this is abstracted as a separate class for the benefit of anyone who doesn't

View file

@ -78,6 +78,22 @@ Zotero.File = new function(){
}
/**
* Get contents of a binary file
*/
this.getBinaryContents = function(file) {
var iStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
.createInstance(Components.interfaces.nsIFileInputStream);
iStream.init(file, 0x01, 0664, 0);
var bStream = Components.classes["@mozilla.org/binaryinputstream;1"]
.createInstance(Components.interfaces.nsIBinaryInputStream);
bStream.setInputStream(iStream);
var string = bStream.readBytes(file.fileSize);
iStream.close();
return string;
}
function getContents(file, charset, maxLength){
var fis = Components.classes["@mozilla.org/network/file-input-stream;1"].
createInstance(Components.interfaces.nsIFileInputStream);

View file

@ -17,14 +17,40 @@
See the License for the specific language governing permissions and
limitations under the License.
Utilities based in part on code taken from Piggy Bank 2.1.1 (BSD-licensed)
***** END LICENSE BLOCK *****
*/
Zotero.Ingester = new Object();
Zotero.Ingester = new function() {
this.importHandler = function(string, uri) {
// attempt to import through Zotero.Translate
var translation = new Zotero.Translate("import");
translation.setLocation(uri);
translation.setString(string);
var frontWindow = Components.classes["@mozilla.org/embedcomp/window-watcher;1"].
getService(Components.interfaces.nsIWindowWatcher).activeWindow;
frontWindow.Zotero_Browser.progress.show();
var saveLocation = null;
try {
saveLocation = frontWindow.ZoteroPane.getSelectedCollection();
} catch(e) {}
translation.setHandler("itemDone", function(obj, item) { frontWindow.Zotero_Browser.itemDone(obj, item, saveLocation) });
translation.setHandler("done", function(obj, item) { frontWindow.Zotero_Browser.finishScraping(obj, item, saveLocation) });
// attempt to retrieve translators
var translators = translation.getTranslators();
if(!translators.length) {
// we lied. we can't really translate this file.
frontWindow.Zotero_Browser.progress.close();
throw "No translator found for handled RIS or Refer file"
}
// translate using first available
translation.setTranslator(translators[0]);
translation.translate();
}
}
Zotero.OpenURL = new function() {
this.resolve = resolve;
@ -457,176 +483,4 @@ Zotero.OpenURL = new function() {
return "";
}
}
}
Zotero.Ingester.MIMEHandler = new function() {
var on = false;
this.init = init;
/*
* registers URIContentListener to handle MIME types
*/
function init() {
var prefStatus = Zotero.Prefs.get("parseEndNoteMIMETypes");
if(!on && prefStatus) {
Zotero.debug("Registering URIContentListener for RIS/Refer");
var uriLoader = Components.classes["@mozilla.org/uriloader;1"].
getService(Components.interfaces.nsIURILoader);
uriLoader.registerContentListener(Zotero.Ingester.MIMEHandler.URIContentListener);
on = true;
} else if(on && !prefStatus) {
Zotero.debug("Unregistering URIContentListener for RIS/Refer");
var uriLoader = Components.classes["@mozilla.org/uriloader;1"].
getService(Components.interfaces.nsIURILoader);
uriLoader.unRegisterContentListener(Zotero.Ingester.MIMEHandler.URIContentListener);
on = false;
}
}
}
/*
* Zotero.Ingester.MIMEHandler.URIContentListener: implements
* nsIURIContentListener interface to grab MIME types
*/
Zotero.Ingester.MIMEHandler.URIContentListener = new function() {
// list of content types to capture
// NOTE: must be from shortest to longest length
this.desiredContentTypes = ["application/x-endnote-refer",
"application/x-research-info-systems"];
this.QueryInterface = QueryInterface;
this.canHandleContent = canHandleContent;
this.doContent = doContent;
this.isPreferred = isPreferred;
this.onStartURIOpen = onStartURIOpen;
function QueryInterface(iid) {
if(iid.equals(Components.interfaces.nsISupports)
|| iid.equals(Components.interfaces.nsISupportsWeakReference)
|| iid.equals(Components.interfaces.nsIURIContentListener)) {
return this;
}
throw Components.results.NS_ERROR_NO_INTERFACE;
}
function canHandleContent(contentType, isContentPreferred, desiredContentType) {
if(Zotero.inArray(contentType, this.desiredContentTypes)) {
return true;
}
return false;
}
function doContent(contentType, isContentPreferred, request, contentHandler) {
Zotero.debug("doing content for "+request.name);
contentHandler.value = new Zotero.Ingester.MIMEHandler.StreamListener(request, contentType);
return false;
}
function isPreferred(contentType, desiredContentType) {
if(Zotero.inArray(contentType, this.desiredContentTypes)) {
return true;
}
return false;
}
function onStartURIOpen(URI) {
return true;
}
}
/*
* Zotero.Ingester.MIMEHandler.StreamListener: implements nsIStreamListener and
* nsIRequestObserver interfaces to download MIME types we've grabbed
*/
Zotero.Ingester.MIMEHandler.StreamListener = function(request, contentType) {
this._request = request;
this._contentType = contentType
this._readString = "";
this._scriptableStream = null;
this._scriptableStreamInput = null
// get front window
var windowWatcher = Components.classes["@mozilla.org/embedcomp/window-watcher;1"].
getService(Components.interfaces.nsIWindowWatcher);
this._frontWindow = windowWatcher.activeWindow;
this._frontWindow.Zotero_Browser.progress.show();
Zotero.debug("EndNote prepared to grab content type "+contentType);
}
Zotero.Ingester.MIMEHandler.StreamListener.prototype.QueryInterface = function(iid) {
if(iid.equals(Components.interfaces.nsISupports)
|| iid.equals(Components.interfaces.nsIRequestObserver)
|| iid.equals(Components.interfaces.nsIStreamListener)) {
return this;
}
throw Components.results.NS_ERROR_NO_INTERFACE;
}
Zotero.Ingester.MIMEHandler.StreamListener.prototype.onStartRequest = function(channel, context) {}
/*
* called when there's data available; basicallly, we just want to collect this data
*/
Zotero.Ingester.MIMEHandler.StreamListener.prototype.onDataAvailable = function(request, context, inputStream, offset, count) {
Zotero.debug(count+" bytes available");
if(inputStream != this._scriptableStreamInput) { // get storage stream
// if there's not one
this._scriptableStream = Components.classes["@mozilla.org/scriptableinputstream;1"].
createInstance(Components.interfaces.nsIScriptableInputStream);
this._scriptableStream.init(inputStream);
this._scriptableStreamInput = inputStream;
}
this._readString += this._scriptableStream.read(count);
}
/*
* called when the request is done
*/
Zotero.Ingester.MIMEHandler.StreamListener.prototype.onStopRequest = function(channel, context, status) {
Zotero.debug("request finished");
var externalHelperAppService = Components.classes["@mozilla.org/uriloader/external-helper-app-service;1"].
getService(Components.interfaces.nsIExternalHelperAppService);
// attempt to import through Zotero.Translate
var translation = new Zotero.Translate("import");
translation.setLocation(this._request.name);
translation.setString(this._readString);
// use front window's save functions and folder
var frontWindow = this._frontWindow;
var saveLocation = null;
try {
saveLocation = frontWindow.ZoteroPane.getSelectedCollection();
} catch(e) {}
translation.setHandler("itemDone", function(obj, item) { frontWindow.Zotero_Browser.itemDone(obj, item, saveLocation) });
translation.setHandler("done", function(obj, item) { frontWindow.Zotero_Browser.finishScraping(obj, item, saveLocation) });
// attempt to retrieve translators
var translators = translation.getTranslators();
if(!translators.length) {
// we lied. we can't really translate this file. call
// nsIExternalHelperAppService with the data
frontWindow.Zotero_Browser.progress.close();
var streamListener;
if(streamListener = externalHelperAppService.doContent(this._contentType, this._request, frontWindow)) {
// create a string input stream
var inputStream = Components.classes["@mozilla.org/io/string-input-stream;1"].
createInstance(Components.interfaces.nsIStringInputStream);
inputStream.setData(this._readString, this._readString.length);
streamListener.onStartRequest(channel, context);
streamListener.onDataAvailable(this._request, context, inputStream, 0, this._readString.length);
streamListener.onStopRequest(channel, context, status);
}
return false;
}
// translate using first available
translation.setTranslator(translators[0]);
translation.translate();
}

View file

@ -0,0 +1,220 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright (c) 2006 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://chnm.gmu.edu
Licensed under the Educational Community License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.opensource.org/licenses/ecl1.php
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
***** END LICENSE BLOCK *****
*/
Zotero.MIMETypeHandler = new function () {
var _typeHandlers, _ignoreContentDispositionTypes, _observers;
/**
* Registers URIContentListener to handle MIME types
*/
this.init = function() {
Zotero.debug("Registering URIContentListener");
// register our nsIURIContentListener and nsIObserver
Components.classes["@mozilla.org/uriloader;1"].
getService(Components.interfaces.nsIURILoader).
registerContentListener(_URIContentListener);
Components.classes["@mozilla.org/observer-service;1"].
getService(Components.interfaces.nsIObserverService).
addObserver(_Observer, "http-on-examine-response", false);
this.initializeHandlers();
}
/**
* Initializes handlers for MIME types
*/
this.initializeHandlers = function() {
_typeHandlers = {};
_ignoreContentDispositionTypes = [];
_observers = [];
if(Zotero.Prefs.get("parseEndNoteMIMETypes")) {
this.addHandler("application/x-endnote-refer", Zotero.Ingester.importHandler, true);
this.addHandler("application/x-research-info-systems", Zotero.Ingester.importHandler, true);
}
this.addHandler("text/x-csl", function(a1, a2, a3) { Zotero.Styles.install(a1, a2, a3) });
}
/**
* Adds a handler to handle a specific MIME type
* @param {String} type MIME type to handle
* @param {Function} fn Function to call to handle type
* @param {Boolean} ignoreContentDisposition If true, ignores the Content-Disposition header,
* which is often used to force a file to download rather than let it be handled by the web
* browser
*/
this.addHandler = function(type, fn, ignoreContentDisposition) {
_typeHandlers[type] = fn;
_ignoreContentDispositionTypes.push(type);
}
/**
* Adds an observer to inspect and possibly modify page headers
*/
this.addObserver = function(fn) {
_observers.push(fn);
}
/**
* Called to observe a page load
*/
var _Observer = new function() {
this.observe = function(channel) {
channel.QueryInterface(Components.interfaces.nsIRequest);
if(channel.loadFlags & Components.interfaces.nsIHttpChannel.LOAD_DOCUMENT_URI) {
channel.QueryInterface(Components.interfaces.nsIHttpChannel);
try {
// remove content-disposition headers for EndNote, etc.
var contentType = channel.getResponseHeader("Content-Type").toLowerCase();
for each(var handledType in _ignoreContentDispositionTypes) {
if(contentType.length < handledType.length) {
break;
} else {
if(contentType.substr(0, handledType.length) == handledType) {
channel.setResponseHeader("Content-Disposition", "", false);
break;
}
}
}
} catch(e) {}
for each(var observer in _observers) {
observer(channel);
}
}
}
}
var _URIContentListener = new function() {
/**
* Standard QI definiton
*/
this.QueryInterface = function(iid) {
if (iid.equals(Components.interfaces.nsISupports)
|| iid.equals(Components.interfaces.nsISupportsWeakReference)
|| iid.equals(Components.interfaces.nsIURIContentListener)) {
return this;
}
throw Components.results.NS_ERROR_NO_INTERFACE;
}
/**
* Called to see if we can handle a content type
*/
this.canHandleContent = this.isPreferred = function(contentType, isContentPreferred, desiredContentType) {
return !!_typeHandlers[contentType.toLowerCase()];
}
/**
* Called to begin handling a content type
*/
this.doContent = function(contentType, isContentPreferred, request, contentHandler) {
Zotero.debug("MIMETypeHandler: handling "+contentType+" from " + request.name);
contentHandler.value = new _StreamListener(request, contentType.toLowerCase());
return false;
}
/**
* Called so that we could stop a load before it happened if we wanted to
*/
this.onStartURIOpen = function(URI) {
return true;
}
}
/**
* @class Implements nsIStreamListener and nsIRequestObserver interfaces to download MIME types
* we've registered ourself as the handler for
* @param {nsIRequest} request The request to handle
* @param {String} contenType The content type being handled
*/
var _StreamListener = function(request, contentType) {
this._request = request;
this._contentType = contentType
this._readString = "";
this._scriptableStream = null;
this._scriptableStreamInput = null;
}
/**
* Standard QI definiton
*/
_StreamListener.prototype.QueryInterface = function(iid) {
if (iid.equals(Components.interfaces.nsISupports)
|| iid.equals(Components.interfaces.nsIRequestObserver)
|| iid.equals(Components.interfaces.nsIStreamListener)) {
return this;
}
throw Components.results.NS_ERROR_NO_INTERFACE;
}
/**
* Called when the request is started; we ignore this
*/
_StreamListener.prototype.onStartRequest = function(channel, context) {}
/**
* Called when there's data available; we collect this data and keep it until the request is
* done
*/
_StreamListener.prototype.onDataAvailable = function(request, context, inputStream, offset, count) {
if (inputStream != this._scriptableStreamInput) {
this._scriptableStream = Components.classes["@mozilla.org/scriptableinputstream;1"]
.createInstance(Components.interfaces.nsIScriptableInputStream);
this._scriptableStream.init(inputStream);
this._scriptableStreamInput = inputStream;
}
this._readString += this._scriptableStream.read(count);
}
/**
* Called when the request is done
*/
_StreamListener.prototype.onStopRequest = function(channel, context, status) {
try {
_typeHandlers[this._contentType](this._readString, (this._request.name ? this._request.name : null),
this._contentType);
} catch(e) {
// if there was an error, handle using nsIExternalHelperAppService
var externalHelperAppService = Components.classes["@mozilla.org/uriloader/external-helper-app-service;1"].
getService(Components.interfaces.nsIExternalHelperAppService);
var frontWindow = Components.classes["@mozilla.org/embedcomp/window-watcher;1"].
getService(Components.interfaces.nsIWindowWatcher).activeWindow;
var newStreamListener = externalHelperAppService.doContent(this._contentType,
this._request, frontWindow, false);
if(newStreamListener) {
// create a string input stream
var inputStream = Components.classes["@mozilla.org/io/string-input-stream;1"].
createInstance(Components.interfaces.nsIStringInputStream);
inputStream.setData(this._readString, this._readString.length);
newStreamListener.onStartRequest(channel, context);
newStreamListener.onDataAvailable(this._request, context, inputStream, 0, this._readString.length);
newStreamListener.onStopRequest(channel, context, status);
}
// then throw our error
throw e;
}
}
}

View file

@ -44,12 +44,11 @@ Zotero.Proxies = new function() {
*/
this.init = function() {
if(!on) {
var observerService = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
observerService.addObserver(this, "http-on-examine-response", false);
var me = this;
Zotero.MIMETypeHandler.addObserver(function(ch) { me.observe(ch) });
this.get();
on = true;
}
on = true;
autoRecognize = Zotero.Prefs.get("proxies.autoRecognize");
transparent = Zotero.Prefs.get("proxies.transparent");
@ -63,94 +62,73 @@ Zotero.Proxies = new function() {
* @param {nsIChannel} channel
*/
this.observe = function(channel) {
channel.QueryInterface(Components.interfaces.nsIHttpChannel);
try {
// remove content-disposition headers for endnote, etc.
var contentType = channel.getResponseHeader("Content-Type").toLowerCase();
for each(var desiredContentType in Zotero.Ingester.MIMEHandler.URIContentListener.desiredContentTypes) {
if(contentType.length < desiredContentType.length) {
break;
} else {
if(contentType.substr(0, desiredContentType.length) == desiredContentType) {
channel.setResponseHeader("Content-Disposition", "", false);
break;
}
}
}
} catch(e) {}
// try to detect a proxy
channel.QueryInterface(Components.interfaces.nsIRequest);
if(channel.loadFlags & Components.interfaces.nsIHttpChannel.LOAD_DOCUMENT_URI) {
channel.QueryInterface(Components.interfaces.nsIHttpChannel);
var url = channel.URI.spec;
channel.QueryInterface(Components.interfaces.nsIHttpChannel);
var url = channel.URI.spec;
// see if there is a proxy we already know
var m = false;
var proxy;
for each(proxy in proxies) {
if(proxy.regexp && proxy.multiHost) {
m = proxy.regexp.exec(url);
if(m) break;
// see if there is a proxy we already know
var m = false;
var proxy;
for each(proxy in proxies) {
if(proxy.regexp && proxy.multiHost) {
m = proxy.regexp.exec(url);
if(m) break;
}
}
if(m) {
// add this host if we know a proxy
if(proxy.autoAssociate) {
var host = m[proxy.parameters.indexOf("%h")+1];
if(proxy.hosts.indexOf(host) == -1) {
proxy.hosts.push(host);
proxy.save();
}
}
if(m) {
// add this host if we know a proxy
if(proxy.autoAssociate) {
var host = m[proxy.parameters.indexOf("%h")+1];
if(proxy.hosts.indexOf(host) == -1) {
proxy.hosts.push(host);
proxy.save();
}
} else if(autoRecognize) {
// otherwise, try to detect a proxy
var proxy = false;
for each(var detector in Zotero.Proxies.Detectors) {
try {
proxy = detector(channel);
} catch(e) {
Components.utils.reportError(e);
}
} else if(autoRecognize) {
// otherwise, try to detect a proxy
var proxy = false;
for each(var detector in Zotero.Proxies.Detectors) {
try {
proxy = detector(channel);
} catch(e) {
Components.utils.reportError(e);
if(!transparent) {
// if transparent is turned off, just save the proxy
proxy.save();
} else if(proxy) {
// otherwise, make sure we want it
var io = {site:proxy.hosts[0], proxy:channel.URI.hostPort};
var window = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
window.openDialog('chrome://zotero/content/proxy.xul', '', 'chrome,modal', io);
if(io.add) proxy.save();
if(io.disable) {
transparent = false;
Zotero.Prefs.set("proxies.transparent", false);
}
if(!transparent) {
// if transparent is turned off, just save the proxy
proxy.save();
} else if(proxy) {
// otherwise, make sure we want it
var io = {site:proxy.hosts[0], proxy:channel.URI.hostPort};
var window = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
window.openDialog('chrome://zotero/content/proxy.xul', '', 'chrome,modal', io);
if(io.add) proxy.save();
if(io.disable) {
transparent = false;
Zotero.Prefs.set("proxies.transparent", false);
}
break;
}
}
}
// try to get an applicable proxy
if(transparent) {
var webNav = null;
try {
webNav = channel.notificationCallbacks.QueryInterface(Components.interfaces.nsIWebNavigation);
} catch(e) {}
if(webNav) {
var proxied = this.properToProxy(url, true);
if(proxied) webNav.loadURI(proxied, 0, channel.URI, null, null);
break;
}
}
}
delete channel;
// try to get an applicable proxy
if(transparent) {
var webNav = null;
try {
webNav = channel.notificationCallbacks.QueryInterface(Components.interfaces.nsIWebNavigation);
} catch(e) {}
if(webNav) {
var proxied = this.properToProxy(url, true);
if(proxied) webNav.loadURI(proxied, 0, channel.URI, null, null);
}
}
}
/**

View file

@ -0,0 +1,473 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright (c) 2006 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://chnm.gmu.edu
Licensed under the Educational Community License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.opensource.org/licenses/ecl1.php
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
***** END LICENSE BLOCK *****
*/
/**
* @property {Boolean} cacheTranslatorData Whether translator data should be cached or reloaded
* every time a translator is accessed
* @property {Zotero.CSL} lastCSL
*/
Zotero.Styles = new function() {
var _initialized = false;
var _styles, _visibleStyles;
this.ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
/**
* Initializes styles cache, loading metadata for styles into memory
*/
this.init = function() {
_initialized = true;
var start = (new Date()).getTime()
_styles = {};
_visibleStyles = [];
this.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
* @privates
*/
function _readStylesFromDirectory(dir, hidden) {
var i = 0;
var contents = dir.directoryEntries;
while(contents.hasMoreElements()) {
var file = contents.getNext().QueryInterface(Components.interfaces.nsIFile);
if(!file.leafName || file.leafName[0] == "." || file.isDirectory()) continue;
var style = new Zotero.Style(file);
if(style.styleID) {
if(_styles[style.styleID]) {
// same style is already cached
Zotero.log('Style with ID '+style.styleID+' already loaded from "'+
_styles[style.styleID].file.leafName+'"', "error",
Zotero.Styles.ios.newFileURI(style.file).spec);
} else {
// add to cache
_styles[style.styleID] = style;
_styles[style.styleID].hidden = hidden;
if(!hidden) _visibleStyles.push(style);
}
}
i++;
}
return i;
}
/**
* Gets a style with a given ID
* @param {String} id
*/
this.get = function(id) {
if(!_initialized) this.init();
return _styles[id] ? _styles[id] : false;
}
/**
* Gets all visible styles
* @return {Zotero.Style[]} An array of Zotero.Style objects
*/
this.getVisible = function() {
if(!_initialized || !this.cacheTranslatorData) this.init();
return _visibleStyles.slice(0);
}
/**
* Gets all styles
* @return {Object} An object whose keys are style IDs, and whose values are Zotero.Style objects
*/
this.getAll = function() {
if(!_initialized || !this.cacheTranslatorData) this.init();
return _styles;
}
/**
* Installs a style file
* @param {String|nsIFile} style An nsIFile representing a style on disk, or a string containing
* the style data
* @param {String} loadURI The URI this style file was loaded from
* @param {Boolean} hidden Whether style is to be hidden. If this parameter is true, UI alerts
* are silenced as well
*/
this.install = function(style, loadURI, hidden) {
// "with ({});" needed to fix default namespace scope issue
// See https://bugzilla.mozilla.org/show_bug.cgi?id=330572
default xml namespace = "http://purl.org/net/xbiblio/csl"; with ({});
const pathRe = /[^\/]+$/;
if(!_initialized || !this.cacheTranslatorData) this.init();
var type = "csl";
// handle nsIFiles
var styleFile = null;
if(style instanceof Components.interfaces.nsIFile) {
styleFile = style;
loadURI = style.leafName;
if(loadURI.substr(-4) == ".ens") {
type = "ens";
style = Zotero.File.getBinaryContents(styleFile);
} else {
style = Zotero.File.getContents(styleFile);
}
}
var error = false;
try {
if(type == "ens") {
// EN style
var type = "ens";
var enConverter = new Zotero.ENConverter(style);
var xml = enConverter.parse();
} else {
// CSL
var xml = new XML(Zotero.CSL.Global.cleanXML(style));
}
} catch(e) {
error = e;
}
if(!xml || error) {
if(!hidden) alert(Zotero.getString('styles.installErrorURI', loadURI));
if(error) throw error;
return false;
}
var source = null;
var styleID = xml.info.id.toString();
if(type == "ens") {
var title = styleFile.leafName.substr(0, styleFile.leafName.length-4);
var fileName = styleFile.leafName;
} else {
// get file name from URL
var m = pathRe.exec(styleID);
var fileName = Zotero.File.getValidFileName(m ? m[0] : styleID);
var title = xml.info.title.toString();
// look for a parent
for each(var link in xml.info.link) {
if(link.@rel == "source") {
source = link.@href.toString();
if(source == styleID) {
if(!hidden) alert(Zotero.getString('styles.installErrorURI', loadURI));
throw "Style with ID "+this.styleID+" references itself as source";
}
break;
}
}
}
// ensure csl or ens extension
if(fileName.substr(-4).toLowerCase() != "."+type) fileName += "."+type;
var 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 existingFile = null;
var existingTitle = null;
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 this._styles) {
if(destFile.equals(existingStyle.file)) {
existingTitle = existingStyle.title;
break;
}
}
}
}
// display a dialog to tell the user we're about to install the style
if(hidden) {
destFile = destFileHidden;
} else {
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
if(existingTitle) {
var text = Zotero.getString('styles.updateStyleURI', [existingTitle, title, loadURI]);
} else {
var text = Zotero.getString('styles.installStyleURI', [title, loadURI]);
}
var index = ps.confirmEx(null, '',
text,
((ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL)),
Zotero.getString('general.install'), null, null, null, {}
);
}
if(hidden || index == 0) {
// user wants to install/update
if(source && !_styles[source]) {
// need to fetch source
if(source.substr(0, 7) == "http://" || source.substr(0, 8) == "https://") {
Zotero.Utilities.HTTP.doGet(source, function(xmlhttp) {
var success = false;
var error = null;
try {
var success = Zotero.Styles.install(xmlhttp.responseText, loadURI, true);
} catch(e) {
error = e;
}
if(success) {
_completeInstall(style, styleID, destFile, existingFile, styleFile);
} else {
if(!hidden) alert(Zotero.getString('styles.installSourceErrorURI', [loadURI, source]));
throw error;
}
});
} else {
if(!hidden) alert(Zotero.getString('styles.installSourceErrorURI', [loadURI, source]));
throw "Source CSL URI is invalid";
}
} else {
_completeInstall(style, styleID, destFile, existingFile, styleFile);
}
return styleID;
}
return false;
}
/**
* Finishes installing a style, copying the file, reloading the style cache, and refreshing the
* styles list in any open windows
* @param {String} style The style string
* @param {String} styleID The style ID
* @param {nsIFile} destFile The destination for the style
* @param {nsIFile} [existingFile] The existing file to delete before copying this one
* @param {nsIFile} [styleFile] The file that contains the style to be installed
* @private
*/
function _completeInstall(style, styleID, destFile, existingFile, styleFile) {
// remove any existing file with a different name
if(existingFile) existingFile.remove(false);
if(styleFile) {
styleFile.copyToFollowingLinks(destFile.parent, destFile.leafName);
} else {
Zotero.File.putContents(destFile, style);
}
// recache
Zotero.Styles.init();
// 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();
win.refreshStylesList(styleID);
}
}
}
/**
* @class Represents a style file and its metadata
* @property {nsIFile} file The path to the style file
* @property {String} styleID
* @property {String} type "csl" for CSL styles, "ens" for legacy styles
* @property {String} title
* @property {String} updated SQL-style date updated
* @property {String} class "in-text" or "note"
* @property {String} source The CSL that contains the formatting information for this one, or null
* if this CSL contains formatting information
* @property {Zotero.CSL} csl The Zotero.CSL object used to format using this style
* @property {Boolean} hidden True if this style is hidden in style selection dialogs, false if it
* is not
*/
Zotero.Style = function(file) {
this.file = file;
var extension = file.leafName.substr(-4).toLowerCase();
if(extension == ".ens") {
this.type = "ens";
this.styleID = Zotero.Styles.ios.newFileURI(this.file).spec;
this.title = file.leafName.substr(0, file.leafName.length-4);
this.updated = Zotero.Date.dateToSQL(new Date(file.lastModifiedTime));
} else if(extension == ".csl") {
// "with ({});" needed to fix default namespace scope issue
// See https://bugzilla.mozilla.org/show_bug.cgi?id=330572
default xml namespace = "http://purl.org/net/xbiblio/csl"; with ({});
this.type = "csl";
var xml = Zotero.CSL.Global.cleanXML(Zotero.File.getContents(file));
try {
xml = new XML(xml);
} catch(e) {
Zotero.log(e.toString(), "error",
Zotero.Styles.ios.newFileURI(this.file).spec, xml.split(/\r?\n/)[e.lineNumber-1],
e.lineNumber);
return;
}
this.styleID = xml.info.id.toString();
this.title = xml.info.title.toString();
this.updated = xml.info.updated.toString().replace(/(.+)T([^\+]+)\+?.*/, "$1 $2");
this._class = xml.@class.toString();
this.source = null;
for each(var link in xml.info.link) {
if(link.@rel == "source") {
this.source = link.@href.toString();
if(this.source == this.styleID) {
throw "Style with ID "+this.styleID+" references itself as source";
this.source = null;
}
break;
}
}
}
}
Zotero.Style.prototype.__defineGetter__("csl",
/**
* Retrieves the Zotero.CSL object for this style
* @type Zotero.CSL
*/
function() {
// cache last style
if(Zotero.Styles.cacheTranslatorData && Zotero.Styles.lastCSL &&
Zotero.Styles.lastCSL.styleID == this.styleID) {
return Zotero.Styles.lastCSL;
}
if(this.type == "ens") {
// EN style
var string = Zotero.File.getBinaryContents(this.file);
var enConverter = new Zotero.ENConverter(string, null, this.title);
var xml = enConverter.parse();
} else {
// "with ({});" needed to fix default namespace scope issue
// See https://bugzilla.mozilla.org/show_bug.cgi?id=330572
default xml namespace = "http://purl.org/net/xbiblio/csl"; with ({});
if(this.source) {
// parent/child
var formatCSL = Zotero.Styles.get(this.source);
if(!formatCSL) {
throw(new Error('Style references '+this.source+', but this style is not installed',
Zotero.Styles.ios.newFileURI(this.file).spec, null));
}
var file = formatCSL.file;
} else {
var file = this.file;
}
var cslString = Zotero.File.getContents(file);
var xml = new XML(Zotero.CSL.Global.cleanXML(cslString));
}
return (Zotero.Styles.lastCSL = new Zotero.CSL(xml));
});
Zotero.Style.prototype.__defineGetter__("class",
/**
* Retrieves the style class, either from the metadata that's already loaded or by loading the file
* @type String
*/
function() {
if(this._class) return this._class;
return (this._class = this.csl.class);
});
/**
* Deletes a style
*/
Zotero.Style.prototype.delete = function() {
// make sure no styles depend on this one
var dependentStyles = false;
var styles = Zotero.Styles.getAll();
for each(var style in styles) {
if(style.source == this.styleID) {
dependentStyles = true;
break;
}
}
if(dependentStyles) {
// copy dependent styles to hidden directory
var hiddenDir = Zotero.getStylesDirectory();
hiddenDir.append("hidden");
Zotero.File.createDirectoryIfMissing(hiddenDir);
this.file.moveTo(hiddenDir, null);
} else {
// remove defunct files
this.file.remove(false);
}
// check to see if this style depended on a hidden one
if(this.source) {
var source = Zotero.Styles.get(this.source);
if(source && source.hidden) {
var deleteSource = true;
// check to see if any other styles depend on the hidden one
for each(var style in Zotero.Styles.getAll()) {
if(style.source == this.source && style.styleID != this.styleID) {
deleteSource = false;
break;
}
}
// if it was only this style with the dependency, delete the source
if(deleteSource) {
source.delete();
}
}
}
Zotero.Styles.init();
}

View file

@ -275,9 +275,8 @@ var Zotero = new function(){
Zotero.Sync.Runner.init();
Zotero.Sync.Storage.init();
Zotero.MIMETypeHandler.init();
Zotero.Proxies.init();
Zotero.Ingester.MIMEHandler.init();
Zotero.Styles.MIMEHandler.init();
this.initialized = true;
Zotero.debug("Initialized in "+((new Date()).getTime() - start)+" ms");

View file

@ -495,12 +495,12 @@ integration.deleteCitedItem.title = Are you sure you want to remove this referen
integration.deleteCitedItem.body = This reference is cited in the text of your document. Deleting it will remove all citations.
styles.installStyleURI = Install style "%1$S" from %2$S?
styles.installStyle = Install style "%1$S"?
styles.updateStyleURI = Update existing style "%1$S" with "%2$S" from %3$S?
styles.updateStyle = Update existing style "%1$S" with "%2$S"?
styles.installed = The style "%S" was installed successfully.
styles.installError = %S does not appear to be a valid CSL file.
styles.installErrorURI = %S does not appear to be a valid CSL file.
styles.installSourceErrorURI = %1$S references an invalid or non-existent CSL file at %2$S as its source.
styles.deleteStyle = Are you sure you want to delete the style "%1$S"?
styles.deleteStyles = Are you sure you want to delete the selected styles?
sync.storage.kbRemaining = %SKB remaining
sync.storage.none = None

View file

@ -164,6 +164,11 @@ grid row hbox:first-child
-moz-box-align: center;
}
#styleManager-csl
{
width: 36px;
}
#zotero-prefpane-keys textbox
{
margin-left: -1px;

View file

@ -18,8 +18,8 @@ var xpcomFiles = [
'zotero',
'annotate',
'attachments',
'cite',
'collectionTreeView',
'csl',
'dataServer',
'data_access',
'data/dataObjects',
@ -43,6 +43,7 @@ var xpcomFiles = [
'integration',
'itemTreeView',
'mime',
'mimeTypeHandler',
'notifier',
'progressWindow',
'proxy',
@ -50,6 +51,7 @@ var xpcomFiles = [
'report',
'schema',
'search',
'style',
'sync',
'storage',
'timeline',