- Move Zotero.Utilities.Internal and Zotero.Utilities.Translate to separate files
- IE compatibility for translation core
This commit is contained in:
parent
21254238e1
commit
4666ae972c
10 changed files with 761 additions and 710 deletions
|
@ -615,7 +615,7 @@ var Zotero_File_Interface = new function() {
|
|||
* Updates progress indicators based on current progress of translation
|
||||
*/
|
||||
this.updateProgress = function(translate) {
|
||||
Zotero.updateZoteroPaneProgressMeter(translate.progress);
|
||||
Zotero.updateZoteroPaneProgressMeter(translate.getProgress());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -64,10 +64,9 @@ Zotero.Translate.ItemSaver.prototype = {
|
|||
var url = 'users/%%USERID%%/items?key=%%APIKEY%%';
|
||||
var payload = JSON.stringify({"items":newItems}, null, "\t")
|
||||
|
||||
Zotero.OAuth.doAuthenticatedPost(url, payload, function(status, message) {
|
||||
Zotero.OAuth.doAuthenticatedPost(url, payload, function(status) {
|
||||
if(!status) {
|
||||
Zotero.debug("Translate: Save to server failed with message "+message+"; payload:\n\n"+payload);
|
||||
callback(false, new Error("Save to server failed with "+message));
|
||||
callback(false, new Error("Save to server failed"));
|
||||
} else {
|
||||
Zotero.debug("Translate: Save to server complete");
|
||||
callback(true, newItems);
|
||||
|
|
|
@ -50,7 +50,9 @@ Zotero.Date = new function(){
|
|||
/**
|
||||
* Load dateFormat bundle into _dateFormatsBundle
|
||||
*/
|
||||
function _loadDateFormatsBundle() {
|
||||
this.getMonths = function() {
|
||||
if(_months) return _months;
|
||||
|
||||
if(Zotero.isFx) {
|
||||
var src = 'chrome://global/locale/dateFormat.properties';
|
||||
var localeService = Components.classes['@mozilla.org/intl/nslocaleservice;1'].
|
||||
|
@ -74,15 +76,9 @@ Zotero.Date = new function(){
|
|||
"long":["January", "February", "March", "April", "May", "June", "July",
|
||||
"Auguest", "September", "October", "November", "December"]};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy getter for reading month strings from dateFormat.properties
|
||||
*/
|
||||
this.__defineGetter__("months", function() {
|
||||
if(!_months) _loadDateFormatsBundle();
|
||||
return _months;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an SQL date in the form '2006-06-13 11:03:05' into a JS Date object
|
||||
|
@ -346,7 +342,8 @@ Zotero.Date = new function(){
|
|||
'aug', 'sep', 'oct', 'nov', 'dec'];
|
||||
// If using a non-English bibliography locale, try those too
|
||||
if (Zotero.locale != 'en-US') {
|
||||
months = months.concat(Zotero.Date.months.short).concat(Zotero.Date.months.long);
|
||||
Zotero.Date.getMonths();
|
||||
months = months.concat(_months['short']).concat(_months['long']);
|
||||
for(var i in months) months[i] = months[i].toLowerCase();
|
||||
}
|
||||
|
||||
|
@ -425,7 +422,7 @@ Zotero.Date = new function(){
|
|||
string += date.part+" ";
|
||||
}
|
||||
|
||||
var months = Zotero.Date.months.long;
|
||||
var months = Zotero.Date.getMonths().long;
|
||||
if(date.month != undefined && months[date.month]) {
|
||||
// get short month strings from CSL interpreter
|
||||
string += months[date.month];
|
||||
|
|
|
@ -25,12 +25,12 @@
|
|||
|
||||
|
||||
Zotero.Debug = new function () {
|
||||
this.__defineGetter__('storing', function () { return _store; });
|
||||
this.__defineGetter__('enabled', function () { return _console || _store; });
|
||||
|
||||
var _console, _stackTrace, _store, _level, _time, _lastTime, _output = [];
|
||||
|
||||
this.init = function () {
|
||||
this.storing = _store;
|
||||
this.enabled = _console || _store;
|
||||
|
||||
_console = Zotero.Prefs.get('debug.log');
|
||||
_store = Zotero.Prefs.get('debug.store');
|
||||
if (_store) {
|
||||
|
@ -133,6 +133,9 @@ Zotero.Debug = new function () {
|
|||
this.clear();
|
||||
}
|
||||
_store = enable;
|
||||
|
||||
this.storing = _store;
|
||||
this.enabled = _console || _store;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1025,6 +1025,11 @@ Zotero.Translate.Base.prototype = {
|
|||
this._runHandler("done", returnValue);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the progress of the import operation, or null if progress cannot be determined
|
||||
*/
|
||||
"getProgress":function() { return null },
|
||||
|
||||
/**
|
||||
* Executed on translator completion, either automatically from a synchronous scraper or as
|
||||
* done() from an asynchronous scraper. Finishes things up and calls callback function(s).
|
||||
|
@ -1322,7 +1327,7 @@ Zotero.Translate.Base.prototype = {
|
|||
/**
|
||||
* No-op for preparing translation
|
||||
*/
|
||||
"_prepareTranslation":function() {},
|
||||
"_prepareTranslation":function() {}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1622,17 +1627,16 @@ Zotero.Translate.Import.prototype._prepareTranslation = function() {
|
|||
this.newCollections = [];
|
||||
}
|
||||
|
||||
Zotero.Translate.Import.prototype.__defineGetter__("progress",
|
||||
/**
|
||||
* Return the progress of the import operation, or null if progress cannot be determined
|
||||
*/
|
||||
function() {
|
||||
Zotero.Translate.Import.prototype.getProgress = function() {
|
||||
if(this._progress !== undefined) return this._progress;
|
||||
if(Zotero.Translate.IO.rdfDataModes.indexOf(this._mode) !== -1 || this._mode === "xml/e4x" || this._mode == "xml/dom" || !this._io) {
|
||||
return null;
|
||||
}
|
||||
return this._io.bytesRead/this._io.contentLength*100;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
|
@ -1686,9 +1690,20 @@ Zotero.Translate.Export.prototype.setDisplayOptions = function(displayOptions) {
|
|||
}
|
||||
|
||||
/**
|
||||
* @borrows Zotero.Translate.Import#complete
|
||||
* Overload {@link Zotero.Translate.Base#complete} to close file and set complete
|
||||
*/
|
||||
Zotero.Translate.Export.prototype.complete = Zotero.Translate.Import.prototype.complete;
|
||||
Zotero.Translate.Export.prototype.complete = function(returnValue, error) {
|
||||
if(this._io) {
|
||||
this._progress = null;
|
||||
this._io.close();
|
||||
if(this._io instanceof Zotero.Translate.IO.String) {
|
||||
this.string = this._io.string;
|
||||
}
|
||||
}
|
||||
|
||||
// call super
|
||||
Zotero.Translate.Base.prototype.complete.apply(this, [returnValue, error]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload {@link Zotero.Translate.Base#getTranslators} to return all translators immediately
|
||||
|
@ -1730,9 +1745,8 @@ Zotero.Translate.Export.prototype._prepareTranslation = function() {
|
|||
// this is currently hackish since we pass null callbacks to the init function (they have
|
||||
// callbacks to be consistent with import, but they are synchronous, so we ignore them)
|
||||
if(!this.location) {
|
||||
var io = this._io = new Zotero.Translate.IO.String(null, this.path ? this.path : "");
|
||||
io.init(this.translator[0].configOptions["dataMode"], function() {});
|
||||
this.__defineGetter__("string", function() { return io.string; });
|
||||
this._io = new Zotero.Translate.IO.String(null, this.path ? this.path : "");
|
||||
this._io.init(this.translator[0].configOptions["dataMode"], function() {});
|
||||
} else if(!Zotero.Translate.IO.Write) {
|
||||
throw new Error("Writing to files is not supported in this build of Zotero.");
|
||||
} else {
|
||||
|
@ -1745,17 +1759,16 @@ Zotero.Translate.Export.prototype._prepareTranslation = function() {
|
|||
this._sandboxManager.importObject(this._io);
|
||||
}
|
||||
|
||||
Zotero.Translate.Export.prototype.__defineGetter__("progress",
|
||||
/**
|
||||
* Return the progress of the import operation, or null if progress cannot be determined
|
||||
*/
|
||||
function() {
|
||||
Zotero.Translate.Export.prototype.getProgress = function() {
|
||||
if(this._progress !== undefined) return this._progress;
|
||||
if(!this._itemGetter) {
|
||||
return null;
|
||||
}
|
||||
return (1-this._itemGetter.numItemsRemaining/this._itemGetter.numItems)*100;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @class Search translation
|
||||
|
@ -1901,11 +1914,12 @@ Zotero.Translate.IO = {
|
|||
*/
|
||||
Zotero.Translate.IO.String = function(string, uri, mode) {
|
||||
if(string && typeof string === "string") {
|
||||
this._string = string;
|
||||
this.string = string;
|
||||
} else {
|
||||
this._string = "";
|
||||
this.string = "";
|
||||
}
|
||||
this._stringPointer = 0;
|
||||
this.contentLength = this.string.length;
|
||||
this.bytesRead = 0;
|
||||
this._uri = uri;
|
||||
}
|
||||
|
||||
|
@ -1923,9 +1937,9 @@ Zotero.Translate.IO.String.prototype = {
|
|||
this._dataStore = new Zotero.RDF.AJAW.RDFIndexedFormula();
|
||||
this.RDF = new Zotero.Translate.IO._RDFSandbox(this._dataStore);
|
||||
|
||||
if(this._string.length) {
|
||||
if(this.contentLength) {
|
||||
try {
|
||||
var xml = Zotero.Translate.IO.parseDOMXML(this._string);
|
||||
var xml = Zotero.Translate.IO.parseDOMXML(this.string);
|
||||
} catch(e) {
|
||||
this._xmlInvalid = true;
|
||||
throw e;
|
||||
|
@ -1941,68 +1955,69 @@ Zotero.Translate.IO.String.prototype = {
|
|||
"read":function(bytes) {
|
||||
// if we are reading in RDF data mode and no string is set, serialize current RDF to the
|
||||
// string
|
||||
if(Zotero.Translate.IO.rdfDataModes.indexOf(this._mode) !== -1 && this._string === "") {
|
||||
this._string = this.RDF.serialize();
|
||||
if(Zotero.Translate.IO.rdfDataModes.indexOf(this._mode) !== -1 && this.string === "") {
|
||||
this.string = this.RDF.serialize();
|
||||
}
|
||||
|
||||
// return false if string has been read
|
||||
if(this._stringPointer >= this._string.length) {
|
||||
if(this.bytesRead >= this.contentLength) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(bytes !== undefined) {
|
||||
if(this._stringPointer >= this._string.length) return false;
|
||||
var oldPointer = this._stringPointer;
|
||||
this._stringPointer += bytes;
|
||||
return this._string.substr(oldPointer, bytes);
|
||||
if(this.bytesRead >= this.contentLength) return false;
|
||||
var oldPointer = this.bytesRead;
|
||||
this.bytesRead += bytes;
|
||||
return this.string.substr(oldPointer, bytes);
|
||||
} else {
|
||||
// bytes not specified; read a line
|
||||
var oldPointer = this._stringPointer;
|
||||
var lfIndex = this._string.indexOf("\n", this._stringPointer);
|
||||
var oldPointer = this.bytesRead;
|
||||
var lfIndex = this.string.indexOf("\n", this.bytesRead);
|
||||
|
||||
if(lfIndex !== -1) {
|
||||
// in case we have a CRLF
|
||||
this._stringPointer = lfIndex+1;
|
||||
if(this._string.length > lfIndex && this._string[lfIndex-1] === "\r") {
|
||||
this.bytesRead = lfIndex+1;
|
||||
if(this.contentLength > lfIndex && this.string[lfIndex-1] === "\r") {
|
||||
lfIndex--;
|
||||
}
|
||||
return this._string.substr(oldPointer, lfIndex-oldPointer);
|
||||
return this.string.substr(oldPointer, lfIndex-oldPointer);
|
||||
}
|
||||
|
||||
if(!this._noCR) {
|
||||
var crIndex = this._string.indexOf("\r", this._stringPointer);
|
||||
var crIndex = this.string.indexOf("\r", this.bytesRead);
|
||||
if(crIndex === -1) {
|
||||
this._noCR = true;
|
||||
} else {
|
||||
this._stringPointer = crIndex+1;
|
||||
return this._string.substr(oldPointer, crIndex-oldPointer-1);
|
||||
this.bytesRead = crIndex+1;
|
||||
return this.string.substr(oldPointer, crIndex-oldPointer-1);
|
||||
}
|
||||
}
|
||||
|
||||
this._stringPointer = this._string.length;
|
||||
return this._string.substr(oldPointer);
|
||||
this.bytesRead = this.contentLength;
|
||||
return this.string.substr(oldPointer);
|
||||
}
|
||||
},
|
||||
|
||||
"write":function(data) {
|
||||
this._string += data;
|
||||
this.string += data;
|
||||
this.contentLength = this.string.length;
|
||||
},
|
||||
|
||||
"_getXML":function() {
|
||||
if(this._mode == "xml/dom") {
|
||||
try {
|
||||
return Zotero.Translate.IO.parseDOMXML(this._string);
|
||||
return Zotero.Translate.IO.parseDOMXML(this.string);
|
||||
} catch(e) {
|
||||
this._xmlInvalid = true;
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
return this._string.replace(/<\?xml[^>]+\?>/, "");
|
||||
return this.string.replace(/<\?xml[^>]+\?>/, "");
|
||||
}
|
||||
},
|
||||
|
||||
"init":function(newMode, callback) {
|
||||
this._stringPointer = 0;
|
||||
this.bytesRead = 0;
|
||||
this._noCR = undefined;
|
||||
|
||||
this._mode = newMode;
|
||||
|
@ -2018,26 +2033,6 @@ Zotero.Translate.IO.String.prototype = {
|
|||
|
||||
"close":function() {}
|
||||
}
|
||||
Zotero.Translate.IO.String.prototype.__defineGetter__("string",
|
||||
function() {
|
||||
if(Zotero.Translate.IO.rdfDataModes.indexOf(this._mode) !== -1) {
|
||||
return this.RDF.serialize();
|
||||
} else {
|
||||
return this._string;
|
||||
}
|
||||
});
|
||||
Zotero.Translate.IO.String.prototype.__defineSetter__("string",
|
||||
function(string) {
|
||||
this._string = string;
|
||||
});
|
||||
Zotero.Translate.IO.String.prototype.__defineGetter__("bytesRead",
|
||||
function() {
|
||||
return this._stringPointer;
|
||||
});
|
||||
Zotero.Translate.IO.String.prototype.__defineGetter__("contentLength",
|
||||
function() {
|
||||
return this._string.length;
|
||||
});
|
||||
|
||||
/****** RDF DATA MODE ******/
|
||||
|
||||
|
|
|
@ -1085,634 +1085,3 @@ Zotero.Utilities = {
|
|||
return newItem;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class All functions accessible from within Zotero.Utilities namespace inside sandboxed
|
||||
* translators
|
||||
*
|
||||
* @constructor
|
||||
* @augments Zotero.Utilities
|
||||
* @borrows Zotero.Date.formatDate as this.formatDate
|
||||
* @borrows Zotero.Date.strToDate as this.strToDate
|
||||
* @borrows Zotero.Date.strToISO as this.strToISO
|
||||
* @borrows Zotero.OpenURL.createContextObject as this.createContextObject
|
||||
* @borrows Zotero.OpenURL.parseContextObject as this.parseContextObject
|
||||
* @borrows Zotero.HTTP.processDocuments as this.processDocuments
|
||||
* @borrows Zotero.HTTP.doPost as this.doPost
|
||||
* @param {Zotero.Translate} translate
|
||||
*/
|
||||
Zotero.Utilities.Translate = function(translate) {
|
||||
this._translate = translate;
|
||||
}
|
||||
|
||||
var tmp = function() {};
|
||||
tmp.prototype = Zotero.Utilities;
|
||||
Zotero.Utilities.Translate.prototype = new tmp();
|
||||
Zotero.Utilities.Translate.prototype.formatDate = Zotero.Date.formatDate;
|
||||
Zotero.Utilities.Translate.prototype.strToDate = Zotero.Date.strToDate;
|
||||
Zotero.Utilities.Translate.prototype.strToISO = Zotero.Date.strToISO;
|
||||
Zotero.Utilities.Translate.prototype.createContextObject = Zotero.OpenURL.createContextObject;
|
||||
Zotero.Utilities.Translate.prototype.parseContextObject = Zotero.OpenURL.parseContextObject;
|
||||
|
||||
/**
|
||||
* Hack to overloads {@link Zotero.Utilities.capitalizeTitle} to allow overriding capitalizeTitles
|
||||
* pref on a per-translate instance basis (for translator testing only)
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.capitalizeTitle = function(string, force) {
|
||||
if(force === undefined) {
|
||||
var translate = this._translate;
|
||||
do {
|
||||
if(translate.capitalizeTitles !== undefined) {
|
||||
force = translate.capitalizeTitles;
|
||||
break;
|
||||
}
|
||||
} while(translate = translate._parentTranslator);
|
||||
}
|
||||
|
||||
return Zotero.Utilities.capitalizeTitle(string, force);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current Zotero version
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.getVersion = function() {
|
||||
return Zotero.version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an XPath query and returns the results
|
||||
*
|
||||
* @deprecated Use {@link Zotero.Utilities.xpath} or doc.evaluate() directly
|
||||
* @type Node[]
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.gatherElementsOnXPath = function(doc, parentNode, xpath, nsResolver) {
|
||||
var elmts = [];
|
||||
|
||||
var iterator = doc.evaluate(xpath, parentNode, nsResolver,
|
||||
(Zotero.isFx ? Components.interfaces.nsIDOMXPathResult.ANY_TYPE : XPathResult.ANY_TYPE),
|
||||
null);
|
||||
var elmt = iterator.iterateNext();
|
||||
var i = 0;
|
||||
while (elmt) {
|
||||
elmts[i++] = elmt;
|
||||
elmt = iterator.iterateNext();
|
||||
}
|
||||
return elmts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a given node as a string containing all child nodes
|
||||
*
|
||||
* @deprecated Use doc.evaluate and the "nodeValue" or "textContent" property
|
||||
* @type String
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.getNodeString = function(doc, contextNode, xpath, nsResolver) {
|
||||
var elmts = this.gatherElementsOnXPath(doc, contextNode, xpath, nsResolver);
|
||||
var returnVar = "";
|
||||
for(var i=0; i<elmts.length; i++) {
|
||||
returnVar += elmts[i].nodeValue;
|
||||
}
|
||||
return returnVar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grabs items based on URLs
|
||||
*
|
||||
* @param {Document} doc DOM document object
|
||||
* @param {Element|Element[]} inHere DOM element(s) to process
|
||||
* @param {RegExp} [urlRe] Regexp of URLs to add to list
|
||||
* @param {RegExp} [urlRe] Regexp of URLs to reject
|
||||
* @return {Object} Associative array of link => textContent pairs, suitable for passing to
|
||||
* Zotero.selectItems from within a translator
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.getItemArray = function(doc, inHere, urlRe, rejectRe) {
|
||||
var availableItems = new Object(); // Technically, associative arrays are objects
|
||||
|
||||
// Require link to match this
|
||||
if(urlRe) {
|
||||
if(urlRe.exec) {
|
||||
var urlRegexp = urlRe;
|
||||
} else {
|
||||
var urlRegexp = new RegExp();
|
||||
urlRegexp.compile(urlRe, "i");
|
||||
}
|
||||
}
|
||||
// Do not allow text to match this
|
||||
if(rejectRe) {
|
||||
if(rejectRe.exec) {
|
||||
var rejectRegexp = rejectRe;
|
||||
} else {
|
||||
var rejectRegexp = new RegExp();
|
||||
rejectRegexp.compile(rejectRe, "i");
|
||||
}
|
||||
}
|
||||
|
||||
if(!inHere.length) {
|
||||
inHere = new Array(inHere);
|
||||
}
|
||||
|
||||
for(var j=0; j<inHere.length; j++) {
|
||||
var links = inHere[j].getElementsByTagName("a");
|
||||
for(var i=0; i<links.length; i++) {
|
||||
if(!urlRe || urlRegexp.test(links[i].href)) {
|
||||
var text = links[i].textContent;
|
||||
if(text) {
|
||||
text = this.trimInternal(text);
|
||||
if(!rejectRe || !rejectRegexp.test(text)) {
|
||||
if(availableItems[links[i].href]) {
|
||||
if(text != availableItems[links[i].href]) {
|
||||
availableItems[links[i].href] += " "+text;
|
||||
}
|
||||
} else {
|
||||
availableItems[links[i].href] = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return availableItems;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load a single document in a hidden browser
|
||||
*
|
||||
* @deprecated Use processDocuments with a single URL
|
||||
* @see Zotero.Utilities.Translate#processDocuments
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.loadDocument = function(url, succeeded, failed) {
|
||||
Zotero.debug("Zotero.Utilities.loadDocument is deprecated; please use processDocuments in new code");
|
||||
this.processDocuments([url], succeeded, null, failed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Already documented in Zotero.HTTP
|
||||
* @ignore
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.processDocuments = function(urls, processor, done, exception) {
|
||||
if(typeof(urls) == "string") {
|
||||
urls = [this._convertURL(urls)];
|
||||
} else {
|
||||
for(var i in urls) {
|
||||
urls[i] = this._convertURL(urls[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Unless the translator has proposed some way to handle an error, handle it
|
||||
// by throwing a "scraping error" message
|
||||
var translate = this._translate;
|
||||
if(exception) {
|
||||
var myException = function(e) {
|
||||
try {
|
||||
exception(e);
|
||||
} catch(e) {
|
||||
try {
|
||||
Zotero.Browser.deleteHiddenBrowser(hiddenBrowser);
|
||||
} catch(e) {}
|
||||
translate.complete(false, e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var myException = function(e) {
|
||||
try {
|
||||
Zotero.Browser.deleteHiddenBrowser(hiddenBrowser);
|
||||
} catch(e) {}
|
||||
translate.complete(false, e);
|
||||
}
|
||||
}
|
||||
|
||||
var translate = this._translate;
|
||||
translate.incrementAsyncProcesses();
|
||||
var hiddenBrowser = Zotero.HTTP.processDocuments(urls, processor, function() {
|
||||
if(done) done();
|
||||
translate.decrementAsyncProcesses();
|
||||
translate.setHandler("done", function() {
|
||||
try {
|
||||
Zotero.Browser.deleteHiddenBrowser(hiddenBrowser);
|
||||
} catch(e) {}
|
||||
});
|
||||
}, myException, true, translate.cookieSandbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the DOM document object corresponding to the page located at URL, but avoids locking the
|
||||
* UI while the request is in process.
|
||||
*
|
||||
* @param {String} url URL to load
|
||||
* @return {Document} DOM document object
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.retrieveDocument = function(url) {
|
||||
if(!Zotero.isFx) throw "Zotero.Utilities.retrieveDocument() is unsupported outside of Firefox";
|
||||
this._translate._debug("WARNING: Zotero.Utilities.retrieveDocument() is unsupported outside of Firefox", 1);
|
||||
|
||||
url = this._convertURL(url);
|
||||
|
||||
var mainThread = Zotero.mainThread;
|
||||
var loaded = false;
|
||||
var listener = function() {
|
||||
loaded = hiddenBrowser.contentDocument.location.href != "about:blank";
|
||||
}
|
||||
|
||||
var hiddenBrowser = Zotero.Browser.createHiddenBrowser();
|
||||
if(translate.cookieSandbox) translate.cookieSandbox.attachToBrowser(hiddenBrowser);
|
||||
|
||||
hiddenBrowser.addEventListener("pageshow", listener, true);
|
||||
hiddenBrowser.loadURI(url);
|
||||
|
||||
// Use a timeout of 2 minutes. Without a timeout, a request to an IP at which no system is
|
||||
// configured will continue indefinitely, and hang Firefox as it shuts down. No same request
|
||||
// should ever take longer than 2 minutes.
|
||||
var endTime = Date.now() + 120000;
|
||||
while(!loaded && Date.now() < endTime) {
|
||||
mainThread.processNextEvent(true);
|
||||
}
|
||||
|
||||
hiddenBrowser.removeEventListener("pageshow", listener, true);
|
||||
hiddenBrowser.contentWindow.setTimeout(function() {
|
||||
Zotero.Browser.deleteHiddenBrowser(hiddenBrowser);
|
||||
}, 1);
|
||||
|
||||
if(!loaded) throw "retrieveDocument failed: request timeout";
|
||||
return hiddenBrowser.contentDocument;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source of the page located at URL, but avoids locking the UI while the request is in
|
||||
* process.
|
||||
*
|
||||
* @param {String} url URL to load
|
||||
* @param {String} [body=null] Request body to POST to the URL; a GET request is
|
||||
* executed if no body is present
|
||||
* @param {Object} [headers] HTTP headers to include in request;
|
||||
* Content-Type defaults to application/x-www-form-urlencoded
|
||||
* for POST; ignored if no body
|
||||
* @param {String} [responseCharset] Character set to force on the response
|
||||
* @return {String} Request body
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.retrieveSource = function(url, body, headers, responseCharset) {
|
||||
this._translate._debug("WARNING: Use of Zotero.Utilities.retrieveSource() is deprecated. "+
|
||||
"The main thread will be frozen when Zotero.Utilities.retrieveSource() is called outside "+
|
||||
"of Firefox, and cross-domain requests will not work.", 1);
|
||||
|
||||
if(Zotero.isFx) {
|
||||
/* Apparently, a synchronous XMLHttpRequest would have the behavior of this routine in FF3, but
|
||||
* in FF3.5, synchronous XHR blocks all JavaScript on the thread. See
|
||||
* http://hacks.mozilla.org/2009/07/synchronous-xhr/. */
|
||||
url = this._convertURL(url);
|
||||
if(!headers) headers = null;
|
||||
if(!responseCharset) responseCharset = null;
|
||||
|
||||
var mainThread = Zotero.mainThread;
|
||||
var finished = false;
|
||||
var listener = function() { finished = true };
|
||||
|
||||
if(body) {
|
||||
var xmlhttp = Zotero.HTTP.doPost(url, body, listener, headers, responseCharset, translate.cookieSandbox);
|
||||
} else {
|
||||
var xmlhttp = Zotero.HTTP.doGet(url, listener, responseCharset, translate.cookieSandbox);
|
||||
}
|
||||
|
||||
while(!finished) mainThread.processNextEvent(true);
|
||||
} else {
|
||||
// Use a synchronous XMLHttpRequest, even though this is inadvisable
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
xmlhttp.open((body ? "POST" : "GET"), url, false);
|
||||
xmlhttp.send(body ? body : null);
|
||||
}
|
||||
|
||||
if(xmlhttp.status >= 400) throw "Zotero.Utilities.retrieveSource() failed: "+xmlhttp.status+" "+xmlhttp.statusText;
|
||||
|
||||
return xmlhttp.responseText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an HTTP GET request via XMLHTTPRequest
|
||||
*
|
||||
* @param {String|String[]} urls URL(s) to request
|
||||
* @param {Function} processor Callback to be executed for each document loaded
|
||||
* @param {Function} done Callback to be executed after all documents have been loaded
|
||||
* @param {String} responseCharset Character set to force on the response
|
||||
* @return {Boolean} True if the request was sent, or false if the browser is offline
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.doGet = function(urls, processor, done, responseCharset) {
|
||||
var callAgain = false;
|
||||
|
||||
if(typeof(urls) == "string") {
|
||||
var url = urls;
|
||||
} else {
|
||||
if(urls.length > 1) callAgain = true;
|
||||
var url = urls.shift();
|
||||
}
|
||||
|
||||
url = this._convertURL(url);
|
||||
|
||||
var me = this;
|
||||
|
||||
this._translate.incrementAsyncProcesses();
|
||||
var xmlhttp = Zotero.HTTP.doGet(url, function(xmlhttp) {
|
||||
try {
|
||||
if(processor) {
|
||||
processor(xmlhttp.responseText, xmlhttp, url);
|
||||
}
|
||||
|
||||
if(callAgain) {
|
||||
me.doGet(urls, processor, done);
|
||||
} else {
|
||||
if(done) {
|
||||
done();
|
||||
}
|
||||
}
|
||||
me._translate.decrementAsyncProcesses();
|
||||
} catch(e) {
|
||||
me._translate.complete(false, e);
|
||||
}
|
||||
}, responseCharset, this._translate.cookieSandbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Already documented in Zotero.HTTP
|
||||
* @ignore
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.doPost = function(url, body, onDone, headers, responseCharset) {
|
||||
url = this._convertURL(url);
|
||||
|
||||
var translate = this._translate;
|
||||
this._translate.incrementAsyncProcesses();
|
||||
var xmlhttp = Zotero.HTTP.doPost(url, body, function(xmlhttp) {
|
||||
try {
|
||||
onDone(xmlhttp.responseText, xmlhttp);
|
||||
translate.decrementAsyncProcesses();
|
||||
} catch(e) {
|
||||
translate.complete(false, e);
|
||||
}
|
||||
}, headers, responseCharset, translate.cookieSandbox ? translate.cookieSandbox : undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a URL to a form that goes through the appropriate proxy, or convert a relative URL to
|
||||
* an absolute one
|
||||
*
|
||||
* @param {String} url
|
||||
* @type String
|
||||
* @private
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype._convertURL = function(url) {
|
||||
const protocolRe = /^(?:(?:http|https|ftp):)/i;
|
||||
|
||||
// convert proxy to proper if applicable
|
||||
if(protocolRe.test(url)) {
|
||||
if(this._translate.translator && this._translate.translator[0]
|
||||
&& this._translate.translator[0].properToProxy) {
|
||||
url = this._translate.translator[0].properToProxy(url);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
// resolve local URL
|
||||
var resolved = "";
|
||||
if(Zotero.isFx) {
|
||||
resolved = Components.classes["@mozilla.org/network/io-service;1"].
|
||||
getService(Components.interfaces.nsIIOService).
|
||||
newURI(this._translate.location, "", null).resolve(url);
|
||||
} else if(Zotero.isChrome || Zotero.isSafari) {
|
||||
var a = document.createElement('a');
|
||||
a.href = url;
|
||||
resolved = a.href;
|
||||
} else if(Zotero.isNode) {
|
||||
resolved = require('url').resolve(this._translate.location, url);
|
||||
}
|
||||
|
||||
if(!protocolRe.test(resolved)) {
|
||||
throw new Error("Invalid URL supplied for HTTP request: "+url);
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
Zotero.Utilities.Translate.prototype.__exposedProps__ = {"HTTP":"r"};
|
||||
for(var j in Zotero.Utilities.Translate.prototype) {
|
||||
if(typeof Zotero.Utilities.Translate.prototype[j] === "function" && j[0] !== "_" && j != "Translate") {
|
||||
Zotero.Utilities.Translate.prototype.__exposedProps__[j] = "r";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class Utility functions not made available to translators
|
||||
*/
|
||||
Zotero.Utilities.Internal = {
|
||||
/*
|
||||
* Adapted from http://developer.mozilla.org/en/docs/nsICryptoHash
|
||||
*
|
||||
* @param {String|nsIFile} strOrFile
|
||||
* @param {Boolean} [base64=false] Return as base-64-encoded string rather than hex string
|
||||
* @return {String}
|
||||
*/
|
||||
"md5":function(strOrFile, base64) {
|
||||
if (typeof strOrFile == 'string') {
|
||||
var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].
|
||||
createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
var result = {};
|
||||
var data = converter.convertToByteArray(strOrFile, result);
|
||||
var ch = Components.classes["@mozilla.org/security/hash;1"]
|
||||
.createInstance(Components.interfaces.nsICryptoHash);
|
||||
ch.init(ch.MD5);
|
||||
ch.update(data, data.length);
|
||||
}
|
||||
else if (strOrFile instanceof Components.interfaces.nsIFile) {
|
||||
// Otherwise throws (NS_ERROR_NOT_AVAILABLE) [nsICryptoHash.updateFromStream]
|
||||
if (!strOrFile.fileSize) {
|
||||
// MD5 for empty string
|
||||
return "d41d8cd98f00b204e9800998ecf8427e";
|
||||
}
|
||||
|
||||
var istream = Components.classes["@mozilla.org/network/file-input-stream;1"]
|
||||
.createInstance(Components.interfaces.nsIFileInputStream);
|
||||
// open for reading
|
||||
istream.init(strOrFile, 0x01, 0444, 0);
|
||||
var ch = Components.classes["@mozilla.org/security/hash;1"]
|
||||
.createInstance(Components.interfaces.nsICryptoHash);
|
||||
// we want to use the MD5 algorithm
|
||||
ch.init(ch.MD5);
|
||||
// this tells updateFromStream to read the entire file
|
||||
const PR_UINT32_MAX = 0xffffffff;
|
||||
ch.updateFromStream(istream, PR_UINT32_MAX);
|
||||
}
|
||||
|
||||
// pass false here to get binary data back
|
||||
var hash = ch.finish(base64);
|
||||
|
||||
if (istream) {
|
||||
istream.close();
|
||||
}
|
||||
|
||||
if (base64) {
|
||||
return hash;
|
||||
}
|
||||
|
||||
/*
|
||||
// This created 36-character hashes
|
||||
|
||||
// return the two-digit hexadecimal code for a byte
|
||||
function toHexString(charCode) {
|
||||
return ("0" + charCode.toString(16)).slice(-2);
|
||||
}
|
||||
|
||||
// convert the binary hash data to a hex string.
|
||||
return [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
|
||||
*/
|
||||
|
||||
// From http://rcrowley.org/2007/11/15/md5-in-xulrunner-or-firefox-extensions/
|
||||
var ascii = [];
|
||||
var ii = hash.length;
|
||||
for (var i = 0; i < ii; ++i) {
|
||||
var c = hash.charCodeAt(i);
|
||||
var ones = c % 16;
|
||||
var tens = c >> 4;
|
||||
ascii.push(String.fromCharCode(tens + (tens > 9 ? 87 : 48)) + String.fromCharCode(ones + (ones > 9 ? 87 : 48)));
|
||||
}
|
||||
return ascii.join('');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 encode / decode
|
||||
* From http://www.webtoolkit.info/
|
||||
*/
|
||||
Zotero.Utilities.Internal.Base64 = {
|
||||
// private property
|
||||
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
|
||||
|
||||
// public method for encoding
|
||||
encode : function (input) {
|
||||
var output = "";
|
||||
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
|
||||
var i = 0;
|
||||
|
||||
input = this._utf8_encode(input);
|
||||
|
||||
while (i < input.length) {
|
||||
|
||||
chr1 = input.charCodeAt(i++);
|
||||
chr2 = input.charCodeAt(i++);
|
||||
chr3 = input.charCodeAt(i++);
|
||||
|
||||
enc1 = chr1 >> 2;
|
||||
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
|
||||
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
|
||||
enc4 = chr3 & 63;
|
||||
|
||||
if (isNaN(chr2)) {
|
||||
enc3 = enc4 = 64;
|
||||
} else if (isNaN(chr3)) {
|
||||
enc4 = 64;
|
||||
}
|
||||
|
||||
output = output +
|
||||
this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
|
||||
this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
|
||||
|
||||
}
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
// public method for decoding
|
||||
decode : function (input) {
|
||||
var output = "";
|
||||
var chr1, chr2, chr3;
|
||||
var enc1, enc2, enc3, enc4;
|
||||
var i = 0;
|
||||
|
||||
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
|
||||
|
||||
while (i < input.length) {
|
||||
|
||||
enc1 = this._keyStr.indexOf(input.charAt(i++));
|
||||
enc2 = this._keyStr.indexOf(input.charAt(i++));
|
||||
enc3 = this._keyStr.indexOf(input.charAt(i++));
|
||||
enc4 = this._keyStr.indexOf(input.charAt(i++));
|
||||
|
||||
chr1 = (enc1 << 2) | (enc2 >> 4);
|
||||
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
|
||||
chr3 = ((enc3 & 3) << 6) | enc4;
|
||||
|
||||
output = output + String.fromCharCode(chr1);
|
||||
|
||||
if (enc3 != 64) {
|
||||
output = output + String.fromCharCode(chr2);
|
||||
}
|
||||
if (enc4 != 64) {
|
||||
output = output + String.fromCharCode(chr3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
output = this._utf8_decode(output);
|
||||
|
||||
return output;
|
||||
|
||||
},
|
||||
|
||||
// private method for UTF-8 encoding
|
||||
_utf8_encode : function (string) {
|
||||
string = string.replace(/\r\n/g,"\n");
|
||||
var utftext = "";
|
||||
|
||||
for (var n = 0; n < string.length; n++) {
|
||||
|
||||
var c = string.charCodeAt(n);
|
||||
|
||||
if (c < 128) {
|
||||
utftext += String.fromCharCode(c);
|
||||
}
|
||||
else if((c > 127) && (c < 2048)) {
|
||||
utftext += String.fromCharCode((c >> 6) | 192);
|
||||
utftext += String.fromCharCode((c & 63) | 128);
|
||||
}
|
||||
else {
|
||||
utftext += String.fromCharCode((c >> 12) | 224);
|
||||
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
|
||||
utftext += String.fromCharCode((c & 63) | 128);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return utftext;
|
||||
},
|
||||
|
||||
// private method for UTF-8 decoding
|
||||
_utf8_decode : function (utftext) {
|
||||
var string = "";
|
||||
var i = 0;
|
||||
var c = c1 = c2 = 0;
|
||||
|
||||
while ( i < utftext.length ) {
|
||||
|
||||
c = utftext.charCodeAt(i);
|
||||
|
||||
if (c < 128) {
|
||||
string += String.fromCharCode(c);
|
||||
i++;
|
||||
}
|
||||
else if((c > 191) && (c < 224)) {
|
||||
c2 = utftext.charCodeAt(i+1);
|
||||
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
|
||||
i += 2;
|
||||
}
|
||||
else {
|
||||
c2 = utftext.charCodeAt(i+1);
|
||||
c3 = utftext.charCodeAt(i+2);
|
||||
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
|
||||
i += 3;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
}
|
244
chrome/content/zotero/xpcom/utilities_internal.js
Normal file
244
chrome/content/zotero/xpcom/utilities_internal.js
Normal file
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2009 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
http://zotero.org
|
||||
|
||||
This file is part of Zotero.
|
||||
|
||||
Zotero is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Zotero is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
Utilities based in part on code taken from Piggy Bank 2.1.1 (BSD-licensed)
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class Utility functions not made available to translators
|
||||
*/
|
||||
Zotero.Utilities.Internal = {
|
||||
/*
|
||||
* Adapted from http://developer.mozilla.org/en/docs/nsICryptoHash
|
||||
*
|
||||
* @param {String|nsIFile} strOrFile
|
||||
* @param {Boolean} [base64=false] Return as base-64-encoded string rather than hex string
|
||||
* @return {String}
|
||||
*/
|
||||
"md5":function(strOrFile, base64) {
|
||||
if (typeof strOrFile == 'string') {
|
||||
var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].
|
||||
createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
var result = {};
|
||||
var data = converter.convertToByteArray(strOrFile, result);
|
||||
var ch = Components.classes["@mozilla.org/security/hash;1"]
|
||||
.createInstance(Components.interfaces.nsICryptoHash);
|
||||
ch.init(ch.MD5);
|
||||
ch.update(data, data.length);
|
||||
}
|
||||
else if (strOrFile instanceof Components.interfaces.nsIFile) {
|
||||
// Otherwise throws (NS_ERROR_NOT_AVAILABLE) [nsICryptoHash.updateFromStream]
|
||||
if (!strOrFile.fileSize) {
|
||||
// MD5 for empty string
|
||||
return "d41d8cd98f00b204e9800998ecf8427e";
|
||||
}
|
||||
|
||||
var istream = Components.classes["@mozilla.org/network/file-input-stream;1"]
|
||||
.createInstance(Components.interfaces.nsIFileInputStream);
|
||||
// open for reading
|
||||
istream.init(strOrFile, 0x01, 0444, 0);
|
||||
var ch = Components.classes["@mozilla.org/security/hash;1"]
|
||||
.createInstance(Components.interfaces.nsICryptoHash);
|
||||
// we want to use the MD5 algorithm
|
||||
ch.init(ch.MD5);
|
||||
// this tells updateFromStream to read the entire file
|
||||
const PR_UINT32_MAX = 0xffffffff;
|
||||
ch.updateFromStream(istream, PR_UINT32_MAX);
|
||||
}
|
||||
|
||||
// pass false here to get binary data back
|
||||
var hash = ch.finish(base64);
|
||||
|
||||
if (istream) {
|
||||
istream.close();
|
||||
}
|
||||
|
||||
if (base64) {
|
||||
return hash;
|
||||
}
|
||||
|
||||
/*
|
||||
// This created 36-character hashes
|
||||
|
||||
// return the two-digit hexadecimal code for a byte
|
||||
function toHexString(charCode) {
|
||||
return ("0" + charCode.toString(16)).slice(-2);
|
||||
}
|
||||
|
||||
// convert the binary hash data to a hex string.
|
||||
return [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
|
||||
*/
|
||||
|
||||
// From http://rcrowley.org/2007/11/15/md5-in-xulrunner-or-firefox-extensions/
|
||||
var ascii = [];
|
||||
var ii = hash.length;
|
||||
for (var i = 0; i < ii; ++i) {
|
||||
var c = hash.charCodeAt(i);
|
||||
var ones = c % 16;
|
||||
var tens = c >> 4;
|
||||
ascii.push(String.fromCharCode(tens + (tens > 9 ? 87 : 48)) + String.fromCharCode(ones + (ones > 9 ? 87 : 48)));
|
||||
}
|
||||
return ascii.join('');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 encode / decode
|
||||
* From http://www.webtoolkit.info/
|
||||
*/
|
||||
Zotero.Utilities.Internal.Base64 = {
|
||||
// private property
|
||||
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
|
||||
|
||||
// public method for encoding
|
||||
encode : function (input) {
|
||||
var output = "";
|
||||
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
|
||||
var i = 0;
|
||||
|
||||
input = this._utf8_encode(input);
|
||||
|
||||
while (i < input.length) {
|
||||
|
||||
chr1 = input.charCodeAt(i++);
|
||||
chr2 = input.charCodeAt(i++);
|
||||
chr3 = input.charCodeAt(i++);
|
||||
|
||||
enc1 = chr1 >> 2;
|
||||
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
|
||||
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
|
||||
enc4 = chr3 & 63;
|
||||
|
||||
if (isNaN(chr2)) {
|
||||
enc3 = enc4 = 64;
|
||||
} else if (isNaN(chr3)) {
|
||||
enc4 = 64;
|
||||
}
|
||||
|
||||
output = output +
|
||||
this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
|
||||
this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
|
||||
|
||||
}
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
// public method for decoding
|
||||
decode : function (input) {
|
||||
var output = "";
|
||||
var chr1, chr2, chr3;
|
||||
var enc1, enc2, enc3, enc4;
|
||||
var i = 0;
|
||||
|
||||
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
|
||||
|
||||
while (i < input.length) {
|
||||
|
||||
enc1 = this._keyStr.indexOf(input.charAt(i++));
|
||||
enc2 = this._keyStr.indexOf(input.charAt(i++));
|
||||
enc3 = this._keyStr.indexOf(input.charAt(i++));
|
||||
enc4 = this._keyStr.indexOf(input.charAt(i++));
|
||||
|
||||
chr1 = (enc1 << 2) | (enc2 >> 4);
|
||||
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
|
||||
chr3 = ((enc3 & 3) << 6) | enc4;
|
||||
|
||||
output = output + String.fromCharCode(chr1);
|
||||
|
||||
if (enc3 != 64) {
|
||||
output = output + String.fromCharCode(chr2);
|
||||
}
|
||||
if (enc4 != 64) {
|
||||
output = output + String.fromCharCode(chr3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
output = this._utf8_decode(output);
|
||||
|
||||
return output;
|
||||
|
||||
},
|
||||
|
||||
// private method for UTF-8 encoding
|
||||
_utf8_encode : function (string) {
|
||||
string = string.replace(/\r\n/g,"\n");
|
||||
var utftext = "";
|
||||
|
||||
for (var n = 0; n < string.length; n++) {
|
||||
|
||||
var c = string.charCodeAt(n);
|
||||
|
||||
if (c < 128) {
|
||||
utftext += String.fromCharCode(c);
|
||||
}
|
||||
else if((c > 127) && (c < 2048)) {
|
||||
utftext += String.fromCharCode((c >> 6) | 192);
|
||||
utftext += String.fromCharCode((c & 63) | 128);
|
||||
}
|
||||
else {
|
||||
utftext += String.fromCharCode((c >> 12) | 224);
|
||||
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
|
||||
utftext += String.fromCharCode((c & 63) | 128);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return utftext;
|
||||
},
|
||||
|
||||
// private method for UTF-8 decoding
|
||||
_utf8_decode : function (utftext) {
|
||||
var string = "";
|
||||
var i = 0;
|
||||
var c = c1 = c2 = 0;
|
||||
|
||||
while ( i < utftext.length ) {
|
||||
|
||||
c = utftext.charCodeAt(i);
|
||||
|
||||
if (c < 128) {
|
||||
string += String.fromCharCode(c);
|
||||
i++;
|
||||
}
|
||||
else if((c > 191) && (c < 224)) {
|
||||
c2 = utftext.charCodeAt(i+1);
|
||||
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
|
||||
i += 2;
|
||||
}
|
||||
else {
|
||||
c2 = utftext.charCodeAt(i+1);
|
||||
c3 = utftext.charCodeAt(i+2);
|
||||
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
|
||||
i += 3;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
}
|
442
chrome/content/zotero/xpcom/utilities_translate.js
Normal file
442
chrome/content/zotero/xpcom/utilities_translate.js
Normal file
|
@ -0,0 +1,442 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2009 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
http://zotero.org
|
||||
|
||||
This file is part of Zotero.
|
||||
|
||||
Zotero is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Zotero is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
Utilities based in part on code taken from Piggy Bank 2.1.1 (BSD-licensed)
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class All functions accessible from within Zotero.Utilities namespace inside sandboxed
|
||||
* translators
|
||||
*
|
||||
* @constructor
|
||||
* @augments Zotero.Utilities
|
||||
* @borrows Zotero.Date.formatDate as this.formatDate
|
||||
* @borrows Zotero.Date.strToDate as this.strToDate
|
||||
* @borrows Zotero.Date.strToISO as this.strToISO
|
||||
* @borrows Zotero.OpenURL.createContextObject as this.createContextObject
|
||||
* @borrows Zotero.OpenURL.parseContextObject as this.parseContextObject
|
||||
* @borrows Zotero.HTTP.processDocuments as this.processDocuments
|
||||
* @borrows Zotero.HTTP.doPost as this.doPost
|
||||
* @param {Zotero.Translate} translate
|
||||
*/
|
||||
Zotero.Utilities.Translate = function(translate) {
|
||||
this._translate = translate;
|
||||
}
|
||||
|
||||
var tmp = function() {};
|
||||
tmp.prototype = Zotero.Utilities;
|
||||
Zotero.Utilities.Translate.prototype = new tmp();
|
||||
|
||||
Zotero.Utilities.Translate.prototype.formatDate = Zotero.Date.formatDate;
|
||||
Zotero.Utilities.Translate.prototype.strToDate = Zotero.Date.strToDate;
|
||||
Zotero.Utilities.Translate.prototype.strToISO = Zotero.Date.strToISO;
|
||||
Zotero.Utilities.Translate.prototype.createContextObject = Zotero.OpenURL.createContextObject;
|
||||
Zotero.Utilities.Translate.prototype.parseContextObject = Zotero.OpenURL.parseContextObject;
|
||||
|
||||
/**
|
||||
* Hack to overloads {@link Zotero.Utilities.capitalizeTitle} to allow overriding capitalizeTitles
|
||||
* pref on a per-translate instance basis (for translator testing only)
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.capitalizeTitle = function(string, force) {
|
||||
if(force === undefined) {
|
||||
var translate = this._translate;
|
||||
do {
|
||||
if(translate.capitalizeTitles !== undefined) {
|
||||
force = translate.capitalizeTitles;
|
||||
break;
|
||||
}
|
||||
} while(translate = translate._parentTranslator);
|
||||
}
|
||||
|
||||
return Zotero.Utilities.capitalizeTitle(string, force);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current Zotero version
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.getVersion = function() {
|
||||
return Zotero.version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an XPath query and returns the results
|
||||
*
|
||||
* @deprecated Use {@link Zotero.Utilities.xpath} or doc.evaluate() directly
|
||||
* @type Node[]
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.gatherElementsOnXPath = function(doc, parentNode, xpath, nsResolver) {
|
||||
var elmts = [];
|
||||
|
||||
var iterator = doc.evaluate(xpath, parentNode, nsResolver,
|
||||
(Zotero.isFx ? Components.interfaces.nsIDOMXPathResult.ANY_TYPE : XPathResult.ANY_TYPE),
|
||||
null);
|
||||
var elmt = iterator.iterateNext();
|
||||
var i = 0;
|
||||
while (elmt) {
|
||||
elmts[i++] = elmt;
|
||||
elmt = iterator.iterateNext();
|
||||
}
|
||||
return elmts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a given node as a string containing all child nodes
|
||||
*
|
||||
* @deprecated Use doc.evaluate and the "nodeValue" or "textContent" property
|
||||
* @type String
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.getNodeString = function(doc, contextNode, xpath, nsResolver) {
|
||||
var elmts = this.gatherElementsOnXPath(doc, contextNode, xpath, nsResolver);
|
||||
var returnVar = "";
|
||||
for(var i=0; i<elmts.length; i++) {
|
||||
returnVar += elmts[i].nodeValue;
|
||||
}
|
||||
return returnVar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grabs items based on URLs
|
||||
*
|
||||
* @param {Document} doc DOM document object
|
||||
* @param {Element|Element[]} inHere DOM element(s) to process
|
||||
* @param {RegExp} [urlRe] Regexp of URLs to add to list
|
||||
* @param {RegExp} [urlRe] Regexp of URLs to reject
|
||||
* @return {Object} Associative array of link => textContent pairs, suitable for passing to
|
||||
* Zotero.selectItems from within a translator
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.getItemArray = function(doc, inHere, urlRe, rejectRe) {
|
||||
var availableItems = new Object(); // Technically, associative arrays are objects
|
||||
|
||||
// Require link to match this
|
||||
if(urlRe) {
|
||||
if(urlRe.exec) {
|
||||
var urlRegexp = urlRe;
|
||||
} else {
|
||||
var urlRegexp = new RegExp();
|
||||
urlRegexp.compile(urlRe, "i");
|
||||
}
|
||||
}
|
||||
// Do not allow text to match this
|
||||
if(rejectRe) {
|
||||
if(rejectRe.exec) {
|
||||
var rejectRegexp = rejectRe;
|
||||
} else {
|
||||
var rejectRegexp = new RegExp();
|
||||
rejectRegexp.compile(rejectRe, "i");
|
||||
}
|
||||
}
|
||||
|
||||
if(!inHere.length) {
|
||||
inHere = new Array(inHere);
|
||||
}
|
||||
|
||||
for(var j=0; j<inHere.length; j++) {
|
||||
var links = inHere[j].getElementsByTagName("a");
|
||||
for(var i=0; i<links.length; i++) {
|
||||
if(!urlRe || urlRegexp.test(links[i].href)) {
|
||||
var text = links[i].textContent;
|
||||
if(text) {
|
||||
text = this.trimInternal(text);
|
||||
if(!rejectRe || !rejectRegexp.test(text)) {
|
||||
if(availableItems[links[i].href]) {
|
||||
if(text != availableItems[links[i].href]) {
|
||||
availableItems[links[i].href] += " "+text;
|
||||
}
|
||||
} else {
|
||||
availableItems[links[i].href] = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return availableItems;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load a single document in a hidden browser
|
||||
*
|
||||
* @deprecated Use processDocuments with a single URL
|
||||
* @see Zotero.Utilities.Translate#processDocuments
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.loadDocument = function(url, succeeded, failed) {
|
||||
Zotero.debug("Zotero.Utilities.loadDocument is deprecated; please use processDocuments in new code");
|
||||
this.processDocuments([url], succeeded, null, failed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Already documented in Zotero.HTTP
|
||||
* @ignore
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.processDocuments = function(urls, processor, done, exception) {
|
||||
if(typeof(urls) == "string") {
|
||||
urls = [this._convertURL(urls)];
|
||||
} else {
|
||||
for(var i in urls) {
|
||||
urls[i] = this._convertURL(urls[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Unless the translator has proposed some way to handle an error, handle it
|
||||
// by throwing a "scraping error" message
|
||||
var translate = this._translate;
|
||||
if(exception) {
|
||||
var myException = function(e) {
|
||||
try {
|
||||
exception(e);
|
||||
} catch(e) {
|
||||
try {
|
||||
Zotero.Browser.deleteHiddenBrowser(hiddenBrowser);
|
||||
} catch(e) {}
|
||||
translate.complete(false, e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var myException = function(e) {
|
||||
try {
|
||||
Zotero.Browser.deleteHiddenBrowser(hiddenBrowser);
|
||||
} catch(e) {}
|
||||
translate.complete(false, e);
|
||||
}
|
||||
}
|
||||
|
||||
var translate = this._translate;
|
||||
translate.incrementAsyncProcesses();
|
||||
var hiddenBrowser = Zotero.HTTP.processDocuments(urls, processor, function() {
|
||||
if(done) done();
|
||||
translate.decrementAsyncProcesses();
|
||||
translate.setHandler("done", function() {
|
||||
try {
|
||||
Zotero.Browser.deleteHiddenBrowser(hiddenBrowser);
|
||||
} catch(e) {}
|
||||
});
|
||||
}, myException, true, translate.cookieSandbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the DOM document object corresponding to the page located at URL, but avoids locking the
|
||||
* UI while the request is in process.
|
||||
*
|
||||
* @param {String} url URL to load
|
||||
* @return {Document} DOM document object
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.retrieveDocument = function(url) {
|
||||
if(!Zotero.isFx) throw "Zotero.Utilities.retrieveDocument() is unsupported outside of Firefox";
|
||||
this._translate._debug("WARNING: Zotero.Utilities.retrieveDocument() is unsupported outside of Firefox", 1);
|
||||
|
||||
url = this._convertURL(url);
|
||||
|
||||
var mainThread = Zotero.mainThread;
|
||||
var loaded = false;
|
||||
var listener = function() {
|
||||
loaded = hiddenBrowser.contentDocument.location.href != "about:blank";
|
||||
}
|
||||
|
||||
var hiddenBrowser = Zotero.Browser.createHiddenBrowser();
|
||||
if(translate.cookieSandbox) translate.cookieSandbox.attachToBrowser(hiddenBrowser);
|
||||
|
||||
hiddenBrowser.addEventListener("pageshow", listener, true);
|
||||
hiddenBrowser.loadURI(url);
|
||||
|
||||
// Use a timeout of 2 minutes. Without a timeout, a request to an IP at which no system is
|
||||
// configured will continue indefinitely, and hang Firefox as it shuts down. No same request
|
||||
// should ever take longer than 2 minutes.
|
||||
var endTime = Date.now() + 120000;
|
||||
while(!loaded && Date.now() < endTime) {
|
||||
mainThread.processNextEvent(true);
|
||||
}
|
||||
|
||||
hiddenBrowser.removeEventListener("pageshow", listener, true);
|
||||
hiddenBrowser.contentWindow.setTimeout(function() {
|
||||
Zotero.Browser.deleteHiddenBrowser(hiddenBrowser);
|
||||
}, 1);
|
||||
|
||||
if(!loaded) throw "retrieveDocument failed: request timeout";
|
||||
return hiddenBrowser.contentDocument;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source of the page located at URL, but avoids locking the UI while the request is in
|
||||
* process.
|
||||
*
|
||||
* @param {String} url URL to load
|
||||
* @param {String} [body=null] Request body to POST to the URL; a GET request is
|
||||
* executed if no body is present
|
||||
* @param {Object} [headers] HTTP headers to include in request;
|
||||
* Content-Type defaults to application/x-www-form-urlencoded
|
||||
* for POST; ignored if no body
|
||||
* @param {String} [responseCharset] Character set to force on the response
|
||||
* @return {String} Request body
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.retrieveSource = function(url, body, headers, responseCharset) {
|
||||
this._translate._debug("WARNING: Use of Zotero.Utilities.retrieveSource() is deprecated. "+
|
||||
"The main thread will be frozen when Zotero.Utilities.retrieveSource() is called outside "+
|
||||
"of Firefox, and cross-domain requests will not work.", 1);
|
||||
|
||||
if(Zotero.isFx) {
|
||||
/* Apparently, a synchronous XMLHttpRequest would have the behavior of this routine in FF3, but
|
||||
* in FF3.5, synchronous XHR blocks all JavaScript on the thread. See
|
||||
* http://hacks.mozilla.org/2009/07/synchronous-xhr/. */
|
||||
url = this._convertURL(url);
|
||||
if(!headers) headers = null;
|
||||
if(!responseCharset) responseCharset = null;
|
||||
|
||||
var mainThread = Zotero.mainThread;
|
||||
var finished = false;
|
||||
var listener = function() { finished = true };
|
||||
|
||||
if(body) {
|
||||
var xmlhttp = Zotero.HTTP.doPost(url, body, listener, headers, responseCharset, translate.cookieSandbox);
|
||||
} else {
|
||||
var xmlhttp = Zotero.HTTP.doGet(url, listener, responseCharset, translate.cookieSandbox);
|
||||
}
|
||||
|
||||
while(!finished) mainThread.processNextEvent(true);
|
||||
} else {
|
||||
// Use a synchronous XMLHttpRequest, even though this is inadvisable
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
xmlhttp.open((body ? "POST" : "GET"), url, false);
|
||||
xmlhttp.send(body ? body : null);
|
||||
}
|
||||
|
||||
if(xmlhttp.status >= 400) throw "Zotero.Utilities.retrieveSource() failed: "+xmlhttp.status+" "+xmlhttp.statusText;
|
||||
|
||||
return xmlhttp.responseText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an HTTP GET request via XMLHTTPRequest
|
||||
*
|
||||
* @param {String|String[]} urls URL(s) to request
|
||||
* @param {Function} processor Callback to be executed for each document loaded
|
||||
* @param {Function} done Callback to be executed after all documents have been loaded
|
||||
* @param {String} responseCharset Character set to force on the response
|
||||
* @return {Boolean} True if the request was sent, or false if the browser is offline
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.doGet = function(urls, processor, done, responseCharset) {
|
||||
var callAgain = false;
|
||||
|
||||
if(typeof(urls) == "string") {
|
||||
var url = urls;
|
||||
} else {
|
||||
if(urls.length > 1) callAgain = true;
|
||||
var url = urls.shift();
|
||||
}
|
||||
|
||||
url = this._convertURL(url);
|
||||
|
||||
var me = this;
|
||||
|
||||
this._translate.incrementAsyncProcesses();
|
||||
var xmlhttp = Zotero.HTTP.doGet(url, function(xmlhttp) {
|
||||
try {
|
||||
if(processor) {
|
||||
processor(xmlhttp.responseText, xmlhttp, url);
|
||||
}
|
||||
|
||||
if(callAgain) {
|
||||
me.doGet(urls, processor, done);
|
||||
} else {
|
||||
if(done) {
|
||||
done();
|
||||
}
|
||||
}
|
||||
me._translate.decrementAsyncProcesses();
|
||||
} catch(e) {
|
||||
me._translate.complete(false, e);
|
||||
}
|
||||
}, responseCharset, this._translate.cookieSandbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Already documented in Zotero.HTTP
|
||||
* @ignore
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype.doPost = function(url, body, onDone, headers, responseCharset) {
|
||||
url = this._convertURL(url);
|
||||
|
||||
var translate = this._translate;
|
||||
this._translate.incrementAsyncProcesses();
|
||||
var xmlhttp = Zotero.HTTP.doPost(url, body, function(xmlhttp) {
|
||||
try {
|
||||
onDone(xmlhttp.responseText, xmlhttp);
|
||||
translate.decrementAsyncProcesses();
|
||||
} catch(e) {
|
||||
translate.complete(false, e);
|
||||
}
|
||||
}, headers, responseCharset, translate.cookieSandbox ? translate.cookieSandbox : undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a URL to a form that goes through the appropriate proxy, or convert a relative URL to
|
||||
* an absolute one
|
||||
*
|
||||
* @param {String} url
|
||||
* @type String
|
||||
* @private
|
||||
*/
|
||||
Zotero.Utilities.Translate.prototype._convertURL = function(url) {
|
||||
const protocolRe = /^(?:(?:http|https|ftp):)/i;
|
||||
|
||||
// convert proxy to proper if applicable
|
||||
if(protocolRe.test(url)) {
|
||||
if(this._translate.translator && this._translate.translator[0]
|
||||
&& this._translate.translator[0].properToProxy) {
|
||||
url = this._translate.translator[0].properToProxy(url);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
// resolve local URL
|
||||
var resolved = "";
|
||||
if(Zotero.isFx) {
|
||||
resolved = Components.classes["@mozilla.org/network/io-service;1"].
|
||||
getService(Components.interfaces.nsIIOService).
|
||||
newURI(this._translate.location, "", null).resolve(url);
|
||||
} else if(Zotero.isNode) {
|
||||
resolved = require('url').resolve(this._translate.location, url);
|
||||
} else {
|
||||
var a = document.createElement('a');
|
||||
a.href = url;
|
||||
resolved = a.href;
|
||||
}
|
||||
|
||||
if(!protocolRe.test(resolved)) {
|
||||
throw new Error("Invalid URL supplied for HTTP request: "+url);
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
Zotero.Utilities.Translate.prototype.__exposedProps__ = {"HTTP":"r"};
|
||||
for(var j in Zotero.Utilities.Translate.prototype) {
|
||||
if(typeof Zotero.Utilities.Translate.prototype[j] === "function" && j[0] !== "_" && j != "Translate") {
|
||||
Zotero.Utilities.Translate.prototype.__exposedProps__[j] = "r";
|
||||
}
|
||||
}
|
|
@ -233,7 +233,7 @@ Timeline.GregorianDateLabeller=function(locale,timeZone){this._locale=locale;thi
|
|||
Modified by Ben for Zotero
|
||||
*/
|
||||
|
||||
Timeline.GregorianDateLabeller.monthNames = Zotero.Date.months.short;
|
||||
Timeline.GregorianDateLabeller.monthNames = Zotero.Date.getMonths().short;
|
||||
Timeline.GregorianDateLabeller.getMonthName=function(month,locale) {
|
||||
return Timeline.GregorianDateLabeller.monthNames[month];
|
||||
};
|
||||
|
|
|
@ -45,7 +45,9 @@ const xpcomFilesAll = [
|
|||
'translation/translate',
|
||||
'translation/translate_firefox',
|
||||
'translation/tlds',
|
||||
'utilities'
|
||||
'utilities',
|
||||
'utilities_internal',
|
||||
'utilities_translate'
|
||||
];
|
||||
|
||||
/** XPCOM files to be loaded only for local translation and DB access **/
|
||||
|
|
Loading…
Reference in a new issue