Index: SRUAggregator/trunk/aggregator.yml
===================================================================
--- SRUAggregator/trunk/aggregator.yml	(revision 5976)
+++ SRUAggregator/trunk/aggregator.yml	(revision 6043)
@@ -1,12 +1,14 @@
 aggregatorParams:
-  CENTER_REGISTRY_URL: http://centres.clarin.eu/restxml/
- # additionalCQLEndpoints:
- #    - https://clarin.ids-mannheim.de/digibibsru-new
- #    - https://lux17.mpi.nl/ds/cqlsearch
- #    - http://www.meertens.knaw.nl/mimore/srucql/
- #    - http://gysseling.corpus.taalbanknederlands.inl.nl/gyssru/
- #    - http://brievenalsbuit.inl.nl/zbsru/
- #    - http://corpus3.aac.ac.at/ddconsru/
-#      - http://dspin.dwds.de:8088/ddc-sru/dta/
+  # CENTER_REGISTRY_URL: http://centres.clarin.eu/restxml/
+  additionalCQLEndpoints:
+    # - http://cqlservlet.mpi.nl/
+    - http://dspin.dwds.de:8088/ddc-sru/dingler/
+    - https://clarin.ids-mannheim.de/digibibsru-new
+    - https://lux17.mpi.nl/ds/cqlsearch
+    # - http://www.meertens.knaw.nl/mimore/srucql/
+    # - http://gysseling.corpus.taalbanknederlands.inl.nl/gyssru/
+    # - http://brievenalsbuit.inl.nl/zbsru/
+    # - http://corpus3.aac.ac.at/ddconsru/
+    #  - http://dspin.dwds.de:8088/ddc-sru/dta/
 
   # AGGREGATOR_FILE_PATH: /data/fcsAggregator/fcsAggregatorCorpora.json
@@ -41,5 +43,5 @@
   # Logger-specific levels.
   loggers:
-    eu.clarin.sru.fcs.aggregator: WARN
+    eu.clarin.sru.fcs.aggregator: INFO
     eu.clarin.sru.client: WARN
 
Index: SRUAggregator/trunk/pom.xml
===================================================================
--- SRUAggregator/trunk/pom.xml	(revision 5976)
+++ SRUAggregator/trunk/pom.xml	(revision 6043)
@@ -8,5 +8,5 @@
 	<groupId>eu.clarin.sru.fcs</groupId>
 	<artifactId>Aggregator2</artifactId>
-	<version>2.0.0-alpha-20</version>
+	<version>2.0.0-alpha-21</version>
 	<name>FCS Aggregator</name>
 
@@ -26,12 +26,4 @@
 			<url>http://oss.sonatype.org/content/repositories/snapshots</url>
 		</repository>
-		<repository>
-			<id>sardine-google-svn-repo</id>
-			<snapshots>
-				<enabled>true</enabled>
-			</snapshots>
-			<name>Sardine maven repo at Google Code</name>
-			<url>http://sardine.googlecode.com/svn/maven/</url>
-		</repository>
 	</repositories> 
 
@@ -63,4 +55,14 @@
 			<artifactId>sru-client</artifactId>
 			<version>0.9.5-DEBUG</version>
+			<exclusions>
+				<exclusion>
+					<groupId>org.slf4j</groupId>
+					<artifactId>slf4j-api</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>commons-codec</groupId>
+					<artifactId>commons-codec</artifactId>
+				</exclusion>
+			</exclusions>
 		</dependency>
 
@@ -74,4 +76,10 @@
 			<artifactId>connectors</artifactId>
 			<version>1.0.6</version>
+			<exclusions>
+				<exclusion>
+					<groupId>com.sun.jersey</groupId>
+					<artifactId>jersey-client</artifactId>
+				</exclusion>
+			</exclusions>
 		</dependency>
 		<dependency>
@@ -89,27 +97,23 @@
 		    <groupId>com.optimaize.languagedetector</groupId>
 		    <artifactId>language-detector</artifactId>
-		    <version>0.4</version>
+			<version>0.4</version>
+			<exclusions>
+				<exclusion>
+					<groupId>com.google.guava</groupId>
+					<artifactId>guava</artifactId>
+				</exclusion>
+			</exclusions>
 		</dependency>
 		
-		<dependency>
-			<groupId>com.googlecode.sardine</groupId>
-			<artifactId>sardine</artifactId>
-			<version>314</version>
-			<type>jar</type>
-			<exclusions>
-				<exclusion>
-					<groupId>org.apache.httpcomponents</groupId>
-					<artifactId>httpcore</artifactId>
-				</exclusion>
-				<exclusion>
-					<artifactId>commons-codec</artifactId>
-					<groupId>commons-codec</groupId>
-				</exclusion>
-			</exclusions>
-		</dependency>
 		<dependency>
 			<groupId>org.apache.poi</groupId>
 			<artifactId>poi-ooxml</artifactId>
 			<version>3.11-beta2</version>
+		</dependency>
+		<dependency>
+			<groupId>com.sun.jersey</groupId>
+			<artifactId>jersey-client</artifactId>
+			<version>1.18.1</version>
+			<type>jar</type>
 		</dependency>
 	</dependencies>
@@ -130,5 +134,5 @@
 						</configuration>
 						<goals>
-							<!--<goal>enforce</goal>-->
+							<goal>enforce</goal>
 						</goals>
 					</execution>
Index: SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/app/Aggregator.java
===================================================================
--- SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/app/Aggregator.java	(revision 5976)
+++ SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/app/Aggregator.java	(revision 6043)
@@ -88,21 +88,22 @@
  * @author edima
  *
- * TODO: Download search results as csv, excel, tcf, plain text
- *
  * TODO: Use weblicht with results
  *
- * TODO: Export search results to personal workspace as csv, excel, tcf, plain
- * text
- *
  * TODO: disable popups easily
- *
- * TODO: websockets
- *
+ * 
  * TODO: atomic replace of cached corpora (file)
- *
- * TODO: show multiple hits on the same result in multiple rows, linked visually
  *
  * TODO: zoom into the results from a corpus, allow functionality only for
  * the view (search for next set of results)
+ *
+ * TODO: fix search bug after going to stats
+ *
+ * TODO: Export search results to personal workspace as csv, excel, tcf, plain
+ * text: ask Marie/Wei about oauth ways to do that ndg oauth; ask Menzo, Willem,
+ * Twan (they did a test, it worked)
+ *
+ * TODO: websockets
+ *
+ * TODO: show multiple hits on the same result in multiple rows, linked visually
  *
  * TODO: optimise page load
Index: SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/lang/LanguagesISO693_3.java
===================================================================
--- SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/lang/LanguagesISO693_3.java	(revision 5976)
+++ SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/lang/LanguagesISO693_3.java	(revision 6043)
@@ -28,17 +28,17 @@
 	public static class Language {
 
-		// code is ISO-639-3 (3 letters) while code_2 is ISO-639-2 (2 letters)
-		String code, code_2, name;
+		// code is ISO-639-3 (3 letters) while code_2 is ISO-639-1 (2 letters)
+		String code_3, code_1, name;
 
-		public Language(String code, String code_2, String name) {
-			this.code = code;
-			this.code_2 = code_2;
+		public Language(String code_3, String code_1, String name) {
+			this.code_3 = code_3;
+			this.code_1 = code_1;
 			this.name = name;
 		}
 	}
 
-	private Map<String, Language> codeToLang = new HashMap<String, Language>();
+	private Map<String, Language> code_3ToLang = new HashMap<String, Language>();
 	private Map<String, Language> nameToLang = new HashMap<String, Language>();
-	private Map<String, Language> code_2ToLang = new HashMap<String, Language>();
+	private Map<String, Language> code_1ToLang = new HashMap<String, Language>();
 
 	private LanguagesISO693_3() {
@@ -53,14 +53,14 @@
 						continue;
 					}
-					String code = toks[0].trim();
-					String code_2 = toks[3].trim().isEmpty() ? null : toks[3].trim();
-					if (code_2 != null && code_2.length() != 2) {
-						throw new RuntimeException("bad code_2 code: " + code_2);
+					String code_3 = toks[0].trim();
+					String code_1 = toks[3].trim().isEmpty() ? null : toks[3].trim();
+					if (code_1 != null && code_1.length() != 2) {
+						throw new RuntimeException("bad ISO-639-1 code: " + code_1);
 					}
 					String name = toks[6].trim();
-					Language l = new Language(code, code_2, name);
-					codeToLang.put(code, l);
-					if (code_2 != null) {
-						code_2ToLang.put(code_2, l);
+					Language l = new Language(code_3, code_1, name);
+					code_3ToLang.put(code_3, l);
+					if (code_1 != null) {
+						code_1ToLang.put(code_1, l);
 					}
 					nameToLang.put(name, l);
@@ -73,5 +73,5 @@
 		ObjectWriter ow = new ObjectMapper().writerWithDefaultPrettyPrinter();
 		try {
-			System.out.println(ow.writeValueAsString(codeToLang));
+			System.out.println(ow.writeValueAsString(code_3ToLang));
 		} catch (JsonProcessingException ex) {
 		}
@@ -85,21 +85,33 @@
 	}
 
-	public Set<String> getCodes() {
-		return codeToLang.keySet();
+	public Set<String> getCodes_3() {
+		return code_3ToLang.keySet();
 	}
 
-	public String codeForCode639_2(String code639_2) {
-		if (code639_2 == null) {
+	public String code_3ForCode_1(String code639_1) {
+		if (code639_1 == null) {
 			return null;
 		}
-		Language l = code_2ToLang.get(code639_2);
+		Language l = code_1ToLang.get(code639_1);
 		if (l == null) {
-			log.error("Unknown 639-2 code: " + code639_2);
+			log.error("Unknown ISO-639-1 code: " + code639_1);
 			return null;
 		}
-		return l.code;
+		return l.code_3;
 	}
 
-	public String codeForName(String name) {
+	public String code_1ForCode_3(String code639_3) {
+		if (code639_3 == null) {
+			return null;
+		}
+		Language l = code_3ToLang.get(code639_3);
+		if (l == null) {
+			log.error("Unknown ISO-639-3 code: " + code639_3);
+			return null;
+		}
+		return l.code_1;
+	}
+
+	public String code_3ForName(String name) {
 		Language l = nameToLang.get(name);
 		if (l == null) {
@@ -107,9 +119,9 @@
 			return null;
 		}
-		return l.code;
+		return l.code_3;
 	}
 
-	public String nameForCode(String code) {
-		Language l = codeToLang.get(code);
+	public String nameForCode_3(String code) {
+		Language l = code_3ToLang.get(code);
 		if (l == null) {
 			log.error("Unknown language code: " + code);
Index: SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/rest/DataTransfer.java
===================================================================
--- SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/rest/DataTransfer.java	(revision 6043)
+++ SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/rest/DataTransfer.java	(revision 6043)
@@ -0,0 +1,60 @@
+package eu.clarin.sru.fcs.aggregator.rest;
+
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.config.ClientConfig;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+import java.util.logging.*;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Random;
+
+/**
+ * @author Yana Panchenko
+ * @author edima
+ */
+public class DataTransfer {
+
+	private static final Logger LOGGER = Logger.getLogger(DataTransfer.class.getName());
+
+	private static final String WSPACE_SERVER_URL = "http://egi-cloud21.zam.kfa-juelich.de";
+	private static final String WSPACE_WEBDAV_DIR = "/owncloud/remote.php/webdav/";
+	private static final String WSPACE_AGGREGATOR_DIR = "aggregator_results/";
+	private static final String DROP_OFF_URL = "http://ws1-clarind.esc.rzg.mpg.de/drop-off/storage/";
+
+	static String uploadToDropOff(byte[] bytes, String mimeType, String fileExtention) {
+		Client client = null;
+		String url = null;
+		try {
+			Date currentDate = new Date();
+			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS");
+			Random generator = new Random();
+			int rn1 = generator.nextInt(1000000000);
+			String createdFileName = format.format(currentDate) + "-" + rn1 + fileExtention;
+
+			ClientConfig config = new DefaultClientConfig();
+			client = Client.create(config);
+			url = DROP_OFF_URL + createdFileName;
+			WebResource service = client.resource(url);
+
+			ClientResponse response = service.type(mimeType) //.accept(MediaType.TEXT_PLAIN).post(String.class, media.getStringData());
+					.post(ClientResponse.class, bytes);
+			if (response.getClientResponseStatus() != ClientResponse.Status.CREATED) {
+				LOGGER.log(Level.SEVERE, "Error uploading {0}", new String[]{url});
+				//"Sorry, export to drop-off error!"
+				return null;
+			}
+		} catch (Exception ex) {
+			LOGGER.log(Level.SEVERE, "Error uploading {0} {1} {2}", new String[]{url, ex.getClass().getName(), ex.getMessage()});
+			//"Sorry, export to drop-off error!"
+			return null;
+		} finally {
+			if (client != null) {
+				client.destroy();
+			}
+		}
+		return url;
+
+	}
+}
Index: SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/rest/RestService.java
===================================================================
--- SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/rest/RestService.java	(revision 5976)
+++ SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/rest/RestService.java	(revision 6043)
@@ -13,4 +13,5 @@
 import eu.clarin.sru.fcs.aggregator.search.Search;
 import eu.clarin.sru.fcs.aggregator.lang.LanguagesISO693_3;
+import eu.clarin.sru.fcs.aggregator.search.ExportException;
 import eu.clarin.sru.fcs.aggregator.search.Exports;
 import java.io.IOException;
@@ -21,4 +22,6 @@
 import java.util.Map;
 import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
@@ -33,4 +36,5 @@
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+import opennlp.tools.tokenize.TokenizerModel;
 import org.slf4j.LoggerFactory;
 
@@ -42,7 +46,9 @@
 @Path("/")
 public class RestService {
+
 	private static final String EXPORT_FILENAME_PREFIX = "ClarinDFederatedContentSearch-";
 	private static final String TCF_MEDIA_TYPE = "text/tcf+xml";
 	private static final String EXCEL_MEDIA_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
+	private static final String SEARCH_RESULTS_ENCODING = "UTF-8";
 
 	private static final org.slf4j.Logger log = LoggerFactory.getLogger(RestService.class);
@@ -82,4 +88,5 @@
 						put("isScan", true);
 						put("institutions", scan.getInstitutions());
+						put("date", scan.getDate());
 					}
 				});
@@ -91,4 +98,5 @@
 						put("isScan", false);
 						put("institutions", search.getInstitutions());
+						put("date", scan.getDate());
 					}
 				});
@@ -105,5 +113,5 @@
 		log.info("get language codes", codes);
 		for (String code : codes) {
-			String name = LanguagesISO693_3.getInstance().nameForCode(code);
+			String name = LanguagesISO693_3.getInstance().nameForCode_3(code);
 			languages.put(code, name != null ? name : code);
 		}
@@ -184,5 +192,5 @@
 		}
 
-		if (format == null || format.trim().isEmpty() || format.trim().equals("text")) {
+	if (format == null || format.trim().isEmpty() || format.trim().equals("text")) {
 			String text = Exports.getExportText(search.getResults());
 			return download(text, MediaType.TEXT_PLAIN, search.getQuery() + ".txt");
@@ -215,3 +223,34 @@
 				.build();
 	}
+
+	@GET
+	@Path("search/{id}/toWeblicht")
+	public Response sendSearchResultsToWeblicht(@PathParam("id") Long searchId,
+			@QueryParam("format") String format) throws Exception {
+		Search search = Aggregator.getInstance().getSearchById(searchId);
+		if (search == null) {
+			return Response.status(Response.Status.NOT_FOUND).entity("Search job not found").build();
+		}
+
+		String url = null;
+		if (format == null || format.isEmpty() || format.trim().equals("text")) {
+			String text = Exports.getExportText(search.getResults());
+			if (text != null) {
+				byte[] bytes = text.getBytes(SEARCH_RESULTS_ENCODING);
+				url = DataTransfer.uploadToDropOff(bytes, "text/plan", ".txt");
+			}
+		} else if (format.equals("tokens")) {
+			byte[] bytes = Exports.getExportTokenizedTCF(
+					search.getResults(), search.getSearchLanguage(),
+					Aggregator.getInstance().getTokenizerModel());
+			if (bytes != null) {
+				url = DataTransfer.uploadToDropOff(bytes, "text/tcf+xml", ".tcf");
+			}
+		} else {
+			return Response.status(400).entity("incorrect format parameter").build();
+		}
+
+		return url == null ? Response.status(503).entity("error while exporting to weblicht").build()
+				: Response.ok().entity(url).build();
+	}
 }
Index: SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/scan/Corpus.java
===================================================================
--- SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/scan/Corpus.java	(revision 5976)
+++ SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/scan/Corpus.java	(revision 6043)
@@ -102,8 +102,8 @@
 
 	public void addLanguage(String language) {
-		if (LanguagesISO693_3.getInstance().getCodes().contains(language)) {
+		if (LanguagesISO693_3.getInstance().getCodes_3().contains(language)) {
 			this.languages.add(language);
 		} else {
-			String code = LanguagesISO693_3.getInstance().codeForName(language);
+			String code = LanguagesISO693_3.getInstance().code_3ForName(language);
 			this.languages.add(code == null ? language : code);
 		}
Index: SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/scan/ScanCrawler.java
===================================================================
--- SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/scan/ScanCrawler.java	(revision 5976)
+++ SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/scan/ScanCrawler.java	(revision 6043)
@@ -368,31 +368,27 @@
 				} else if (infoNode.getNodeType() == Node.ELEMENT_NODE && infoNode.getLocalName().equals("Title")) {
 					Element element = (Element) infoNode;
-					String descr = infoNode.getTextContent().replaceAll("&lt;br/&gt;", " ");
-					descr = descr.replaceAll("<br/>", " ");
-					descr = descr.replaceAll("[\t\n\r ]+", " ");
-					c.setTitle(descr.trim());
+					String x = cleanup(infoNode.getTextContent());
+					if (!x.isEmpty()) {
+						c.setTitle(x);
+						if ("en".equals(element.getAttribute("xml:lang"))) {
+							enTitle = x;
+						}
+					}
+				} else if (infoNode.getNodeType() == Node.ELEMENT_NODE && infoNode.getLocalName().equals("Description")) {
+					Element element = (Element) infoNode;
+					String x = cleanup(infoNode.getTextContent());
+					c.setDescription(x);
 					//String lang = element.getAttributeNS("http://clarin.eu/fcs/1.0/resource-info", "lang");
 					//System.out.println("ATTRIBUTE LANG: " + lang);
 					if ("en".equals(element.getAttribute("xml:lang"))) {
-						enTitle = c.getDescription();
-					}
-				} else if (infoNode.getNodeType() == Node.ELEMENT_NODE && infoNode.getLocalName().equals("Description")) {
-					Element element = (Element) infoNode;
-					String descr = infoNode.getTextContent().replaceAll("&lt;br/&gt;", " ");
-					descr = descr.replaceAll("<br/>", " ");
-					descr = descr.replaceAll("[\t\n\r ]+", " ");
-					c.setDescription(descr.trim());
-					//String lang = element.getAttributeNS("http://clarin.eu/fcs/1.0/resource-info", "lang");
-					//System.out.println("ATTRIBUTE LANG: " + lang);
-					if ("en".equals(element.getAttribute("xml:lang"))) {
-						enDescription = c.getDescription();
-					}
-				}
-			}
-			// title in Engish has priority
+						enDescription = x;
+					}
+				}
+			}
+			// title in English has priority
 			if (enTitle != null && !enTitle.isEmpty()) {
 				c.setTitle(enTitle);
 			}
-			// description in Engish has priority
+			// description in English has priority
 			if (enDescription != null && !enDescription.isEmpty()) {
 				c.setDescription(enDescription);
@@ -400,3 +396,13 @@
 		}
 	}
+
+	private static String cleanup(String x) {
+		if (x == null) {
+			return "";
+		}
+		x = x.replaceAll("&lt;br/&gt;", " ");
+		x = x.replaceAll("<br/>", " ");
+		x = x.replaceAll("[\t\n\r ]+", " ");
+		return x.trim();
+	}
 }
Index: SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/scan/Statistics.java
===================================================================
--- SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/scan/Statistics.java	(revision 5976)
+++ SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/scan/Statistics.java	(revision 6043)
@@ -4,4 +4,5 @@
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
@@ -109,4 +110,10 @@
 	private final Object lock = new Object();
 
+	Date date = new Date();
+
+	public Date getDate() {
+		return date;
+	}
+
 	// institution to endpoint to statistics_per_endpoint map
 	Map<String, Map<String, EndpointStats>> institutions
@@ -186,3 +193,4 @@
 		return stats;
 	}
+
 }
Index: SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/search/DataTransfer.java
===================================================================
--- SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/search/DataTransfer.java	(revision 5976)
+++ 	(revision )
@@ -1,92 +1,0 @@
-package eu.clarin.sru.fcs.aggregator.search;
-
-import com.googlecode.sardine.Sardine;
-import com.googlecode.sardine.SardineFactory;
-import com.googlecode.sardine.impl.SardineException;
-import com.sun.jersey.api.client.Client;
-import com.sun.jersey.api.client.ClientResponse;
-import com.sun.jersey.api.client.WebResource;
-import com.sun.jersey.api.client.config.ClientConfig;
-import com.sun.jersey.api.client.config.DefaultClientConfig;
-import java.util.logging.*;
-import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Random;
-
-/**
- * @author Yana Panchenko
- * @author edima
- */
-public class DataTransfer {
-
-	private static final Logger LOGGER = Logger.getLogger(DataTransfer.class.getName());
-
-	private static final String WSPACE_SERVER_URL = "http://egi-cloud21.zam.kfa-juelich.de";
-	private static final String WSPACE_WEBDAV_DIR = "/owncloud/remote.php/webdav/";
-	private static final String WSPACE_AGGREGATOR_DIR = "aggregator_results/";
-	private static final String DROP_OFF_URL = "http://ws1-clarind.esc.rzg.mpg.de/drop-off/storage/";
-
-	static void uploadToPW(String user, String pass, byte[] bytes, String mimeType, String fileExtention) {
-		try {
-			Sardine sardine = SardineFactory.begin();
-			sardine.setCredentials(user, pass);
-			String outputDir = WSPACE_SERVER_URL + WSPACE_WEBDAV_DIR + WSPACE_AGGREGATOR_DIR;
-			if (!sardine.exists(outputDir)) {
-				sardine.createDirectory(outputDir);
-			}
-			Date currentDate = new Date();
-			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS");
-			Random generator = new Random();
-			int rn1 = generator.nextInt(1000000000);
-			String createdFilePath = outputDir + format.format(currentDate) + "-" + rn1 + fileExtention;
-			while (sardine.exists(createdFilePath)) {
-				rn1 = generator.nextInt(1000000000);
-				createdFilePath = outputDir + format.format(currentDate) + "-" + rn1 + fileExtention;
-			}
-			sardine.put(createdFilePath, bytes, mimeType);
-			// "Export complete!\nCreated file:\n" + createdFilePath
-		} catch (SardineException ex) {
-			LOGGER.log(Level.SEVERE, "Error accessing " + WSPACE_SERVER_URL + WSPACE_WEBDAV_DIR, ex);
-			//"Wrong name or password!"
-		} catch (IOException ex) {
-			LOGGER.log(Level.SEVERE, "Error exporting {0} {1} {2}", new String[]{fileExtention, ex.getClass().getName(), ex.getMessage()});
-			//"Sorry, export error!"
-		}
-	}
-
-	static String uploadToDropOff(byte[] bytes, String mimeType, String fileExtention) {
-		Client client = null;
-		String url = null;
-		try {
-			Date currentDate = new Date();
-			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS");
-			Random generator = new Random();
-			int rn1 = generator.nextInt(1000000000);
-			String createdFileName = format.format(currentDate) + "-" + rn1 + fileExtention;
-
-			ClientConfig config = new DefaultClientConfig();
-			client = Client.create(config);
-			url = DROP_OFF_URL + createdFileName;
-			WebResource service = client.resource(url);
-
-			ClientResponse response = service.type(mimeType) //.accept(MediaType.TEXT_PLAIN).post(String.class, media.getStringData());
-					.post(ClientResponse.class, bytes);
-			if (response.getClientResponseStatus() != ClientResponse.Status.CREATED) {
-				LOGGER.log(Level.SEVERE, "Error uploading {0}", new String[]{url});
-				//"Sorry, export to drop-off error!"
-				return null;
-			}
-		} catch (Exception ex) {
-			LOGGER.log(Level.SEVERE, "Error uploading {0} {1} {2}", new String[]{url, ex.getClass().getName(), ex.getMessage()});
-			//"Sorry, export to drop-off error!"
-			return null;
-		} finally {
-			if (client != null) {
-				client.destroy();
-			}
-		}
-		return url;
-
-	}
-}
Index: SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/search/ExportException.java
===================================================================
--- SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/search/ExportException.java	(revision 5976)
+++ SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/search/ExportException.java	(revision 6043)
@@ -11,3 +11,6 @@
 	}
 
+	public ExportException(String message) {
+		super(message);
+	}
 }
Index: SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/search/Exports.java
===================================================================
--- SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/search/Exports.java	(revision 5976)
+++ SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/search/Exports.java	(revision 6043)
@@ -2,4 +2,5 @@
 
 import eu.clarin.sru.fcs.aggregator.lang.LanguagesISO693_2;
+import eu.clarin.sru.fcs.aggregator.lang.LanguagesISO693_3;
 import eu.clarin.weblicht.wlfxb.io.WLDObjector;
 import eu.clarin.weblicht.wlfxb.io.WLFormatException;
@@ -12,4 +13,5 @@
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -225,15 +227,16 @@
 			String searchLanguage, TokenizerModel tokenizerModel) throws ExportException {
         StringBuilder text = new StringBuilder();
-        Set<String> resultsLangs = new HashSet<String>();
-        if (resultsProcessed != null && !resultsProcessed.isEmpty()) {
-            for (Result result : resultsProcessed) {
-                resultsLangs.addAll(result.getCorpus().getLanguages());
-                for (Kwic kwic : result.getKwics()) {
-                    text.append(kwic.getLeft());
-                    text.append(" ");
-                    text.append(kwic.getKeyword());
-                    text.append(" ");
-                    text.append(kwic.getRight());
-                    text.append("\n");
+        if (resultsProcessed != null && !resultsProcessed.isEmpty()) {
+			for (Result result : resultsProcessed) {
+				for (Kwic kwic : result.getKwics()) {
+					int i = kwic.getFragments().size() - 1;
+					for (Kwic.TextFragment tf : kwic.getFragments()) {
+						text.append(tf.text);
+						if (i > 0) {
+							text.append(" ");
+						}
+						i--;
+					}
+					text.append("\n");
                 }
             }
@@ -244,21 +247,7 @@
         } else {
             WLData data;
-            MetaData md = new MetaData();
-            String resultsLang = "unknown";
-            if (resultsLangs.size() == 1) {
-                resultsLang = resultsLangs.iterator().next();
-				String code2 = LanguagesISO693_2.getInstance().langForCode(resultsLang).getCode_639_1();
-                if (code2 != null) {
-                    resultsLang = code2;
-                }
-            } else if (!searchLanguage.equals("anylang")) {
-				String code2 = LanguagesISO693_2.getInstance().langForCode(resultsLang).getCode_639_1();
-                if (code2 == null) {
-                    resultsLang = searchLanguage;
-                } else {
-                    resultsLang = code2;
-                }
-            }
-            TextCorpusStored tc = new TextCorpusStored(resultsLang);
+			MetaData md = new MetaData();
+			String languageCode = LanguagesISO693_3.getInstance().code_1ForCode_3(searchLanguage);
+			TextCorpusStored tc = new TextCorpusStored(languageCode);
             tc.createTextLayer().addText(text.toString());
 			addTokensSentencesMatches(resultsProcessed, tc, tokenizerModel);
@@ -276,5 +265,5 @@
 
 	private static void addTokensSentencesMatches(List<Result> resultsProcessed, TextCorpusStored tc, TokenizerModel model) {
-        if (model == null || !tc.getLanguage().equals("de")) {
+		if (model == null || !"de".equals(tc.getLanguage())) {
             return;
         }
Index: SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/search/Kwic.java
===================================================================
--- SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/search/Kwic.java	(revision 5976)
+++ SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/search/Kwic.java	(revision 6043)
@@ -64,7 +64,7 @@
 		}
 
-		String code_iso639_2 = Aggregator.getInstance().detectLanguage(hits.getText());
-		language = code_iso639_2 == null ? null
-				: LanguagesISO693_3.getInstance().codeForCode639_2(code_iso639_2);
+		String code_iso639_1 = Aggregator.getInstance().detectLanguage(hits.getText());
+		language = code_iso639_1 == null ? null
+				: LanguagesISO693_3.getInstance().code_3ForCode_1(code_iso639_1);
 	}
 
Index: SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/search/Search.java
===================================================================
--- SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/search/Search.java	(revision 5976)
+++ SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/search/Search.java	(revision 6043)
@@ -17,7 +17,4 @@
 import java.util.Random;
 import java.util.concurrent.atomic.AtomicLong;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import opennlp.tools.tokenize.TokenizerModel;
 import org.slf4j.LoggerFactory;
 
@@ -68,5 +65,5 @@
 				? ClarinFCSRecordData.LEGACY_RECORD_SCHEMA
 				: ClarinFCSRecordData.RECORD_SCHEMA);
-		searchRequest.setQuery("\"" + searchString + "\"");
+		searchRequest.setQuery(searchString);
 		searchRequest.setStartRecord(startRecord);
 		if (corpus.getHandle() != null) {
@@ -149,63 +146,4 @@
 	}
 
-	private void exportPWText(String user, String pass) {
-		byte[] bytes = null;
-		try {
-			String text = Exports.getExportText(results);
-			if (text != null) {
-				bytes = text.getBytes(SEARCH_RESULTS_ENCODING);
-			}
-		} catch (Exception ex) {
-			Logger.getLogger(Search.class.getName()).log(Level.SEVERE, null, ex);
-		}
-		if (bytes != null) {
-			DataTransfer.uploadToPW(user, pass, bytes, "text/plan", ".txt");
-		}
-	}
-
-	private String useWebLichtOnText() {
-		String url = null;
-		try {
-			String text = Exports.getExportText(results);
-			if (text != null) {
-				byte[] bytes = text.getBytes(SEARCH_RESULTS_ENCODING);
-				url = DataTransfer.uploadToDropOff(bytes, "text/plan", ".txt");
-			}
-		} catch (Exception ex) {
-			Logger.getLogger(Search.class.getName()).log(Level.SEVERE, null, ex);
-		}
-		return url;
-	}
-
-	private String useWebLichtOnToks(TokenizerModel tokenizerModel) throws ExportException {
-		String url = null;
-		byte[] bytes = Exports.getExportTokenizedTCF(results, searchLanguage, tokenizerModel);
-		if (bytes != null) {
-			url = DataTransfer.uploadToDropOff(bytes, "text/tcf+xml", ".tcf");
-		}
-		return url;
-	}
-
-	private void exportPWExcel(String user, String pass) throws ExportException {
-		byte[] bytes = Exports.getExportExcel(results);
-		if (bytes != null) {
-			DataTransfer.uploadToPW(user, pass, bytes, "application/vnd.ms-excel", ".xls");
-		}
-	}
-
-	private void exportPWTCF(String user, String pass, TokenizerModel tokenizerModel) throws ExportException {
-		byte[] bytes = Exports.getExportTokenizedTCF(results, searchLanguage, tokenizerModel);
-		if (bytes != null) {
-			DataTransfer.uploadToPW(user, pass, bytes, "text/tcf+xml", ".tcf");
-		}
-	}
-
-	private void exportPWCSV(String user, String pass) {
-		String csv = Exports.getExportCSV(results, ";");
-		if (csv != null) {
-			DataTransfer.uploadToPW(user, pass, csv.getBytes(), "text/csv", ".csv");
-		}
-	}
-
 	public void shutdown() {
 		// nothing to do 
Index: SRUAggregator/trunk/src/main/resources/assets/js/corpora.js
===================================================================
--- SRUAggregator/trunk/src/main/resources/assets/js/corpora.js	(revision 5976)
+++ SRUAggregator/trunk/src/main/resources/assets/js/corpora.js	(revision 6043)
@@ -12,40 +12,4 @@
 
 /////////////////////////////////
-
-var SearchCorpusBox = React.createClass({displayName: 'SearchCorpusBox',
-	propTypes: {
-		search: PT.func.isRequired,
-	},
-
-	getInitialState: function () {
-		return {
-			query: ""
-		};
-	},
-
-	handleChange: function(event) {
-		var query = event.target.value;
-		this.setState({query: query});
-
-		if (query.length === 0 || 2 <= query.length) {
-			this.props.search(query);
-		}
-		event.stopPropagation();
-	},
-
-	handleKey: function(event) {
-		if (event.keyCode==13) {
-			this.props.search(event.target.value);
-		}
-	},
-
-	render: function() {
-		return 	React.createElement("div", {className: "form-group"}, 
-					React.createElement("input", {className: "form-control search search-collection", type: "text", 
-						value: this.state.query, placeholder: "Search for collection", 
-						onChange: this.handleChange})
-				);
-	}
-});
 
 var CorpusView = window.MyAggregator.CorpusView = React.createClass({displayName: 'CorpusView',
@@ -281,3 +245,39 @@
 });
 
+var SearchCorpusBox = React.createClass({displayName: 'SearchCorpusBox',
+	propTypes: {
+		search: PT.func.isRequired,
+	},
+
+	getInitialState: function () {
+		return {
+			query: ""
+		};
+	},
+
+	handleChange: function(event) {
+		var query = event.target.value;
+		this.setState({query: query});
+
+		if (query.length === 0 || 2 <= query.length) {
+			this.props.search(query);
+		}
+		event.stopPropagation();
+	},
+
+	handleKey: function(event) {
+		if (event.keyCode==13) {
+			this.props.search(event.target.value);
+		}
+	},
+
+	render: function() {
+		return 	React.createElement("div", {className: "form-group"}, 
+					React.createElement("input", {className: "form-control search search-collection", type: "text", 
+						value: this.state.query, placeholder: "Search for collection", 
+						onChange: this.handleChange})
+				);
+	}
+});
+
 })();
Index: SRUAggregator/trunk/src/main/resources/assets/js/corpora.jsx
===================================================================
--- SRUAggregator/trunk/src/main/resources/assets/js/corpora.jsx	(revision 5976)
+++ SRUAggregator/trunk/src/main/resources/assets/js/corpora.jsx	(revision 6043)
@@ -12,40 +12,4 @@
 
 /////////////////////////////////
-
-var SearchCorpusBox = React.createClass({
-	propTypes: {
-		search: PT.func.isRequired,
-	},
-
-	getInitialState: function () {
-		return {
-			query: ""
-		};
-	},
-
-	handleChange: function(event) {
-		var query = event.target.value;
-		this.setState({query: query});
-
-		if (query.length === 0 || 2 <= query.length) {
-			this.props.search(query);
-		}
-		event.stopPropagation();
-	},
-
-	handleKey: function(event) {
-		if (event.keyCode==13) {
-			this.props.search(event.target.value);
-		}
-	},
-
-	render: function() {
-		return 	<div className="form-group">
-					<input className="form-control search search-collection" type="text" 
-						value={this.state.query} placeholder="Search for collection" 
-						onChange={this.handleChange} />
-				</div>;
-	}
-});
 
 var CorpusView = window.MyAggregator.CorpusView = React.createClass({
@@ -281,3 +245,39 @@
 });
 
+var SearchCorpusBox = React.createClass({
+	propTypes: {
+		search: PT.func.isRequired,
+	},
+
+	getInitialState: function () {
+		return {
+			query: ""
+		};
+	},
+
+	handleChange: function(event) {
+		var query = event.target.value;
+		this.setState({query: query});
+
+		if (query.length === 0 || 2 <= query.length) {
+			this.props.search(query);
+		}
+		event.stopPropagation();
+	},
+
+	handleKey: function(event) {
+		if (event.keyCode==13) {
+			this.props.search(event.target.value);
+		}
+	},
+
+	render: function() {
+		return 	<div className="form-group">
+					<input className="form-control search search-collection" type="text" 
+						value={this.state.query} placeholder="Search for collection" 
+						onChange={this.handleChange} />
+				</div>;
+	}
+});
+
 })();
Index: SRUAggregator/trunk/src/main/resources/assets/js/main.js
===================================================================
--- SRUAggregator/trunk/src/main/resources/assets/js/main.js	(revision 5976)
+++ SRUAggregator/trunk/src/main/resources/assets/js/main.js	(revision 6043)
@@ -3,5 +3,5 @@
 "use strict";
 
-var VERSION = "VERSION 2.0.0.Î±20";
+var VERSION = "VERSION 2.0.0.Î±21";
 var URLROOT = "/Aggregator-testing";
 
@@ -13,5 +13,5 @@
 var Main = React.createClass({displayName: 'Main',
 	componentWillMount: function() {
-		routeFromLocation(this);
+		routeFromLocation.bind(this);
 	},
 
@@ -102,5 +102,5 @@
 			}
 			this.setState({navbarPageFn: pageFn});
-			console.log("new page: " + document.location + ", name: " + pageFnName);
+			// console.log("new page: " + document.location + ", name: " + pageFnName);
 		}
 	},
@@ -193,5 +193,5 @@
 			success: function(json, textStatus, jqXHR) {
 				this.setState({stats: json});
-				console.log("stats:", json);
+				// console.log("stats:", json);
 			}.bind(this),
 		});
@@ -294,5 +294,5 @@
 
 	renderInstitution: function(isScan, inst) {
-		return 	React.createElement("div", {style: {marginBottom:30}, key: inst[0]}, 
+		return 	React.createElement("div", {style: {marginTop:30}, key: inst[0]}, 
 					React.createElement("h4", null, inst[0]), 
 					React.createElement("div", {style: {marginLeft:20}}, " ", _.pairs(inst[1]).map(this.renderEndpoint.bind(this, isScan)) )
@@ -302,16 +302,16 @@
 	renderStatistics: function(stats) {
 		return 	React.createElement("div", {className: "container statistics", style: {marginTop:20}}, 
-					React.createElement("ul", {className: "list-inline list-unstyled"}, 
-						 stats.maxConcurrentScanRequestsPerEndpoint ? 
-							React.createElement("li", null, "max concurrent scan requests per endpoint:", " ", 
-								React.createElement("kbd", null, stats.maxConcurrentScanRequestsPerEndpoint), ","
-							) : false, 
+					React.createElement("div", null, 
+						React.createElement("div", null, "Start date: ", new Date(stats.date).toLocaleString()), 
+						 stats.isScan ? 
+							React.createElement("div", null, "Max concurrent scan requests per endpoint:", " ", 
+								React.createElement("kbd", null, stats.maxConcurrentScanRequestsPerEndpoint)
+							) 
+							: 
+							React.createElement("div", null, "Max concurrent search requests per endpoint:", " ", 
+								React.createElement("kbd", null, stats.maxConcurrentSearchRequestsPerEndpoint)
+							), 
 						
-						 stats.maxConcurrentSearchRequestsPerEndpoint ? 
-							React.createElement("li", null, "max concurrent search requests per endpoint:", " ", 
-								React.createElement("kbd", null, stats.maxConcurrentSearchRequestsPerEndpoint), ","
-							) : false, 
-						
-						React.createElement("li", null, "timeout:", " ", React.createElement("kbd", null, stats.timeout, " seconds"))
+						React.createElement("div", null, "Timeout: ", " ", React.createElement("kbd", null, stats.timeout, " seconds"))
 					), 
 					React.createElement("div", null, " ",  _.pairs(stats.institutions).map(this.renderInstitution.bind(this, stats.isScan)), " ")
@@ -508,19 +508,21 @@
 }
 
-var routeFromLocation = function(com) {
+var routeFromLocation = function() {
+	// console.log("routeFromLocation: " + document.location);
+	if (!this) throw "routeFromLocation must be bound to main";
 	var path = window.location.pathname.split('/');
 	if (path.length === 3) {
 		var p = path[2];
 		if (p === 'help') {
-			com.toHelp(false);
+			this.toHelp(false);
 		} else if (p === 'about') {
-			com.toAbout(false);
+			this.toAbout(false);
 		} else if (p === 'stats') {
-			com.toStatistics(false);
+			this.toStatistics(false);
 		} else {
-			com.toAggregator(false);
+			this.toAggregator(false);
 		}
 	} else {
-		com.toAggregator(false);
+		this.toAggregator(false);
 	}
 };
@@ -529,10 +531,7 @@
 React.render(React.createElement(Footer, null), document.getElementById('footer') );
 
-window.onpopstate = function(event) {
-	console.log("popped location: " + document.location + ", state: " + JSON.stringify(event.state));
-	routeFromLocation(main);
-};
-
-window.main = main;
+window.onpopstate = routeFromLocation.bind(main);
+
+routeFromLocation.bind(main)();
 
 })();
@@ -544,14 +543,2 @@
 
 
-
-
-
-
-
-
-
-
-
-
-
-
Index: SRUAggregator/trunk/src/main/resources/assets/js/main.jsx
===================================================================
--- SRUAggregator/trunk/src/main/resources/assets/js/main.jsx	(revision 5976)
+++ SRUAggregator/trunk/src/main/resources/assets/js/main.jsx	(revision 6043)
@@ -3,5 +3,5 @@
 "use strict";
 
-var VERSION = "VERSION 2.0.0.Î±20";
+var VERSION = "VERSION 2.0.0.Î±21";
 var URLROOT = "/Aggregator-testing";
 
@@ -13,5 +13,5 @@
 var Main = React.createClass({
 	componentWillMount: function() {
-		routeFromLocation(this);
+		routeFromLocation.bind(this);
 	},
 
@@ -102,5 +102,5 @@
 			}
 			this.setState({navbarPageFn: pageFn});
-			console.log("new page: " + document.location + ", name: " + pageFnName);
+			// console.log("new page: " + document.location + ", name: " + pageFnName);
 		}
 	},
@@ -193,5 +193,5 @@
 			success: function(json, textStatus, jqXHR) {
 				this.setState({stats: json});
-				console.log("stats:", json);
+				// console.log("stats:", json);
 			}.bind(this),
 		});
@@ -294,5 +294,5 @@
 
 	renderInstitution: function(isScan, inst) {
-		return 	<div style={{marginBottom:30}} key={inst[0]}>
+		return 	<div style={{marginTop:30}} key={inst[0]}>
 					<h4>{inst[0]}</h4>
 					<div style={{marginLeft:20}}> {_.pairs(inst[1]).map(this.renderEndpoint.bind(this, isScan)) }</div>
@@ -302,17 +302,17 @@
 	renderStatistics: function(stats) {
 		return 	<div className="container statistics" style={{marginTop:20}}>
-					<ul className='list-inline list-unstyled'>
-						{ stats.maxConcurrentScanRequestsPerEndpoint ? 
-							<li>max concurrent scan requests per endpoint:{" "}
-								<kbd>{stats.maxConcurrentScanRequestsPerEndpoint}</kbd>,
-							</li> : false
+					<div>
+						<div>Start date: {new Date(stats.date).toLocaleString()}</div>
+						{ stats.isScan ? 
+							<div>Max concurrent scan requests per endpoint:{" "}
+								<kbd>{stats.maxConcurrentScanRequestsPerEndpoint}</kbd>
+							</div> 
+							: 
+							<div>Max concurrent search requests per endpoint:{" "}
+								<kbd>{stats.maxConcurrentSearchRequestsPerEndpoint}</kbd>
+							</div>
 						}
-						{ stats.maxConcurrentSearchRequestsPerEndpoint ? 
-							<li>max concurrent search requests per endpoint:{" "}
-								<kbd>{stats.maxConcurrentSearchRequestsPerEndpoint}</kbd>,
-							</li> : false
-						}
-						<li>timeout:{" "}<kbd>{stats.timeout} seconds</kbd></li>
-					</ul>
+						<div>Timeout: {" "}<kbd>{stats.timeout} seconds</kbd></div>
+					</div>
 					<div> { _.pairs(stats.institutions).map(this.renderInstitution.bind(this, stats.isScan)) } </div>
 				</div>
@@ -508,19 +508,21 @@
 }
 
-var routeFromLocation = function(com) {
+var routeFromLocation = function() {
+	// console.log("routeFromLocation: " + document.location);
+	if (!this) throw "routeFromLocation must be bound to main";
 	var path = window.location.pathname.split('/');
 	if (path.length === 3) {
 		var p = path[2];
 		if (p === 'help') {
-			com.toHelp(false);
+			this.toHelp(false);
 		} else if (p === 'about') {
-			com.toAbout(false);
+			this.toAbout(false);
 		} else if (p === 'stats') {
-			com.toStatistics(false);
+			this.toStatistics(false);
 		} else {
-			com.toAggregator(false);
+			this.toAggregator(false);
 		}
 	} else {
-		com.toAggregator(false);
+		this.toAggregator(false);
 	}
 };
@@ -529,10 +531,7 @@
 React.render(<Footer />, document.getElementById('footer') );
 
-window.onpopstate = function(event) {
-	console.log("popped location: " + document.location + ", state: " + JSON.stringify(event.state));
-	routeFromLocation(main);
-};
-
-window.main = main;
+window.onpopstate = routeFromLocation.bind(main);
+
+routeFromLocation.bind(main)();
 
 })();
@@ -544,14 +543,2 @@
 
 
-
-
-
-
-
-
-
-
-
-
-
-
Index: SRUAggregator/trunk/src/main/resources/assets/js/search.js
===================================================================
--- SRUAggregator/trunk/src/main/resources/assets/js/search.js	(revision 5976)
+++ SRUAggregator/trunk/src/main/resources/assets/js/search.js	(revision 6043)
@@ -62,5 +62,5 @@
 		corpus.selected = true; // selected in the corpus view
 		corpus.expanded = false; // not expanded in the corpus view
-		corpus.priority = 1; // priority in corpus view
+		corpus.priority = 1; // used for ordering search results in corpus view 
 		corpus.index = index; // original order, used for stable sort
 	});
@@ -164,5 +164,4 @@
 	},
 
-	timeout: 0,
 	nohits: { 
 		requests: [],
@@ -182,5 +181,6 @@
 
 			searchId: null,
-			hits: this.nohits,
+			timeout: 0,
+			hits: this.nohits,			
 		};
 	},
@@ -195,5 +195,7 @@
 			url: 'rest/corpora',
 			success: function(json, textStatus, jqXHR) {
-				this.setState({corpora : new Corpora(json, this.updateCorpora)});
+				if (this.isMounted()) {
+					this.setState({corpora : new Corpora(json, this.updateCorpora)});
+				}
 			}.bind(this),
 		});
@@ -204,5 +206,7 @@
 			url: 'rest/languages',
 			success: function(json, textStatus, jqXHR) {
-				this.setState({languageMap : json});
+				if (this.isMounted()) {
+					this.setState({languageMap : json});
+				}
 			}.bind(this),
 		});
@@ -233,7 +237,7 @@
 			success: function(searchId, textStatus, jqXHR) {
 				// console.log("search ["+query+"] ok: ", searchId, jqXHR);
-				this.setState({searchId : searchId});
-				this.timeout = 250;
-				setTimeout(this.refreshSearchResults, this.timeout);
+				var timeout = 250;
+				setTimeout(this.refreshSearchResults, timeout);
+				this.setState({ searchId: searchId, timeout: timeout });
 			}.bind(this),
 		});
@@ -241,5 +245,5 @@
 
 	refreshSearchResults: function() {
-		if (!this.state.searchId) {
+		if (!this.state.searchId || !this.isMounted()) {
 			return;
 		}
@@ -247,14 +251,15 @@
 			url: 'rest/search/'+this.state.searchId,
 			success: function(json, textStatus, jqXHR) {
+				var timeout = this.state.timeout;
 				if (json.requests.length > 0) {
-					if (this.timeout < 10000) {
-						this.timeout = 1.5 * this.timeout;
+					if (timeout < 10000) {
+						timeout = 1.5 * timeout;
 					}
-					setTimeout(this.refreshSearchResults, this.timeout);
-					// console.log("new search in: " + this.timeout+ "ms");
+					setTimeout(this.refreshSearchResults, timeout);
+					// console.log("new search in: " + this.timeout + "ms");
 				} else {
 					console.log("search ended; hits:", json);
 				}
-				this.setState({hits:json});
+				this.setState({ hits: json, timeout: timeout });
 			}.bind(this),
 		});
@@ -656,15 +661,4 @@
 	},
 
-	renderToolbox: function() {
-		if (this.props.requests.length > 0) {
-			return false;
-		}
-		return 	React.createElement("div", {className: "toolbox float-left"}, 
-					React.createElement("a", {className: "btn btn-default", href: this.props.getDownloadLink("text")}, 
-						React.createElement("span", {className: "glyphicon glyphicon-download-alt", 'aria-hidden': "true"}), " Download"
-					)
-				);
-	},
-
 	renderProgressBar: function() {
 		var percents = 100 * this.props.results.length / (this.props.requests.length + this.props.results.length);
@@ -693,14 +687,44 @@
 	},
 
-	renderKwicCheckbox: function() {
-		return	React.createElement("div", {className: "float-right", style: {marginRight:17}}, 
-					React.createElement("div", {className: "btn-group", style: {display:"inline-block"}}, 
-						React.createElement("label", {forHtml: "inputKwic", className: "btn-default"}, 
-							 this.state.displayKwic ? 
-								React.createElement("input", {id: "inputKwic", type: "checkbox", value: "kwic", checked: true, onChange: this.toggleKwic}) :
-								React.createElement("input", {id: "inputKwic", type: "checkbox", value: "kwic", onChange: this.toggleKwic}), 
-							
-							"Â " + ' ' +
-							"Display as Key Word In Context"
+	renderDownloadLinks: function() {
+		return (
+			React.createElement("div", {className: "dropdown"}, 
+				React.createElement("button", {className: "btn btn-default", 'aria-expanded': "false", 'data-toggle': "dropdown"}, 
+					React.createElement("span", {className: "glyphicon glyphicon-download-alt", 'aria-hidden': "true"}), 
+					" ", " Download ", " ", 
+					React.createElement("span", {className: "caret"})
+				), 
+				React.createElement("ul", {className: "dropdown-menu"}, 
+					React.createElement("li", null, " ", React.createElement("a", {href: this.props.getDownloadLink("csv")}, 
+							" ", " As CSV file")), 
+					React.createElement("li", null, " ", React.createElement("a", {href: this.props.getDownloadLink("excel")}, 
+							" ", " As Excel file")), 
+					React.createElement("li", null, " ", React.createElement("a", {href: this.props.getDownloadLink("tcf")}, 
+							" ", " As TCF file")), 
+					React.createElement("li", null, " ", React.createElement("a", {href: this.props.getDownloadLink("text")}, 
+							" ", " As Plain Text file"))
+				)
+			)
+		);
+	},
+
+	renderToolbox: function(hits) {
+		if (hits <= 0) {
+			return false;
+		}
+		return 	React.createElement("div", {key: "-toolbox-", style: {marginBottom:10}}, 
+					React.createElement("div", {className: "toolbox float-left inline"}, 
+						this.renderDownloadLinks()
+					), 
+					React.createElement("div", {className: "float-right inline", style: {marginTop:15}}, 
+						React.createElement("div", {className: "btn-group", style: {display:"inline-block"}}, 
+							React.createElement("label", {forHtml: "inputKwic", className: "btn-default"}, 
+								 this.state.displayKwic ? 
+									React.createElement("input", {id: "inputKwic", type: "checkbox", value: "kwic", checked: true, onChange: this.toggleKwic}) :
+									React.createElement("input", {id: "inputKwic", type: "checkbox", value: "kwic", onChange: this.toggleKwic}), 
+								
+								"Â " + ' ' +
+								"Display as Key Word In Context"
+							)
 						)
 					)
@@ -719,10 +743,5 @@
 						React.createElement("div", {key: "-found-message-", style: margintop}, this.renderFoundMessage(hits), " "), 
 						React.createElement("div", {key: "-progress-", style: margintop}, this.renderProgressBar()), 
-						hits > 0 ? 
-							React.createElement("div", {key: "-option-KWIC-", className: "row"}, 
-								this.renderToolbox(), 
-								this.renderKwicCheckbox()
-							)
-						 	: false, 
+						this.renderToolbox(hits), 
 						this.props.results.map(this.renderResultPanels)
 					)
Index: SRUAggregator/trunk/src/main/resources/assets/js/search.jsx
===================================================================
--- SRUAggregator/trunk/src/main/resources/assets/js/search.jsx	(revision 5976)
+++ SRUAggregator/trunk/src/main/resources/assets/js/search.jsx	(revision 6043)
@@ -62,5 +62,5 @@
 		corpus.selected = true; // selected in the corpus view
 		corpus.expanded = false; // not expanded in the corpus view
-		corpus.priority = 1; // priority in corpus view
+		corpus.priority = 1; // used for ordering search results in corpus view 
 		corpus.index = index; // original order, used for stable sort
 	});
@@ -164,5 +164,4 @@
 	},
 
-	timeout: 0,
 	nohits: { 
 		requests: [],
@@ -182,5 +181,6 @@
 
 			searchId: null,
-			hits: this.nohits,
+			timeout: 0,
+			hits: this.nohits,			
 		};
 	},
@@ -195,5 +195,7 @@
 			url: 'rest/corpora',
 			success: function(json, textStatus, jqXHR) {
-				this.setState({corpora : new Corpora(json, this.updateCorpora)});
+				if (this.isMounted()) {
+					this.setState({corpora : new Corpora(json, this.updateCorpora)});
+				}
 			}.bind(this),
 		});
@@ -204,5 +206,7 @@
 			url: 'rest/languages',
 			success: function(json, textStatus, jqXHR) {
-				this.setState({languageMap : json});
+				if (this.isMounted()) {
+					this.setState({languageMap : json});
+				}
 			}.bind(this),
 		});
@@ -233,7 +237,7 @@
 			success: function(searchId, textStatus, jqXHR) {
 				// console.log("search ["+query+"] ok: ", searchId, jqXHR);
-				this.setState({searchId : searchId});
-				this.timeout = 250;
-				setTimeout(this.refreshSearchResults, this.timeout);
+				var timeout = 250;
+				setTimeout(this.refreshSearchResults, timeout);
+				this.setState({ searchId: searchId, timeout: timeout });
 			}.bind(this),
 		});
@@ -241,5 +245,5 @@
 
 	refreshSearchResults: function() {
-		if (!this.state.searchId) {
+		if (!this.state.searchId || !this.isMounted()) {
 			return;
 		}
@@ -247,14 +251,15 @@
 			url: 'rest/search/'+this.state.searchId,
 			success: function(json, textStatus, jqXHR) {
+				var timeout = this.state.timeout;
 				if (json.requests.length > 0) {
-					if (this.timeout < 10000) {
-						this.timeout = 1.5 * this.timeout;
+					if (timeout < 10000) {
+						timeout = 1.5 * timeout;
 					}
-					setTimeout(this.refreshSearchResults, this.timeout);
-					// console.log("new search in: " + this.timeout+ "ms");
+					setTimeout(this.refreshSearchResults, timeout);
+					// console.log("new search in: " + this.timeout + "ms");
 				} else {
 					console.log("search ended; hits:", json);
 				}
-				this.setState({hits:json});
+				this.setState({ hits: json, timeout: timeout });
 			}.bind(this),
 		});
@@ -656,15 +661,4 @@
 	},
 
-	renderToolbox: function() {
-		if (this.props.requests.length > 0) {
-			return false;
-		}
-		return 	<div className="toolbox float-left">
-					<a className="btn btn-default" href={this.props.getDownloadLink("text")}>
-						<span className="glyphicon glyphicon-download-alt" aria-hidden="true"/> Download
-					</a>
-				</div>;
-	},
-
 	renderProgressBar: function() {
 		var percents = 100 * this.props.results.length / (this.props.requests.length + this.props.results.length);
@@ -693,15 +687,45 @@
 	},
 
-	renderKwicCheckbox: function() {
-		return	<div className="float-right" style={{marginRight:17}}>
-					<div className="btn-group" style={{display:"inline-block"}}>
-						<label forHtml="inputKwic" className="btn-default">
-							{ this.state.displayKwic ? 
-								<input id="inputKwic" type="checkbox" value="kwic" checked onChange={this.toggleKwic} /> :
-								<input id="inputKwic" type="checkbox" value="kwic" onChange={this.toggleKwic} />
-							}
-							&nbsp;
-							Display as Key Word In Context
-						</label>
+	renderDownloadLinks: function() {
+		return (
+			<div className="dropdown">
+				<button className="btn btn-default" aria-expanded="false" data-toggle="dropdown" >
+					<span className="glyphicon glyphicon-download-alt" aria-hidden="true"/>
+					{" "} Download {" "} 
+					<span className="caret"/>
+				</button>
+				<ul className="dropdown-menu">
+					<li> <a href={this.props.getDownloadLink("csv")}>
+							{" "} As CSV file</a></li>
+					<li> <a href={this.props.getDownloadLink("excel")}>
+							{" "} As Excel file</a></li>
+					<li> <a href={this.props.getDownloadLink("tcf")}>
+							{" "} As TCF file</a></li>
+					<li> <a href={this.props.getDownloadLink("text")}>
+							{" "} As Plain Text file</a></li>
+				</ul>
+			</div>
+		);
+	},
+
+	renderToolbox: function(hits) {
+		if (hits <= 0) {
+			return false;
+		}
+		return 	<div key="-toolbox-" style={{marginBottom:10}}>
+					<div className="toolbox float-left inline">
+						{this.renderDownloadLinks()}
+					</div>
+					<div className="float-right inline" style={{marginTop:15}}>
+						<div className="btn-group" style={{display:"inline-block"}}>
+							<label forHtml="inputKwic" className="btn-default">
+								{ this.state.displayKwic ? 
+									<input id="inputKwic" type="checkbox" value="kwic" checked onChange={this.toggleKwic} /> :
+									<input id="inputKwic" type="checkbox" value="kwic" onChange={this.toggleKwic} />
+								}
+								&nbsp;
+								Display as Key Word In Context
+							</label>
+						</div>
 					</div>
 				</div>;
@@ -719,10 +743,5 @@
 						<div key="-found-message-" style={margintop}>{this.renderFoundMessage(hits)} </div>
 						<div key="-progress-" style={margintop}>{this.renderProgressBar()}</div>
-						{hits > 0 ? 
-							<div key="-option-KWIC-" className="row">
-								{this.renderToolbox()}
-								{this.renderKwicCheckbox()}
-							</div>
-						 	: false }
+						{this.renderToolbox(hits)}
 						{this.props.results.map(this.renderResultPanels)}
 					</ReactCSSTransitionGroup>
