diff --git a/Zotero.dot b/Zotero.dot
new file mode 100644
index 000000000..39b32dcda
Binary files /dev/null and b/Zotero.dot differ
diff --git a/chrome/chromeFiles/content/scholar/xpcom/cite.js b/chrome/chromeFiles/content/scholar/xpcom/cite.js
index 799a69a43..8112043bd 100644
--- a/chrome/chromeFiles/content/scholar/xpcom/cite.js
+++ b/chrome/chromeFiles/content/scholar/xpcom/cite.js
@@ -202,7 +202,9 @@ CSL.prototype.createBibliography = function(items, format) {
// add line feeds
if(format == "HTML") {
var coins = Scholar.OpenURL.createContextObject(item, "1.0");
- string += '';
+ if(coins) {
+ string += '';
+ }
if(this._class == "note") {
output += "
"+string+"\r\n";
@@ -215,6 +217,8 @@ CSL.prototype.createBibliography = function(items, format) {
output += index+". ";
}
output += string+"\\\r\n\\\r\n";
+ } else if(format == "Integration") {
+ output += string+"\r\n\r\n";
}
}
@@ -760,6 +764,8 @@ CSL.prototype._escapeString = function(string, format) {
}
return newString;
+ } else if(format == "Integration") {
+ return string.replace(/\\/g, "\\\\");
} else {
return string;
}
@@ -820,7 +826,7 @@ CSL.prototype._formatString = function(element, string, format, dontEscape) {
if(style) {
string = ''+string+'';
}
- } else if(format == "RTF") {
+ } else if(format == "RTF" || format == "Integration") {
if(element["font-style"] && (element["font-style"] == "oblique" || element["font-style"] == "italic")) {
string = "\\i "+string+"\\i0 ";
}
diff --git a/chrome/chromeFiles/content/scholar/xpcom/integration.js b/chrome/chromeFiles/content/scholar/xpcom/integration.js
new file mode 100644
index 000000000..64447a7c5
--- /dev/null
+++ b/chrome/chromeFiles/content/scholar/xpcom/integration.js
@@ -0,0 +1,340 @@
+Scholar.Integration = new function() {
+ var _contentLengthRe = /[\r\n]Content-Length: *([0-9]+)/i;
+ var _XMLRe = /<\?[^>]+\?>/;
+ this.ns = "http://chnm.gmu.edu/firefoxscholar/soap";
+
+ this.init = init;
+ this.handleHeader = handleHeader;
+ this.handleEnvelope = handleEnvelope;
+
+ /*
+ * initializes a very rudimentary web server used for SOAP RPC
+ */
+ function init() {
+ // start listening on socket
+ var sock = Components.classes["@mozilla.org/network/server-socket;1"];
+ serv = sock.createInstance();
+ serv = serv.QueryInterface(Components.interfaces.nsIServerSocket);
+
+ // bind to a random port on loopback only
+ serv.init(50001, true, -1);
+ serv.asyncListen(Scholar.Integration.SocketListener);
+
+ Scholar.debug("Integration HTTP server listening on 127.0.0.1:"+serv.port);
+ }
+
+ /*
+ * registers a SOAP method
+ */
+ function registerSOAP(name, callback) {
+ _registeredSOAP[name] = callback;
+ }
+
+ /*
+ * handles an HTTP request
+ */
+ function handleHeader(header) {
+ // get first line of request (all we care about for now)
+ var spaceIndex = header.indexOf(" ");
+ var method = header.substr(0, spaceIndex);
+ var uri = header.substr(spaceIndex+1);
+
+ if(!method || !uri) {
+ 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) {
+ Scholar.debug("Got SOAP envelope");
+ Scholar.debug(envelope);
+ envelope = envelope.replace(_XMLRe, "");
+
+ var env = new Namespace("http://schemas.xmlsoap.org/soap/envelope/");
+ var xml = new XML(envelope);
+ var request = xml.env::Body.children()[0];
+ if(request.namespace() != this.ns) {
+ Scholar.debug("SOAP method not supported: invalid namespace");
+ } else {
+ var name = request.localName();
+ if(Scholar.Integration.SOAP[name]) {
+ if(request.input.length()) {
+ var input = request.input.toString();
+ var vars = new Array();
+ vars[0] = "";
+ var i = 0;
+
+ colonIndex = input.indexOf(":");
+ while(colonIndex != -1) {
+ Scholar.debug(input);
+ if(input[colonIndex+1] == ":") { // escaped
+ vars[i] += input.substr(0, colonIndex+1);
+ input = input.substr(colonIndex+2);
+ } else { // not escaped
+ vars[i] += input.substr(0, colonIndex);
+ i++;
+ vars[i] = "";
+ input = input.substr(colonIndex+1);
+ }
+ colonIndex = input.indexOf(":");
+ }
+ vars[i] += input;
+ } else {
+ var vars = null;
+ }
+
+ // execute request
+ var output = Scholar.Integration.SOAP[name](vars);
+
+ // ugh: we can't use real SOAP, since AppleScript VBA can't pass
+ // objects, so implode arrays
+ if(!output) {
+ output = "";
+ }
+
+ if(typeof(output) == "object") {
+ for(var i in output) {
+ if(typeof(output[i]) == "string") {
+ output[i] = output[i].replace(/:/g, "::");
+ }
+ }
+ output = output.join(":");
+ }
+
+ // create envelope
+ var responseEnvelope =
+
+
+
+
+
+ ;
+
+ // return OK
+ return _generateResponse("200 OK", 'text/xml; charset="utf-8"',
+ responseEnvelope.toXMLString());
+ } else {
+ Scholar.debug("SOAP method not supported");
+ }
+ }
+ }
+
+ /*
+ * 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 += "Content-Length: "+body.length+"\r\n\r\n"+body;
+ } else {
+ response += "Content-Length: 0\r\n\r\n"
+ }
+
+ return response;
+ }
+}
+
+Scholar.Integration.SocketListener = new function() {
+ this.onSocketAccepted = onSocketAccepted;
+
+ /*
+ * 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(0, 0, 0);
+
+ var dataListener = new Scholar.Integration.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);
+ }
+}
+
+/*
+ * handles the actual acquisition of data
+ */
+Scholar.Integration.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)
+ */
+Scholar.Integration.DataListener.prototype.onStartRequest = function(request, context) {}
+
+/*
+ * called when a request stops
+ */
+Scholar.Integration.DataListener.prototype.onStopRequest = function(request, context, status) {
+ this.iStream.close();
+ this.oStream.close();
+}
+
+/*
+ * called when new data is available
+ */
+Scholar.Integration.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
+ */
+Scholar.Integration.DataListener.prototype._headerFinished = function() {
+ this.headerFinished = true;
+ var output = Scholar.Integration.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
+ */
+Scholar.Integration.DataListener.prototype._bodyData = function() {
+ if(this.body.length >= this.bodyLength) {
+ if(this.body.length > this.bodyLength) {
+ // truncate
+ this.body = this.body.substr(0, this.bodyLength);
+ }
+
+ var output = Scholar.Integration.handleEnvelope(this.body);
+ this._requestFinished(output);
+ }
+}
+
+/*
+ * returns HTTP data from a request
+ */
+Scholar.Integration.DataListener.prototype._requestFinished = function(response) {
+ // close input stream
+ this.iStream.close();
+
+ // write response
+ this.oStream.write(response, response.length);
+
+ // close output stream
+ this.oStream.close();
+}
+
+Scholar.Integration.SOAP = new function() {
+ this.getCitation = getCitation;
+ this.getBibliography = getBibliography;
+
+ function getCitation(parameters) {
+ var myWindow = Components.classes["@mozilla.org/appshell/appShellService;1"]
+ .getService(Components.interfaces.nsIAppShellService)
+ .hiddenDOMWindow;
+
+ var io = {dataIn: null, dataOut: null};
+ myWindow.openDialog('chrome://scholar/content/selectItemsDialog.xul','',
+ 'chrome,popup,modal,centerscreen,titlebar=no',io);
+
+ if(io.dataOut && io.dataOut[0]) {
+ var item = Scholar.Items.get(io.dataOut[0]);
+ var creator = item.getCreator(0);
+ return [io.dataOut[0], "{{"+creator.lastName+"}}"]
+ }
+ }
+
+ function getBibliography(vars) {
+ // get items
+ var itemIDs = vars[1].split(",");
+ var items = Scholar.Items.get(itemIDs);
+
+ return Scholar.Cite.getBibliography(vars[0], items, "Integration");
+ }
+}
+
+/*Scholar.Integration.registerURL("/", "text/plain", function(vars) {
+ return "Hi there! The HTTP server is working!";
+});
+Scholar.Integration.registerURL("/getBibliography", "text/html",
+ function(vars) { return Scholar.Integration.Cite.getBibliography(vars) });
+Scholar.Integration.registerURL("/getCitation", "text/html",
+ function(vars) { return Scholar.Integration.Cite.getCitation(vars) });*/
+
+Scholar.Integration.init();
\ No newline at end of file
diff --git a/components/chnmIScholarService.js b/components/chnmIScholarService.js
index bff51d75c..6d78e820a 100644
--- a/components/chnmIScholarService.js
+++ b/components/chnmIScholarService.js
@@ -58,6 +58,10 @@ Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader)
.loadSubScript("chrome://scholar/content/xpcom/utilities.js");
+Cc["@mozilla.org/moz/jssubscript-loader;1"]
+ .getService(Ci.mozIJSSubScriptLoader)
+ .loadSubScript("chrome://scholar/content/xpcom/integration.js");
+
Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader)
.loadSubScript("chrome://scholar/content/xpcom/file.js");