Integration megacommit, part 2: Zotero code
Closes #884, final period missing when a citation is first added in note styles Closes #1298, issues with footnotes and citations in OOo Closes #1069, Use async HTTP calls for integration requests Closes #1027, User-customizable integration port number Closes #698, Migration away from VBA Closes #1085, Migrate VBA plug-in to new XML-based API Closes #792, Auto-updating of OO plugins
This commit is contained in:
parent
6da149b22c
commit
136c47be5f
8 changed files with 2181 additions and 997 deletions
|
@ -106,15 +106,11 @@ var Zotero_File_Interface_Bibliography = new function() {
|
|||
styleChanged(selectIndex);
|
||||
}
|
||||
if(document.getElementById("formatUsing")) {
|
||||
if(_io.useBookmarks && _io.useBookmarks == 1) document.getElementById("formatUsing").selectedIndex = 1;
|
||||
if(_io.openOffice) {
|
||||
var formatOption = "referenceMarks";
|
||||
} else {
|
||||
var formatOption = "fields";
|
||||
}
|
||||
if(_io.fieldType == "Bookmarks") document.getElementById("formatUsing").selectedIndex = 1;
|
||||
Zotero.safeDebug(_io)
|
||||
var formatOption = (_io.primaryFieldType == "ReferenceMark" ? "referenceMarks" : "fields");
|
||||
document.getElementById("fields").label = Zotero.getString("integration."+formatOption+".label");
|
||||
document.getElementById("fields-caption").textContent = Zotero.getString("integration."+formatOption+".caption");
|
||||
document.getElementById("fields-caption").textContent = Zotero.getString("integration."+formatOption+".caption");
|
||||
document.getElementById("fields-file-format-notice").textContent = Zotero.getString("integration."+formatOption+".fileFormatNotice");
|
||||
document.getElementById("bookmarks-file-format-notice").textContent = Zotero.getString("integration.fields.fileFormatNotice");
|
||||
}
|
||||
|
@ -170,7 +166,7 @@ var Zotero_File_Interface_Bibliography = new function() {
|
|||
// ONLY FOR integrationDocPrefs.xul: collect displayAs
|
||||
if(document.getElementById("displayAs")) {
|
||||
_io.useEndnotes = document.getElementById("displayAs").selectedIndex;
|
||||
_io.useBookmarks = document.getElementById("formatUsing").selectedIndex;
|
||||
_io.fieldType = (document.getElementById("formatUsing").selectedIndex == 0 ? _io.primaryFieldType : _io.secondaryFieldType);
|
||||
}
|
||||
|
||||
// save style (this happens only for "Export Bibliography," or Word
|
||||
|
|
File diff suppressed because it is too large
Load diff
345
chrome/content/zotero/xpcom/integration_compat.js
Executable file
345
chrome/content/zotero/xpcom/integration_compat.js
Executable file
|
@ -0,0 +1,345 @@
|
|||
/*
|
||||
***** 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.Integration.Compat = new function() {
|
||||
var _contentLengthRe = /[\r\n]Content-Length: *([0-9]+)/i;
|
||||
var _XMLRe = /<\?[^>]+\?>/;
|
||||
var _onlineObserverRegistered;
|
||||
|
||||
this.sessions = {};
|
||||
|
||||
var ns = "http://www.zotero.org/namespaces/SOAP";
|
||||
this.ns = new Namespace(ns);
|
||||
|
||||
this.init = init;
|
||||
this.handleHeader = handleHeader;
|
||||
this.handleEnvelope = handleEnvelope;
|
||||
|
||||
/*
|
||||
* initializes a very rudimentary web server used for SOAP RPC
|
||||
*/
|
||||
function init() {
|
||||
this.env = new Namespace("http://schemas.xmlsoap.org/soap/envelope/");
|
||||
|
||||
if (Zotero.Utilities.HTTP.browserIsOffline()) {
|
||||
Zotero.debug('Browser is offline -- not initializing integration HTTP server');
|
||||
_registerOnlineObserver()
|
||||
return;
|
||||
}
|
||||
|
||||
// start listening on socket
|
||||
var serv = Components.classes["@mozilla.org/network/server-socket;1"]
|
||||
.createInstance(Components.interfaces.nsIServerSocket);
|
||||
try {
|
||||
// bind to a random port on loopback only
|
||||
serv.init(Zotero.Prefs.get('integration.port'), true, -1);
|
||||
serv.asyncListen(Zotero.Integration.Compat.SocketListener);
|
||||
|
||||
Zotero.debug("Integration HTTP server listening on 127.0.0.1:"+serv.port);
|
||||
} catch(e) {
|
||||
Zotero.debug("Not initializing integration HTTP server");
|
||||
}
|
||||
|
||||
_registerOnlineObserver()
|
||||
}
|
||||
|
||||
/*
|
||||
* handles an HTTP request
|
||||
*/
|
||||
function handleHeader(header) {
|
||||
// get first line of request (all we care about for now)
|
||||
var method = header.substr(0, header.indexOf(" "));
|
||||
|
||||
if(!method) {
|
||||
return _generateResponse("400 Bad Request");
|
||||
}
|
||||
|
||||
if(method != "POST") {
|
||||
return _generateResponse("501 Method Not Implemented");
|
||||
} else {
|
||||
// parse content length
|
||||
var m = _contentLengthRe.exec(header);
|
||||
if(!m) {
|
||||
return _generateResponse("400 Bad Request");
|
||||
} else {
|
||||
return parseInt(m[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* handles a SOAP envelope
|
||||
*/
|
||||
function handleEnvelope(envelope) {
|
||||
Zotero.debug("Integration: SOAP Request\n"+envelope);
|
||||
envelope = envelope.replace(_XMLRe, "");
|
||||
var env = this.env;
|
||||
|
||||
var xml = new XML(envelope);
|
||||
var request = xml.env::Body.children()[0];
|
||||
if(request.namespace() != this.ns) {
|
||||
Zotero.debug("Integration: SOAP method not supported: invalid namespace");
|
||||
} else if(!xml.env::Header.children().length()) {
|
||||
// old style SOAP request
|
||||
var name = request.localName();
|
||||
var output = "ERROR:"+Zotero.getString("integration.error.incompatibleVersion", Zotero.version);
|
||||
var response = <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<SOAP-ENV:Body>
|
||||
<m:{name}Response xmlns:m={this.ns}>
|
||||
<output>{output}</output>
|
||||
</m:{name}Response>
|
||||
</SOAP-ENV:Body>
|
||||
</SOAP-ENV:Envelope>;
|
||||
return _generateResponse("200 OK", 'text/xml; charset="UTF-8"', response.toXMLString());
|
||||
} else {
|
||||
// new style SOAP request
|
||||
var text = Zotero.getString("integration.error.incompatibleVersion", Zotero.version);
|
||||
var response = <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<SOAP-ENV:Body>
|
||||
<SOAP-ENV:Fault>
|
||||
<SOAP-ENV:Code>
|
||||
<SOAP-ENV:Value>XML-ENV:Sender</SOAP-ENV:Value>
|
||||
<SOAP-ENV:Subcode>z:incompatibleVersion</SOAP-ENV:Subcode>
|
||||
</SOAP-ENV:Code>
|
||||
</SOAP-ENV:Fault>
|
||||
<SOAP-ENV:Reason>
|
||||
<SOAP-ENV:Text>{text}</SOAP-ENV:Text>
|
||||
</SOAP-ENV:Reason>
|
||||
</SOAP-ENV:Body>
|
||||
</SOAP-ENV:Envelope>
|
||||
return _generateResponse("500 Internal Server Error", 'text/xml; charset="UTF-8"', response.toXMLString());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* generates the response to an HTTP request
|
||||
*/
|
||||
function _generateResponse(status, contentType, body) {
|
||||
var response = "HTTP/1.0 "+status+"\r\n";
|
||||
|
||||
if(body) {
|
||||
if(contentType) {
|
||||
response += "Content-Type: "+contentType+"\r\n";
|
||||
}
|
||||
response += "\r\n"+body;
|
||||
} else {
|
||||
response += "Content-Length: 0\r\n\r\n"
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
function _registerOnlineObserver() {
|
||||
if (_onlineObserverRegistered) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Observer to enable the integration when we go online
|
||||
var observer = {
|
||||
observe: function(subject, topic, data) {
|
||||
if (data == 'online') {
|
||||
Zotero.Integration.Compat.init();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var observerService =
|
||||
Components.classes["@mozilla.org/observer-service;1"]
|
||||
.getService(Components.interfaces.nsIObserverService);
|
||||
observerService.addObserver(observer, "network:offline-status-changed", false);
|
||||
|
||||
_onlineObserverRegistered = true;
|
||||
}
|
||||
}
|
||||
|
||||
Zotero.Integration.Compat.SocketListener = new function() {
|
||||
this.onSocketAccepted = onSocketAccepted;
|
||||
this.onStopListening = onStopListening;
|
||||
|
||||
/*
|
||||
* called when a socket is opened
|
||||
*/
|
||||
function onSocketAccepted(socket, transport) {
|
||||
// get an input stream
|
||||
var iStream = transport.openInputStream(0, 0, 0);
|
||||
var oStream = transport.openOutputStream(Components.interfaces.nsITransport.OPEN_BLOCKING, 0, 0);
|
||||
|
||||
var dataListener = new Zotero.Integration.Compat.DataListener(iStream, oStream);
|
||||
var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"]
|
||||
.createInstance(Components.interfaces.nsIInputStreamPump);
|
||||
pump.init(iStream, -1, -1, 0, 0, false);
|
||||
pump.asyncRead(dataListener, null);
|
||||
}
|
||||
|
||||
function onStopListening(serverSocket, status) {
|
||||
Zotero.debug("Integration HTTP server going offline");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* handles the actual acquisition of data
|
||||
*/
|
||||
Zotero.Integration.Compat.DataListener = function(iStream, oStream) {
|
||||
this.header = "";
|
||||
this.headerFinished = false;
|
||||
|
||||
this.body = "";
|
||||
this.bodyLength = 0;
|
||||
|
||||
this.iStream = iStream;
|
||||
this.oStream = oStream;
|
||||
this.sStream = Components.classes["@mozilla.org/scriptableinputstream;1"]
|
||||
.createInstance(Components.interfaces.nsIScriptableInputStream);
|
||||
this.sStream.init(iStream);
|
||||
|
||||
this.foundReturn = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* called when a request begins (although the request should have begun before
|
||||
* the DataListener was generated)
|
||||
*/
|
||||
Zotero.Integration.Compat.DataListener.prototype.onStartRequest = function(request, context) {}
|
||||
|
||||
/*
|
||||
* called when a request stops
|
||||
*/
|
||||
Zotero.Integration.Compat.DataListener.prototype.onStopRequest = function(request, context, status) {
|
||||
this.iStream.close();
|
||||
this.oStream.close();
|
||||
}
|
||||
|
||||
/*
|
||||
* called when new data is available
|
||||
*/
|
||||
Zotero.Integration.Compat.DataListener.prototype.onDataAvailable = function(request, context,
|
||||
inputStream, offset, count) {
|
||||
var readData = this.sStream.read(count);
|
||||
|
||||
if(this.headerFinished) { // reading body
|
||||
this.body += readData;
|
||||
// check to see if data is done
|
||||
this._bodyData();
|
||||
} else { // reading header
|
||||
// see if there's a magic double return
|
||||
var lineBreakIndex = readData.indexOf("\r\n\r\n");
|
||||
if(lineBreakIndex != -1) {
|
||||
if(lineBreakIndex != 0) {
|
||||
this.header += readData.substr(0, lineBreakIndex+4);
|
||||
this.body = readData.substr(lineBreakIndex+4);
|
||||
}
|
||||
|
||||
this._headerFinished();
|
||||
return;
|
||||
}
|
||||
var lineBreakIndex = readData.indexOf("\n\n");
|
||||
if(lineBreakIndex != -1) {
|
||||
if(lineBreakIndex != 0) {
|
||||
this.header += readData.substr(0, lineBreakIndex+2);
|
||||
this.body = readData.substr(lineBreakIndex+2);
|
||||
}
|
||||
|
||||
this._headerFinished();
|
||||
return;
|
||||
}
|
||||
if(this.header && this.header[this.header.length-1] == "\n" &&
|
||||
(readData[0] == "\n" || readData[0] == "\r")) {
|
||||
if(readData.length > 1 && readData[1] == "\n") {
|
||||
this.header += readData.substr(0, 2);
|
||||
this.body = readData.substr(2);
|
||||
} else {
|
||||
this.header += readData[0];
|
||||
this.body = readData.substr(1);
|
||||
}
|
||||
|
||||
this._headerFinished();
|
||||
return;
|
||||
}
|
||||
this.header += readData;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* processes an HTTP header and decides what to do
|
||||
*/
|
||||
Zotero.Integration.Compat.DataListener.prototype._headerFinished = function() {
|
||||
this.headerFinished = true;
|
||||
var output = Zotero.Integration.Compat.handleHeader(this.header);
|
||||
|
||||
if(typeof(output) == "number") {
|
||||
this.bodyLength = output;
|
||||
// check to see if data is done
|
||||
this._bodyData();
|
||||
} else {
|
||||
this._requestFinished(output);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* checks to see if Content-Length bytes of body have been read and, if they
|
||||
* have, processes the body
|
||||
*/
|
||||
Zotero.Integration.Compat.DataListener.prototype._bodyData = function() {
|
||||
if(this.body.length >= this.bodyLength) {
|
||||
// convert to UTF-8
|
||||
var dataStream = Components.classes["@mozilla.org/io/string-input-stream;1"]
|
||||
.createInstance(Components.interfaces.nsIStringInputStream);
|
||||
dataStream.setData(this.body, this.bodyLength);
|
||||
|
||||
var utf8Stream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
|
||||
.createInstance(Components.interfaces.nsIConverterInputStream);
|
||||
utf8Stream.init(dataStream, "UTF-8", 4096, "?");
|
||||
|
||||
this.body = "";
|
||||
var string = {};
|
||||
while(utf8Stream.readString(this.bodyLength, string)) {
|
||||
this.body += string.value;
|
||||
}
|
||||
|
||||
// handle envelope
|
||||
var output = Zotero.Integration.Compat.handleEnvelope(this.body);
|
||||
this._requestFinished(output);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* returns HTTP data from a request
|
||||
*/
|
||||
Zotero.Integration.Compat.DataListener.prototype._requestFinished = function(response) {
|
||||
// close input stream
|
||||
this.iStream.close();
|
||||
|
||||
// open UTF-8 converter for output stream
|
||||
var intlStream = Components.classes["@mozilla.org/intl/converter-output-stream;1"]
|
||||
.createInstance(Components.interfaces.nsIConverterOutputStream);
|
||||
|
||||
// write
|
||||
try {
|
||||
intlStream.init(this.oStream, "UTF-8", 1024, "?".charCodeAt(0));
|
||||
|
||||
// write response
|
||||
intlStream.writeString(response);
|
||||
} finally {
|
||||
intlStream.close();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue