[Don't merge] General DOI search translator

A sort of proof-of-concept search translator for DOIs. The idea is to extend our coverage of DOI resolution via one centralized translator. The translator works, but it's not linked to any other translator currently, so testing it is a bit awkward.

This uses the content negotiation protocol and resolves DOIs through dx.doi.org. The protocol is currently supported by CrossRef and DataCite. The search then falls back to EIDR. I'll write up a translator for mEDRA, though they don't have a structured way of serving metadata yet (they say that they're working on something, but it's only available to registered users at the moment).

We could utilize dx.doi.org resources much more efficiently if there was a way for translators to get the HTTP response headers. Or, at the very least, the URL that we're being redirected to. Perhaps it's already possible. Any thoughts on this?

This also requires a small patch to the Zotero client to extend detectImport/detectSearch functionality to translators via getTranslators.I have a feeling Simon or Dan will have a good reason why this should not been done:
```
From fcbd2b9f469db7279a888ca92b23cb1923951b0f Mon Sep 17 00:00:00 2001
From: aurimasv <aurimas.dev@gmail.com>
Date: Thu, 6 Sep 2012 07:32:31 -0500
Subject: [PATCH] Extend detect(Import|Search) functionality to translators
 via getTranslators.

---
 .../content/zotero/xpcom/translation/translate.js  |   42 ++++++++++++--------
 1 file changed, 26 insertions(+), 16 deletions(-)

diff --git a/chrome/content/zotero/xpcom/translation/translate.js b/chrome/content/zotero/xpcom/translation/translate.js
index 839a066..dfc19f3 100644
--- a/chrome/content/zotero/xpcom/translation/translate.js
+++ b/chrome/content/zotero/xpcom/translation/translate.js
@@ -275,7 +275,7 @@ Zotero.Translate.Sandbox = {
 			};
 			
 			var translatorsHandlerSet = false;
-			safeTranslator.getTranslators = function() {
+			safeTranslator.getTranslators = function(getAllTranslators, checkSetTranslator) {
 				if(!translation._handlers["translators"] || !translation._handlers["translators"].length) {
 					if(Zotero.isConnector) {
 						throw new Error('Translator must register a "translators" handler to '+
@@ -291,7 +291,7 @@ Zotero.Translate.Sandbox = {
 					});
 				}
 				translate.incrementAsyncProcesses("safeTranslator#getTranslators()");
-				return translation.getTranslators();
+				return translation.getTranslators(getAllTranslators, checkSetTranslator);
 			};
 			
 			var doneHandlerSet = false;
@@ -925,13 +925,28 @@ Zotero.Translate.Base.prototype = {
 		this._getAllTranslators = getAllTranslators;
 
 		if(checkSetTranslator) {
-			// setTranslator must be called beforehand if checkSetTranslator is set
+			this._getTranslatorsTranslatorsReceived(this._getSetTranslators());
+		} else {
+			this._getTranslatorsGetPotentialTranslators();
+		}
+
+		// if detection returns immediately, return found translators
+		if(!this._currentState) return this._foundTranslators;
+	},
+
+  /**
+   * Get an array of translator objects for translator IDs set via setTranslator
+   * @return {Zotero.Translator[]}
+   */
+  "_getSetTranslators":function() {
+      // setTranslator must be called beforehand
 			if( !this.translator || !this.translator[0] ) {
 				throw new Error("getTranslators: translator must be set via setTranslator before calling" +
 													" getTranslators with the checkSetTranslator flag");
 			}
 			var translators = new Array();
 			var t;
+
 			for(var i=0, n=this.translator.length; i<n; i++) {
 				if(typeof(this.translator[i]) == 'string') {
 					t = Zotero.Translators.get(this.translator[i]);
@@ -943,15 +958,10 @@ Zotero.Translate.Base.prototype = {
 				if(t) translators.push(t);
 			}
 			if(!translators.length) throw new Error("getTranslators: no valid translators were set.");
-			this._getTranslatorsTranslatorsReceived(translators);
-		} else {
-			this._getTranslatorsGetPotentialTranslators();
-		}
 
-		// if detection returns immediately, return found translators
-		if(!this._currentState) return this._foundTranslators;
-	},
-	
+      return translators;
+  },
+
 	/**
 	 * Get all potential translators
 	 * @return {Zotero.Translator[]}
@@ -1777,8 +1787,8 @@ Zotero.Translate.Import.prototype._getTranslatorsGetPotentialTranslators = funct
  * Overload {@link Zotero.Translate.Base#getTranslators} to return all translators immediately only
  * if no string or location is set
  */
-Zotero.Translate.Import.prototype.getTranslators = function() {
-	if(!this._string && !this.location) {
+Zotero.Translate.Import.prototype.getTranslators = function(getAllTranslators, checkSetTranslator) {
+	if(!this._string && !this.location && !checkSetTranslator) {
 		if(this._currentState === "detect") throw new Error("getTranslators: detection is already running");
 		this._currentState = "detect";
 		var me = this;
@@ -1789,7 +1799,7 @@ Zotero.Translate.Import.prototype.getTranslators = function() {
 		});
 		if(this._currentState === null) return this._foundTranslators;
 	} else {
-		return Zotero.Translate.Base.prototype.getTranslators.call(this);
+		return Zotero.Translate.Base.prototype.getTranslators.call(this, getAllTranslators, checkSetTranslator);
 	}
 }
 	
@@ -2048,8 +2058,8 @@ Zotero.Translate.Search.prototype.setSearch = function(search) {
 /**
  * Overloads {@link Zotero.Translate.Base#getTranslators} to always return all potential translators
  */
-Zotero.Translate.Search.prototype.getTranslators = function() {
-	return Zotero.Translate.Base.prototype.getTranslators.call(this, true);
+Zotero.Translate.Search.prototype.getTranslators = function(getAllTranslators, checkSetTranslator) {
+	return Zotero.Translate.Base.prototype.getTranslators.call(this, true, checkSetTranslator);
 }
 
 /**
-- 
1.7.10.msysgit.1

```