www

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

utilities_translate.js (11575B)


      1 /*
      2     ***** BEGIN LICENSE BLOCK *****
      3     
      4     Copyright © 2009 Center for History and New Media
      5                      George Mason University, Fairfax, Virginia, USA
      6                      http://zotero.org
      7     
      8     This file is part of Zotero.
      9     
     10     Zotero is free software: you can redistribute it and/or modify
     11     it under the terms of the GNU Affero General Public License as published by
     12     the Free Software Foundation, either version 3 of the License, or
     13     (at your option) any later version.
     14     
     15     Zotero is distributed in the hope that it will be useful,
     16     but WITHOUT ANY WARRANTY; without even the implied warranty of
     17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     18     GNU Affero General Public License for more details.
     19     
     20     You should have received a copy of the GNU Affero General Public License
     21     along with Zotero.  If not, see <http://www.gnu.org/licenses/>.
     22     
     23 	
     24 	Utilities based in part on code taken from Piggy Bank 2.1.1 (BSD-licensed)
     25 	
     26     ***** END LICENSE BLOCK *****
     27 */
     28 
     29 /**
     30  * @class All functions accessible from within Zotero.Utilities namespace inside sandboxed
     31  * translators
     32  *
     33  * @constructor
     34  * @augments Zotero.Utilities
     35  * @borrows Zotero.Date.formatDate as this.formatDate
     36  * @borrows Zotero.Date.strToDate as this.strToDate
     37  * @borrows Zotero.Date.strToISO as this.strToISO
     38  * @borrows Zotero.OpenURL.createContextObject as this.createContextObject
     39  * @borrows Zotero.OpenURL.parseContextObject as this.parseContextObject
     40  * @borrows Zotero.HTTP.processDocuments as this.processDocuments
     41  * @borrows Zotero.HTTP.doPost as this.doPost
     42  * @param {Zotero.Translate} translate
     43  */
     44 Zotero.Utilities.Translate = function(translate) {
     45 	this._translate = translate;
     46 }
     47 
     48 var tmp = function() {};
     49 tmp.prototype = Zotero.Utilities;
     50 Zotero.Utilities.Translate.prototype = new tmp();
     51 
     52 Zotero.Utilities.Translate.prototype.formatDate = Zotero.Date.formatDate;
     53 Zotero.Utilities.Translate.prototype.strToDate = Zotero.Date.strToDate;
     54 Zotero.Utilities.Translate.prototype.strToISO = Zotero.Date.strToISO;
     55 Zotero.Utilities.Translate.prototype.createContextObject = Zotero.OpenURL.createContextObject;
     56 Zotero.Utilities.Translate.prototype.parseContextObject = Zotero.OpenURL.parseContextObject;
     57 
     58 /**
     59  * Hack to overloads {@link Zotero.Utilities.capitalizeTitle} to allow overriding capitalizeTitles 
     60  * pref on a per-translate instance basis (for translator testing only)
     61  */
     62 Zotero.Utilities.Translate.prototype.capitalizeTitle = function(string, force) {
     63 	if(force === undefined) {
     64 		var translate = this._translate;
     65 		do {
     66 			if(translate.capitalizeTitles !== undefined) {
     67 				force = translate.capitalizeTitles;
     68 				break;
     69 			}
     70 		} while(translate = translate._parentTranslator);
     71 	}
     72 	
     73 	return Zotero.Utilities.capitalizeTitle(string, force);
     74 }
     75 
     76 /**
     77  * Gets the current Zotero version
     78  *
     79  * @type String
     80  */
     81 Zotero.Utilities.Translate.prototype.getVersion = function() {
     82 	return Zotero.version;
     83 }
     84 
     85 /**
     86  * Takes an XPath query and returns the results
     87  *
     88  * @deprecated Use {@link Zotero.Utilities.xpath} or doc.evaluate() directly
     89  * @type Node[]
     90  */
     91 Zotero.Utilities.Translate.prototype.gatherElementsOnXPath = function(doc, parentNode, xpath, nsResolver) {
     92 	var elmts = [];
     93 	
     94 	var iterator = doc.evaluate(xpath, parentNode, nsResolver,
     95 		(Zotero.isFx ? Components.interfaces.nsIDOMXPathResult.ANY_TYPE : XPathResult.ANY_TYPE),
     96 		null);
     97 	var elmt = iterator.iterateNext();
     98 	var i = 0;
     99 	while (elmt) {
    100 		elmts[i++] = elmt;
    101 		elmt = iterator.iterateNext();
    102 	}
    103 	return elmts;
    104 }
    105 
    106 /**
    107  * Gets a given node as a string containing all child nodes
    108  *
    109  * @deprecated Use doc.evaluate and the "nodeValue" or "textContent" property
    110  * @type String
    111  */
    112 Zotero.Utilities.Translate.prototype.getNodeString = function(doc, contextNode, xpath, nsResolver) {
    113 	var elmts = this.gatherElementsOnXPath(doc, contextNode, xpath, nsResolver);
    114 	var returnVar = "";
    115 	for(var i=0; i<elmts.length; i++) {
    116 		returnVar += elmts[i].nodeValue;
    117 	}
    118 	return returnVar;
    119 }
    120 
    121 /**
    122  * Grabs items based on URLs
    123  *
    124  * @param {Document} doc DOM document object
    125  * @param {Element|Element[]} inHere DOM element(s) to process
    126  * @param {RegExp} [urlRe] Regexp of URLs to add to list
    127  * @param {RegExp} [urlRe] Regexp of URLs to reject
    128  * @return {Object} Associative array of link => textContent pairs, suitable for passing to
    129  *	Zotero.selectItems from within a translator
    130  */
    131 Zotero.Utilities.Translate.prototype.getItemArray = function(doc, inHere, urlRe, rejectRe) {
    132 	var availableItems = new Object();	// Technically, associative arrays are objects
    133 	
    134 	// Require link to match this
    135 	if(urlRe) {
    136 		if(urlRe.exec) {
    137 			var urlRegexp = urlRe;
    138 		} else {
    139 			var urlRegexp = new RegExp();
    140 			urlRegexp.compile(urlRe, "i");
    141 		}
    142 	}
    143 	// Do not allow text to match this
    144 	if(rejectRe) {
    145 		if(rejectRe.exec) {
    146 			var rejectRegexp = rejectRe;
    147 		} else {
    148 			var rejectRegexp = new RegExp();
    149 			rejectRegexp.compile(rejectRe, "i");
    150 		}
    151 	}
    152 	
    153 	if(!inHere.length) {
    154 		inHere = new Array(inHere);
    155 	}
    156 	
    157 	for(var j=0; j<inHere.length; j++) {
    158 		var links = inHere[j].getElementsByTagName("a");
    159 		for(var i=0; i<links.length; i++) {
    160 			if(!urlRe || urlRegexp.test(links[i].href)) {
    161 				var text = "textContent" in links[i] ? links[i].textContent : links[i].innerText;
    162 				if(text) {
    163 					text = this.trimInternal(text);
    164 					if(!rejectRe || !rejectRegexp.test(text)) {
    165 						if(availableItems[links[i].href]) {
    166 							if(text != availableItems[links[i].href]) {
    167 								availableItems[links[i].href] += " "+text;
    168 							}
    169 						} else {
    170 							availableItems[links[i].href] = text;
    171 						}
    172 					}
    173 				}
    174 			}
    175 		}
    176 	}
    177 	
    178 	return availableItems;
    179 }
    180 
    181 
    182 /**
    183  * Load a single document in a hidden browser
    184  *
    185  * @deprecated Use processDocuments with a single URL
    186  * @see Zotero.Utilities.Translate#processDocuments
    187  */
    188 Zotero.Utilities.Translate.prototype.loadDocument = function(url, succeeded, failed) {
    189 	Zotero.debug("Zotero.Utilities.loadDocument is deprecated; please use processDocuments in new code");
    190 	this.processDocuments([url], succeeded, null, failed);
    191 }
    192 
    193 /**
    194  * Already documented in Zotero.HTTP, except this optionally takes noCompleteOnError, which prevents
    195  * the translation process from being cancelled automatically on error, as it is normally. The promise
    196  * is still rejected on error for handling by the calling function.
    197  * @ignore
    198  */
    199 Zotero.Utilities.Translate.prototype.processDocuments = async function (urls, processor, noCompleteOnError) {
    200 	// Handle old signature: urls, processor, onDone, onError
    201 	if (arguments.length > 3 || typeof arguments[2] == 'function') {
    202 		Zotero.debug("ZU.processDocuments() now takes only 3 arguments -- update your code");
    203 		var onDone = arguments[2];
    204 		var onError = arguments[3];
    205 		noCompleteOnError = false;
    206 	}
    207 	
    208 	var translate = this._translate;
    209 
    210 	if (typeof urls == "string") {
    211 		urls = [translate.resolveURL(urls)];
    212 	} else {
    213 		for(var i in urls) {
    214 			urls[i] = translate.resolveURL(urls[i]);
    215 		}
    216 	}
    217 	
    218 	var processDoc = function (doc) {
    219 		if (Zotero.isFx) {
    220 			let newLoc = doc.location;
    221 			let url = Services.io.newURI(newLoc.href, null, null);
    222 			return processor(
    223 				// Rewrap document for the sandbox
    224 				translate._sandboxManager.wrap(
    225 					Zotero.Translate.DOMWrapper.unwrap(doc),
    226 					null,
    227 					// Duplicate overrides from Zotero.HTTP.wrapDocument()
    228 					{
    229 						documentURI: newLoc.spec,
    230 						URL: newLoc.spec,
    231 						location: new Zotero.HTTP.Location(url),
    232 						defaultView: new Zotero.HTTP.Window(url)
    233 					}
    234 				),
    235 				newLoc.href
    236 			);
    237 		}
    238 		
    239 		return processor(doc, doc.location.href);
    240 	};
    241 	
    242 	var funcs = [];
    243 	// If current URL passed, use loaded document instead of reloading
    244 	for (var i = 0; i < urls.length; i++) {
    245 		if(translate.document && translate.document.location
    246 				&& translate.document.location.toString() === urls[i]) {
    247 			Zotero.debug("Translate: Attempted to load the current document using processDocuments; using loaded document instead");
    248 			funcs.push(() => processDoc(this._translate.document, urls[i]));
    249 			urls.splice(i, 1);
    250 			i--;
    251 		}
    252 	}
    253 	
    254 	translate.incrementAsyncProcesses("Zotero.Utilities.Translate#processDocuments");
    255 	
    256 	if (urls.length) {
    257 		funcs.push(
    258 			() => Zotero.HTTP.processDocuments(
    259 				urls,
    260 				function (doc) {
    261 					if (!processor) return;
    262 					return processDoc(doc);
    263 				},
    264 				translate.cookieSandbox
    265 			)
    266 		);
    267 	}
    268 	
    269 	var f;
    270 	while (f = funcs.shift()) {
    271 		try {
    272 			let maybePromise = f();
    273 			// The processor may or may not return a promise
    274 			if (maybePromise) {
    275 				await maybePromise;
    276 			}
    277 		}
    278 		catch (e) {
    279 			if (onError) {
    280 				try {
    281 					onError(e);
    282 				}
    283 				catch (e) {
    284 					translate.complete(false, e);
    285 				}
    286 			}
    287 			// Unless instructed otherwise, end the translation on error
    288 			else if (!noCompleteOnError) {
    289 				translate.complete(false, e);
    290 			}
    291 			throw e;
    292 		}
    293 	}
    294 	
    295 	// Deprecated
    296 	if (onDone) {
    297 		onDone();
    298 	}
    299 	
    300 	translate.decrementAsyncProcesses("Zotero.Utilities.Translate#processDocuments");
    301 }
    302 
    303 /**
    304 * Send an HTTP GET request via XMLHTTPRequest
    305 * 
    306 * @param {String|String[]} urls URL(s) to request
    307 * @param {Function} processor Callback to be executed for each document loaded
    308 * @param {Function} done Callback to be executed after all documents have been loaded
    309 * @param {String} responseCharset Character set to force on the response
    310 * @param {Object} requestHeaders HTTP headers to include with request
    311 * @return {Boolean} True if the request was sent, or false if the browser is offline
    312 */
    313 Zotero.Utilities.Translate.prototype.doGet = function(urls, processor, done, responseCharset, requestHeaders) {
    314 	var callAgain = false,
    315 		me = this,
    316 		translate = this._translate;
    317 	
    318 	if(typeof(urls) == "string") {
    319 		var url = urls;
    320 	} else {
    321 		if(urls.length > 1) callAgain = true;
    322 		var url = urls.shift();
    323 	}
    324 	
    325 	url = translate.resolveURL(url);
    326 	
    327 	translate.incrementAsyncProcesses("Zotero.Utilities.Translate#doGet");
    328 	var xmlhttp = Zotero.HTTP.doGet(url, function(xmlhttp) {
    329 		try {
    330 			if(processor) {
    331 				processor(xmlhttp.responseText, xmlhttp, url);
    332 			}
    333 			
    334 			if(callAgain) {
    335 				me.doGet(urls, processor, done, responseCharset);
    336 			} else {
    337 				if(done) {
    338 					done();
    339 				}
    340 			}
    341 			translate.decrementAsyncProcesses("Zotero.Utilities.Translate#doGet");
    342 		} catch(e) {
    343 			translate.complete(false, e);
    344 		}
    345 	}, responseCharset, this._translate.cookieSandbox, requestHeaders);
    346 }
    347 
    348 /**
    349  * Already documented in Zotero.HTTP
    350  * @ignore
    351  */
    352 Zotero.Utilities.Translate.prototype.doPost = function(url, body, onDone, headers, responseCharset) {
    353 	var translate = this._translate;
    354 	url = translate.resolveURL(url);
    355 	
    356 	translate.incrementAsyncProcesses("Zotero.Utilities.Translate#doPost");
    357 	var xmlhttp = Zotero.HTTP.doPost(url, body, function(xmlhttp) {
    358 		try {
    359 			onDone(xmlhttp.responseText, xmlhttp);
    360 			translate.decrementAsyncProcesses("Zotero.Utilities.Translate#doPost");
    361 		} catch(e) {
    362 			translate.complete(false, e);
    363 		}
    364 	}, headers, responseCharset, translate.cookieSandbox ? translate.cookieSandbox : undefined);
    365 }
    366 
    367 Zotero.Utilities.Translate.prototype.urlToProxy = function(url) {
    368 	var proxy = this._translate._proxy;
    369 	if (proxy) return proxy.toProxy(url);
    370 	return url;
    371 };
    372 
    373 Zotero.Utilities.Translate.prototype.urlToProper = function(url) {
    374 	var proxy = this._translate._proxy;
    375 	if (proxy) return proxy.toProper(url);
    376 	return url;
    377 };
    378 
    379 Zotero.Utilities.Translate.prototype.__exposedProps__ = {"HTTP":"r"};
    380 for(var j in Zotero.Utilities.Translate.prototype) {
    381 	if(typeof Zotero.Utilities.Translate.prototype[j] === "function" && j[0] !== "_" && j != "Translate") {
    382 		Zotero.Utilities.Translate.prototype.__exposedProps__[j] = "r";
    383 	}
    384 }