www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | README | LICENSE

commit 4666ae972ccd713369436e2b582b5d778ec821b1
parent 21254238e1a22ecca74689e83ad82c7a90ddc9a9
Author: Simon Kornblith <simon@simonster.com>
Date:   Wed, 31 Aug 2011 23:25:48 +0000

- Move Zotero.Utilities.Internal and Zotero.Utilities.Translate to separate files
- IE compatibility for translation core


Diffstat:
Mchrome/content/zotero/fileInterface.js | 2+-
Mchrome/content/zotero/xpcom/connector/translate_item.js | 5++---
Mchrome/content/zotero/xpcom/date.js | 19++++++++-----------
Mchrome/content/zotero/xpcom/debug.js | 9++++++---
Mchrome/content/zotero/xpcom/translation/translate.js | 111++++++++++++++++++++++++++++++++++++++-----------------------------------------
Mchrome/content/zotero/xpcom/utilities.js | 635+------------------------------------------------------------------------------
Achrome/content/zotero/xpcom/utilities_internal.js | 245+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Achrome/content/zotero/xpcom/utilities_translate.js | 443+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mchrome/skin/default/zotero/timeline/bundle.js | 2+-
Mcomponents/zotero-service.js | 4+++-
10 files changed, 764 insertions(+), 711 deletions(-)

diff --git a/chrome/content/zotero/fileInterface.js b/chrome/content/zotero/fileInterface.js @@ -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()); } } diff --git a/chrome/content/zotero/xpcom/connector/translate_item.js b/chrome/content/zotero/xpcom/connector/translate_item.js @@ -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); diff --git a/chrome/content/zotero/xpcom/date.js b/chrome/content/zotero/xpcom/date.js @@ -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]; diff --git a/chrome/content/zotero/xpcom/debug.js b/chrome/content/zotero/xpcom/debug.js @@ -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; } diff --git a/chrome/content/zotero/xpcom/translation/translate.js b/chrome/content/zotero/xpcom/translation/translate.js @@ -1026,6 +1026,11 @@ Zotero.Translate.Base.prototype = { }, /** + * 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). * @param {Boolean|String} returnValue An item type or a boolean true or false @@ -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 ******/ diff --git a/chrome/content/zotero/xpcom/utilities.js b/chrome/content/zotero/xpcom/utilities.js @@ -1084,635 +1084,4 @@ 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; - } - } -\ No newline at end of file +} +\ No newline at end of file diff --git a/chrome/content/zotero/xpcom/utilities_internal.js b/chrome/content/zotero/xpcom/utilities_internal.js @@ -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; + } + } +\ No newline at end of file diff --git a/chrome/content/zotero/xpcom/utilities_translate.js b/chrome/content/zotero/xpcom/utilities_translate.js @@ -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"; + } +} +\ No newline at end of file diff --git a/chrome/skin/default/zotero/timeline/bundle.js b/chrome/skin/default/zotero/timeline/bundle.js @@ -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]; }; diff --git a/components/zotero-service.js b/components/zotero-service.js @@ -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 **/