changeset 39:37840afb7b80

new: full text search
author Zoe Hong <zhong@mpiwg-berlin.mpg.de>
date Fri, 04 Dec 2015 14:28:44 +0100
parents 8b7a204fa929
children 35ed4e650a53
files src/main/java/de/mpiwg/gazetteer/bo/LGFullTextSearchFile.java src/main/java/de/mpiwg/gazetteer/db/DBBook.java src/main/java/de/mpiwg/gazetteer/db/DBContents.java src/main/java/de/mpiwg/gazetteer/db/DBCoordinatesBook.java src/main/java/de/mpiwg/gazetteer/rest/GetFullTextSearchHtmlFile.java src/main/java/de/mpiwg/gazetteer/rest/TextServlet.java src/main/java/de/mpiwg/gazetteer/utils/AbstractDataProvider.java src/main/java/de/mpiwg/gazetteer/utils/DBService.java src/main/java/de/mpiwg/gazetteer/utils/DataProvider.java src/main/java/de/mpiwg/gazetteer/utils/FileManager.java src/main/java/de/mpiwg/web/fullTextSearch/SortContentByAdminType.java src/main/java/de/mpiwg/web/fullTextSearch/SortContentByBookId.java src/main/java/de/mpiwg/web/fullTextSearch/SortContentByBookName.java src/main/java/de/mpiwg/web/fullTextSearch/SortContentByDynasty.java src/main/java/de/mpiwg/web/fullTextSearch/SortContentById.java src/main/java/de/mpiwg/web/fullTextSearch/SortContentByInx.java src/main/java/de/mpiwg/web/fullTextSearch/SortContentByLevel1.java src/main/java/de/mpiwg/web/fullTextSearch/SortContentByLevel2.java src/main/java/de/mpiwg/web/fullTextSearch/SortContentByPeriod.java src/main/java/de/mpiwg/web/fullTextSearch/SortContentByStartPage.java src/main/java/de/mpiwg/web/jsp/FullTextSearchPage.java src/main/java/de/mpiwg/web/jsp/JSPProxy.java src/main/java/de/mpiwg/web/jsp/SearchPage.java src/main/java/de/mpiwg/web/jsp/SessionBean.java src/main/resources/config.properties src/main/resources/hibernate.cfg.xml src/main/webapp/componentes/paginator.jsp src/main/webapp/componentes/template.jsp src/main/webapp/pages/fullTextSearch.jsp src/main/webapp/pages/home.jsp src/main/webapp/pages/search.jsp src/main/webapp/resources/.DS_Store src/main/webapp/resources/css/style.css src/main/webapp/resources/js/LGSearch.js src/main/webapp/resources/js/general.js src/main/webapp/resources/js/proxyMethods.js
diffstat 36 files changed, 2302 insertions(+), 51 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/de/mpiwg/gazetteer/bo/LGFullTextSearchFile.java	Fri Dec 04 14:28:44 2015 +0100
@@ -0,0 +1,85 @@
+package de.mpiwg.gazetteer.bo;
+
+import java.io.Serializable;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Inheritance;
+import javax.persistence.InheritanceType;
+import javax.persistence.Table;
+
+import cl.maps.duplex.DuplexKey;
+
+
+@Entity
+@Table(name="FullTextSearchFile")
+@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
+public class LGFullTextSearchFile extends DBEntry implements Cloneable, Serializable {
+	private static final long serialVersionUID = 7805209462045170778L;
+	
+
+	@Column(name="userId")
+	private Long userId;
+	
+	@Column(name="searchTerms")
+	private String searchTerms;
+	
+	@Column(name="fileName")
+	private String fileName;
+
+	
+	
+	public boolean isEmpty() {
+		if (this.fileName == null) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+
+
+	public Long getUserId() {
+		return userId;
+	}
+
+
+
+	public void setUserId(Long userId) {
+		this.userId = userId;
+	}
+
+
+	public String getSearchTerms() {
+		return searchTerms;
+	}
+
+
+
+	public void setSearchTerms(String searchTerms) {
+		this.searchTerms = searchTerms;
+	}
+
+
+
+	public String getFileName() {
+		return fileName;
+	}
+
+
+
+	public void setFileName(String fileName) {
+		this.fileName = fileName;
+	}
+	
+	
+	public DuplexKey<Long, Long> getKey(){
+		return new DuplexKey<Long, Long>(this.userId, this.id);
+	}
+
+	
+	
+}
--- a/src/main/java/de/mpiwg/gazetteer/db/DBBook.java	Fri Nov 20 15:37:04 2015 +0100
+++ b/src/main/java/de/mpiwg/gazetteer/db/DBBook.java	Fri Dec 04 14:28:44 2015 +0100
@@ -16,8 +16,8 @@
 	private String volume;
 	private String dynasty;
 	
-	private String level1;
-	private String level2;
+	private String level1 = "";
+	private String level2 = "";
 	private String admin_type;
 	private String in_jibengujiku;
 	
@@ -27,6 +27,7 @@
 	private String toc_correction;
 	
 	
+	
 
 	private DBSectionVersion currentSectionVersion;
 	
@@ -49,9 +50,11 @@
 		this.start_year = rs.getInt("start_year");
 		this.end_year = rs.getInt("end_year");
 		this.comments = rs.getString("comments");
-		//this.toc_correction = Integer.parseInt(rs.getString("toc_correction"));
+		
 		this.toc_correction = rs.getString("toc_correction");
 		
+		
+		
 	}
 	
 	@Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/de/mpiwg/gazetteer/db/DBContents.java	Fri Dec 04 14:28:44 2015 +0100
@@ -0,0 +1,127 @@
+package de.mpiwg.gazetteer.db;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+
+import de.mpiwg.gazetteer.utils.DBService;
+
+
+
+public class DBContents implements Comparable<DBContents>{
+
+	private Long id;
+	
+	private String bookId;
+	private Integer page;
+	private String content;
+	
+	private DBSection section;	// section already contain DBBook
+	private Integer inx = null;	// local index for each row
+	
+	private DBCoordinatesBook coordinatesBook = null;
+	
+	private boolean isRemoved = false;
+	
+
+	public DBContents(ResultSet rs) throws SQLException{
+		
+		this.id = rs.getLong("id");
+		this.bookId = rs.getString("books_id");
+		this.page = rs.getInt("line");
+		this.content = rs.getString("content");
+
+		// set this.section by bookId and page
+		this.section = DBService.getInstance().getSectionByBookIdAndPage(this.bookId, this.page);		
+		
+		// set this.coordinatesBook by bookId
+		this.coordinatesBook = DBService.getInstance().getCoordinatesBook(bookId);
+		
+	}
+	
+	
+	public Long getId() {
+		return id;
+	}
+	public void setId(Long id) {
+		this.id = id;
+	}
+	
+
+	public String getBookId() {
+		return bookId;
+	}
+
+	public void setBookId(String bookId) {
+		this.bookId = bookId;
+	}
+
+
+
+	public String getContent() {
+		return content;
+	}
+
+
+	public void setContent(String content) {
+		this.content = content;
+	}
+
+
+	public DBSection getSection() {
+		return section;
+	}
+
+
+	public void setSection(DBSection section) {
+		this.section = section;
+	}
+
+	
+
+	public Integer getPage() {
+		return page;
+	}
+
+
+	public void setPage(Integer page) {
+		this.page = page;
+	}
+
+
+	public Integer getInx() {
+		return inx;
+	}
+
+
+	public void setInx(Integer inx) {
+		this.inx = inx;
+	}
+
+
+
+	public DBCoordinatesBook getCoordinatesBook() {
+		return coordinatesBook;
+	}
+
+
+	public void setCoordinatesBook(DBCoordinatesBook coordinatesBook) {
+		this.coordinatesBook = coordinatesBook;
+	}
+
+
+	public boolean isRemoved() {
+		return isRemoved;
+	}
+
+
+	public void setRemoved(boolean isRemoved) {
+		this.isRemoved = isRemoved;
+	}
+
+
+	@Override
+	public int compareTo(DBContents o) {
+		return this.getInx().compareTo(o.getInx());
+	}
+}
--- a/src/main/java/de/mpiwg/gazetteer/db/DBCoordinatesBook.java	Fri Nov 20 15:37:04 2015 +0100
+++ b/src/main/java/de/mpiwg/gazetteer/db/DBCoordinatesBook.java	Fri Dec 04 14:28:44 2015 +0100
@@ -18,7 +18,7 @@
 		this.books_id = rs.getString("books_id");
 		this.place_name = rs.getString("place_name");
 		this.x = rs.getString("x");
-		this.y = rs.getString("y");
+		this.y = rs.getString("y");	
 		
 	}
 	
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/de/mpiwg/gazetteer/rest/GetFullTextSearchHtmlFile.java	Fri Dec 04 14:28:44 2015 +0100
@@ -0,0 +1,69 @@
+package de.mpiwg.gazetteer.rest;
+
+import java.io.PrintWriter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+import org.json.JSONObject;
+
+import de.mpiwg.gazetteer.bo.LGFile;
+import de.mpiwg.gazetteer.bo.LGFullTextSearchFile;
+import de.mpiwg.gazetteer.utils.DataProvider;
+import de.mpiwg.gazetteer.utils.FileManager;
+import de.mpiwg.gazetteer.utils.HTTPUtils;
+import de.mpiwg.gazetteer.utils.exceptions.GazetteerException;
+import de.mpiwg.web.jsp.FullTextSearchPage;
+
+public class GetFullTextSearchHtmlFile extends AbstractServletMethod {
+	public static String name = "getFullTextSearchHtmlFile";
+
+	private static Logger logger = Logger.getLogger(GetFullTextSearchHtmlFile.class);
+	
+	
+	public static void execute(HttpServletRequest request, HttpServletResponse response) throws Exception{
+		
+		Long fileId = getQueryLongParam(request, "fileId");
+		
+		logger.debug("getting full text search html file.");
+		
+		// get html file from /gazetteer-server/ftsearch-data/html/...
+		if(fileId != null){
+	
+			LGFullTextSearchFile file = DataProvider.getInstance().getFullTextSearchFile(fileId);
+			if(file != null){
+				
+				String text = FileManager.getFullTextSearchHtmlFileText(file);
+				PrintWriter out = response.getWriter();
+				out.print(text);
+				out.flush();
+				response.setContentType("text/plain; charset=UTF-8");
+				
+			}else{
+				response.setContentType("application/json");
+				JSONObject json = new JSONObject();
+				json.put("status", "error");
+				json.put("message", "File no found (" + fileId + ")");
+				json.put("code", GazetteerException.CODE);
+				PrintWriter out = response.getWriter();
+				out.print(json.toString());
+				out.flush();
+			}
+			
+			
+			
+		}else{
+			response.setContentType("application/json");
+			JSONObject json = new JSONObject();
+			json.put("status", "error");
+			json.put("message", "Following parameters are mandatory: fileId, userId");
+			PrintWriter out = response.getWriter();
+			out.print(json.toString());
+			out.flush();
+		}
+	
+	}
+	
+}
--- a/src/main/java/de/mpiwg/gazetteer/rest/TextServlet.java	Fri Nov 20 15:37:04 2015 +0100
+++ b/src/main/java/de/mpiwg/gazetteer/rest/TextServlet.java	Fri Dec 04 14:28:44 2015 +0100
@@ -65,6 +65,8 @@
 			GetTable4File.execute(request, response);
 		}else if(StringUtils.equals(GetTabDelimited4File.name, method)){
 			GetTabDelimited4File.execute(request, response);
+		}else if(StringUtils.equals(GetFullTextSearchHtmlFile.name, method)){
+			GetFullTextSearchHtmlFile.execute(request, response);
 		}else{
 			writeError(response, "Content-type wrong. It should be: multipart/form-data");
 		}
--- a/src/main/java/de/mpiwg/gazetteer/utils/AbstractDataProvider.java	Fri Nov 20 15:37:04 2015 +0100
+++ b/src/main/java/de/mpiwg/gazetteer/utils/AbstractDataProvider.java	Fri Dec 04 14:28:44 2015 +0100
@@ -5,6 +5,7 @@
 import cl.maps.duplex.DuplexMap;
 import de.mpiwg.gazetteer.bo.LGBranch;
 import de.mpiwg.gazetteer.bo.LGFile;
+import de.mpiwg.gazetteer.bo.LGFullTextSearchFile;
 
 public class AbstractDataProvider {
 
@@ -48,4 +49,28 @@
 	}
 	
 	
+	private DuplexMap<LGFullTextSearchFile, Long, Long> fullTextSearchFileMap = null;
+	
+	protected DuplexMap<LGFullTextSearchFile, Long, Long> getFullTextSearchFileMap(){
+		if(fullTextSearchFileMap == null){
+			loadFullTextSearchFiles();
+		}
+		return fullTextSearchFileMap;
+	}
+	
+	
+	public void setFullTextSearchFileMap(
+			DuplexMap<LGFullTextSearchFile, Long, Long> fullTextSearchFileMap) {
+		this.fullTextSearchFileMap = fullTextSearchFileMap;
+	}
+
+	private void loadFullTextSearchFiles(){
+		List<LGFullTextSearchFile> list = DBService.getAllLGFullTextSearchFileFromDB();
+		this.fullTextSearchFileMap = new DuplexMap<LGFullTextSearchFile, Long, Long>();
+		for(LGFullTextSearchFile file : list){
+			this.fullTextSearchFileMap.put(file.getKey(), file);
+		}
+	}
+	
+	
 }
--- a/src/main/java/de/mpiwg/gazetteer/utils/DBService.java	Fri Nov 20 15:37:04 2015 +0100
+++ b/src/main/java/de/mpiwg/gazetteer/utils/DBService.java	Fri Dec 04 14:28:44 2015 +0100
@@ -19,7 +19,9 @@
 import de.mpiwg.gazetteer.bo.DBEntry;
 import de.mpiwg.gazetteer.bo.LGBranch;
 import de.mpiwg.gazetteer.bo.LGFile;
+import de.mpiwg.gazetteer.bo.LGFullTextSearchFile;
 import de.mpiwg.gazetteer.db.DBBook;
+import de.mpiwg.gazetteer.db.DBContents;
 import de.mpiwg.gazetteer.db.DBCoordinatesBook;
 import de.mpiwg.gazetteer.db.DBSection;
 import de.mpiwg.gazetteer.db.DBSectionVersion;
@@ -229,7 +231,8 @@
 		return list;
 	}
 	
-	public static List<DBBook> searchFullText(List<String> termList) throws SQLException{
+	/*
+	public static List<DBBook> searchFullText0(List<String> termList) throws SQLException{
 		Long start = System.currentTimeMillis();
 		
 		String condition = "";
@@ -258,6 +261,7 @@
 		Connection conn = null;
 		Statement stmt = null;
 		List<DBBook> resultSet = new ArrayList<DBBook>();
+		// TODO new structure for the result of full text search?
 		try {
 			conn = getNewConnection();
 			stmt = conn.createStatement();
@@ -284,7 +288,97 @@
 		
 		return resultSet;
 	}
+	*/
 	
+	
+	public DBSection getSectionByBookIdAndPage(String bookId, int page) throws SQLException {
+		
+		Connection conn = null;
+		Statement stmt = null;
+		
+		DBSection section = null;
+		
+		String query = "SELECT * FROM "+ SECTIONS_TABLE +" WHERE books_id='" + bookId + "' " +
+						"and start_page<=" + page + " and end_page>=" + page;
+		
+		try {
+			Class.forName(JDBC_DRIVER);
+			conn = getNewConnection();
+			stmt = conn.createStatement();
+			
+			ResultSet rs = stmt.executeQuery(query);
+			
+			while (rs.next()) {
+				section = new DBSection(rs);
+				DBBook book = getInstance().getBook(bookId);
+				section.setBook(book);
+				
+			}
+			rs.close();
+		} catch (Exception e) {
+			e.printStackTrace();
+		} finally {
+			conn.close();
+		}
+		
+		
+		return section;
+		
+	}
+
+	
+	public static List<DBContents> searchFullText(List<String> termList) throws SQLException{
+		Long start = System.currentTimeMillis();
+		
+		String condition = "(";
+		for(int i=0; i<termList.size() ; i++){
+			String term = termList.get(i);
+			if(i>0){
+				condition += " OR ";
+			}
+			
+			condition += " content like '%" + term + "%' ";
+		}
+		condition += ")";
+
+		
+		String sql = "SELECT contents.id, contents.books_id AS books_id, contents.line AS line, contents.content AS content " +
+                    	"FROM contents " +
+                    	"WHERE " + condition + "and (books_id='00204' or books_id='00219')";	// TODO: remove the books_id condition after debug
+             
+		//logger.debug("sql: " + sql);
+
+		Connection conn = null;
+		Statement stmt = null;
+		List<DBContents> resultSet = new ArrayList<DBContents>();
+		try {
+			conn = getNewConnection();
+			stmt = conn.createStatement();
+			
+			Integer count = 0;
+			ResultSet rs = stmt.executeQuery(sql);
+			while (rs.next()) {
+				count++;
+				DBContents content = new DBContents(rs);	
+				content.setInx(count);	// local index for row
+				resultSet.add(content);	
+			}
+			logger.debug("************************* Count " + count + " **********************************");
+			rs.close();
+		} catch (Exception e) {
+			e.printStackTrace();
+		} finally {
+			conn.close();
+		}
+		
+		long end = System.currentTimeMillis();
+		logger.debug("Time execution full text search [ms]: " + (end - start));
+		
+		return resultSet;
+	}
+	
+	
+
 	/**
 	 * This methods search from a list of terms. 
 	 * Every term is considered a subsequence of whole section name.
@@ -636,6 +730,20 @@
 		return list;
 	}
 	
+	
+	protected static List<LGFullTextSearchFile> getAllLGFullTextSearchFileFromDB() {
+		List<LGFullTextSearchFile> list = null;
+		
+		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+		session.getTransaction().begin();
+		Query query = session.createQuery("from LGFullTextSearchFile");
+		list = query.list();
+		session.getTransaction().commit();
+
+		return list;
+		
+	}
+	
 	protected static  List<LGFile> getAllLGFileFromDB(){
 		List<LGFile> list = null;
 
@@ -712,7 +820,7 @@
 			// query in sections_versions table. 
 			// Each line is a current version (with largest version number) for a book.
 			String query = "SELECT * FROM sections_versions s1 " +
-					"JOIN ( " +
+						"JOIN ( " +
 							"SELECT books_id, MAX(version) AS version " +
 							"FROM sections_versions " +
 							"GROUP BY books_id) AS s2 " +
@@ -778,5 +886,27 @@
 		
 	}
 
-	
+	public static LGFullTextSearchFile getExistFullTextSearchFile(Long userId, String fileName) {		
+		//logger.info("getExistFullTextSearchFile: (userId,fileName)=" + userId + ","+fileName);
+		List<LGFullTextSearchFile> list = new ArrayList<LGFullTextSearchFile>();
+
+		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+		session.getTransaction().begin();
+		Query query = session.createQuery("from LGFullTextSearchFile where userId = :userId and fileName = :fileName");
+		query.setLong("userId", userId);
+		query.setString("fileName", fileName);
+		
+		list = query.list();
+		session.getTransaction().commit();
+
+		if (list.size() != 0) {
+			//logger.info("existing record.");
+			return list.get(0);
+		} else {
+			//logger.info("new record.");
+			return null;
+		}
+				
+	}
+
 }
--- a/src/main/java/de/mpiwg/gazetteer/utils/DataProvider.java	Fri Nov 20 15:37:04 2015 +0100
+++ b/src/main/java/de/mpiwg/gazetteer/utils/DataProvider.java	Fri Dec 04 14:28:44 2015 +0100
@@ -8,8 +8,11 @@
 import org.apache.log4j.Logger;
 import org.hibernate.Session;
 
+import cl.maps.duplex.DuplexKey;
 import de.mpiwg.gazetteer.bo.LGBranch;
 import de.mpiwg.gazetteer.bo.LGFile;
+import de.mpiwg.gazetteer.bo.LGFullTextSearchFile;
+import de.mpiwg.gazetteer.db.DBContents;
 import de.mpiwg.gazetteer.utils.exceptions.NoAuthorizedException;
 import de.mpiwg.gazetteer.utils.exceptions.VersioningException;
 
@@ -255,4 +258,63 @@
 		return branch.getId();
 		
 	}
+
+	
+	public List<LGFullTextSearchFile> getSearchFileList4User(Long userId) {
+	
+		List<LGFullTextSearchFile> list = new ArrayList<LGFullTextSearchFile>();
+		
+		for(LGFullTextSearchFile searchFile : getFullTextSearchFileMap().values()){
+			if(searchFile.getUserId() == userId){
+				list.add(searchFile);
+			}
+		}
+		
+		return list;
+	}
+
+	public LGFullTextSearchFile saveLGFullTextSearchFile(List<DBContents> list, Long userId, String fileName, String searchTerms) throws Exception {	
+		// save as csv and html file in filesystem, and records in db `FullTextSearchFile
+		// List<DBContents> list is the filteredList of searching result
+	
+		Date date = new Date();
+		
+		// check if record with (userId, fileName) already existed, update it; otherwise, create one
+		
+		LGFullTextSearchFile searchFile = DBService.getExistFullTextSearchFile(userId, fileName);
+		if (searchFile == null) {
+			searchFile = new LGFullTextSearchFile();
+			searchFile.setFileName(fileName);
+			searchFile.setUserId(userId);
+		}
+
+		searchFile.setSearchTerms(searchTerms);
+			
+		//Saving into DB
+		//##################################
+		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+		session.getTransaction().begin();
+		
+		DBService.saveDBEntry0(session, searchFile, date);
+		
+		//Saving physical file in the operating system
+		FileManager.saveFullTextSearchFileAsCsv(searchFile, userId, list);	// save csv file to LGMap's datasets folder
+		FileManager.saveFullTextSearchFileAsHtml(searchFile, userId, list);	// save html file to ftsearch-data folder
+
+		session.getTransaction().commit();
+		//##################################
+		
+		return searchFile;
+	}
+
+	public LGFullTextSearchFile getFullTextSearchFile(Long fileId) {
+
+		LGFullTextSearchFile file = getFullTextSearchFileMap().getValuesByOwnKey(fileId);
+		
+		return file;
+	}
+
+	
+
+	
 }
--- a/src/main/java/de/mpiwg/gazetteer/utils/FileManager.java	Fri Nov 20 15:37:04 2015 +0100
+++ b/src/main/java/de/mpiwg/gazetteer/utils/FileManager.java	Fri Dec 04 14:28:44 2015 +0100
@@ -1,20 +1,23 @@
 package de.mpiwg.gazetteer.utils;
 
 import java.io.File;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.Date;
-import java.util.UUID;
+import java.util.List;
 
-import org.apache.commons.lang.RandomStringUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.log4j.Logger;
 
 import de.mpiwg.gazetteer.bo.LGBranch;
 import de.mpiwg.gazetteer.bo.LGFile;
+import de.mpiwg.gazetteer.bo.LGFullTextSearchFile;
+import de.mpiwg.gazetteer.db.DBBook;
+import de.mpiwg.gazetteer.db.DBContents;
 
 public class FileManager {
 
@@ -40,6 +43,140 @@
 		return base;
 	}
 	
+	
+	public static void saveFullTextSearchFileAsCsv(LGFullTextSearchFile file, Long userId, List<DBContents> list) throws Exception{
+		
+		// String absolutePath = PropertiesUtils.getPropValue("ftsearch_root") + "/csv/" + userId.toString() + "/";
+		String absolutePath = PropertiesUtils.getPropValue("lgmap_datasets") + "/";
+		
+		String fileName = file.getUserId() + "_" + file.getFileName() + ".csv";
+		
+		File folder = new File(absolutePath);
+		folder.mkdirs();
+		
+		logger.info("Trying to save file " + absolutePath + fileName + ".");
+		
+		PrintWriter out = new PrintWriter(absolutePath + fileName);
+		
+		String text = new String();	// make it from list
+		text += "Address,LEVEL1,LEVEL2,Name,PERIOD,TimeSpan:begin,TimeSpan:end,Longitude,Latitude,PAGE,SECTION,CONTENT,BOOK_ID,Description\n";
+	
+		for (DBContents r : list) {
+			
+			DBBook book = r.getSection().getBook();
+			
+			String description = book.getVolume() + "/" + book.getAuthor() + "/" + book.getEdition();
+			
+			text += r.getCoordinatesBook().getPlace_name() + "," +
+					book.getLevel1() + "," +
+					book.getLevel2() + "," +
+					book.getName() + "," +
+					book.getPeriod() + "," +
+					book.getStart_year() + "," +
+					book.getEnd_year() + "," +
+					r.getCoordinatesBook().getX() + "," +
+					r.getCoordinatesBook().getY() + "," +
+					r.getPage() + "," +
+					r.getSection().getName() + "," +
+					r.getContent() + "," +
+					r.getBookId() + "," +
+				description + "\n";
+		
+		}
+		
+		text = text.substring(0, text.length()-2);	// cut the last 2 chars, which are "\n" here
+	
+		out.println(text);
+		out.close();
+		
+		logger.info("The file " + fileName + " has been saved correctly.");
+
+		
+	}
+	
+	
+	private static String getLGMapUrl(LGFullTextSearchFile file) throws IOException{
+		String lgmap = PropertiesUtils.getPropValue("lgmap") + "&file=" + file.getUserId().toString() + "_" + file.getFileName() + ".csv&name=" + file.getSearchTerms();
+		
+		return lgmap;
+	}
+	
+	public static void saveFullTextSearchFileAsHtml(LGFullTextSearchFile file, Long userId, List<DBContents> list) throws Exception{
+		
+		String absolutePath = PropertiesUtils.getPropValue("ftsearch_root") + "/html/" + userId.toString() + "/";
+		String fileName = file.getFileName() + ".html";
+		
+		File folder = new File(absolutePath);
+		folder.mkdirs();
+		
+		logger.info("Trying to save file " + absolutePath + fileName + ".");
+		
+		PrintWriter out = new PrintWriter(absolutePath + fileName);
+		
+		String text = new String();
+		String header = new String();
+		text += "<html>";
+		
+		String root_server =  PropertiesUtils.getPropValue("root_server");
+		
+		header = "<head>" +
+					"<meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>" +
+					
+					"<link href='" + root_server + "/resources/css/style.css' type='text/css' rel='stylesheet'/>" +
+					"<script src='" + root_server + "/resources/js/jquery.min.js' type='text/javascript'></script>" +						
+					"<script src='" + root_server + "/resources/js/LGSearch.js' type='text/javascript'></script>" +
+					
+				"</head>";
+		
+		text += header;
+		text += "<body>"
+				+ "<div class='subTitel'>Table name: " + file.getFileName() + "<div>"
+				+ "<div class='label'>" + list.size() + " result(s) in the table.</dvi>"
+				+ "<div class='label'>searching by keywords (possibly with filters): " + '"' + "<span id='searchTerm'>" + file.getSearchTerms() + "</span>" + '"' + "</div>"
+				+ "<div class='label'><a href='" + getLGMapUrl(file) + "' target='_blank'>view on LGMap</a><div>"
+				+ "<br>"
+				+ "<table class='pageTable'>"
+					+ "<tr>" 
+						+ "<td class='tableTitle'>#</td>" 
+						+ "<td class='tableTitle'>book id</td>" 
+						+ "<td class='tableTitle'>book name</td>"
+						+ "<td class='tableTitle'>level1</td>"
+						+ "<td class='tableTitle'>level2</td>"
+						+ "<td class='tableTitle'>period</td>"
+						+ "<td class='tableTitle'>section name</td>"
+						+ "<td class='tableTitle'>page</td>"
+						+ "<td class='tableTitle'>content</td>"
+		 			+ "<tr>";
+		for (DBContents r : list) {			
+			
+			DBBook book = r.getSection().getBook();
+
+			text += "<tr>" +
+						"<td>" + r.getInx() + "</td>" +
+						"<td>" + r.getBookId() + "</td>" +
+						"<td>" + book.getName() + "</td>" +
+						"<td>" + book.getLevel1() + "</td>" +
+						"<td>" + book.getLevel2() + "</td>" +
+						"<td>" + book.getPeriod() + "</td>" +
+						"<td>" + r.getSection().getName() + "</td>" +
+						"<td>" + r.getPage() + "</td>" +
+						"<td class='content'>" + r.getContent() + "</td>" +
+					"</tr>";
+		
+		}
+		
+		text += "</table></body></html>";
+		
+		out.println(text);
+		out.close();
+		
+		logger.info("The file " + fileName + " has been saved correctly.");
+
+		
+	}
+	
+	
+	
 	public static String saveFile(LGBranch branch, LGFile file, Date date, Long userId) throws Exception{
 				
 		String absolutePath = getRootPath() + branch.getSectionId() + "/";
@@ -82,6 +219,17 @@
 		System.out.println("Loading: " + absolutePath);
 		return new File(absolutePath);
 	}
+
+
+	public static String getFullTextSearchHtmlFileText(LGFullTextSearchFile file) throws Exception {
+		
+		String absolutePath = PropertiesUtils.getPropValue("ftsearch_root") + "/html/"  + file.getUserId().toString() + "/" + file.getFileName() + ".html";
+		System.out.println("Loading: " + absolutePath);
+	
+		byte[] encoded = Files.readAllBytes(Paths.get(absolutePath));
+		return new String(encoded, "UTF8");
+		
+	}
 	
 	
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/de/mpiwg/web/fullTextSearch/SortContentByAdminType.java	Fri Dec 04 14:28:44 2015 +0100
@@ -0,0 +1,16 @@
+package de.mpiwg.web.fullTextSearch;
+
+import java.util.Comparator;
+
+import de.mpiwg.gazetteer.db.DBContents;
+
+
+public class SortContentByAdminType  implements Comparator<DBContents>{
+	
+	public int compare(DBContents o1, DBContents o2) {
+		if(o1.getSection() == null || o2.getSection() == null){
+			return o1.getId().compareTo(o2.getId());	
+		}
+		return o1.getSection().getBook().getAdmin_type().compareTo(o2.getSection().getBook().getAdmin_type());
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/de/mpiwg/web/fullTextSearch/SortContentByBookId.java	Fri Dec 04 14:28:44 2015 +0100
@@ -0,0 +1,15 @@
+package de.mpiwg.web.fullTextSearch;
+
+import java.util.Comparator;
+
+import de.mpiwg.gazetteer.db.DBContents;
+
+public class SortContentByBookId implements Comparator<DBContents>{
+	
+	public int compare(DBContents o1, DBContents o2) {
+		if(o1.getSection() == null || o2.getSection() == null){
+			return o1.getId().compareTo(o2.getId());	
+		}
+		return o1.getSection().getBook().getId().compareTo(o2.getSection().getBook().getId());
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/de/mpiwg/web/fullTextSearch/SortContentByBookName.java	Fri Dec 04 14:28:44 2015 +0100
@@ -0,0 +1,16 @@
+package de.mpiwg.web.fullTextSearch;
+
+import java.util.Comparator;
+
+import de.mpiwg.gazetteer.db.DBContents;
+import de.mpiwg.gazetteer.db.DBSection;
+
+public class SortContentByBookName implements Comparator<DBContents>{
+	
+	public int compare(DBContents o1, DBContents o2) {
+		if(o1.getSection() == null || o2.getSection() == null){
+			return o1.getId().compareTo(o2.getId());	
+		}
+		return o1.getSection().getBook().getName().compareTo(o2.getSection().getBook().getName());
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/de/mpiwg/web/fullTextSearch/SortContentByDynasty.java	Fri Dec 04 14:28:44 2015 +0100
@@ -0,0 +1,16 @@
+package de.mpiwg.web.fullTextSearch;
+
+import java.util.Comparator;
+
+import de.mpiwg.gazetteer.db.DBContents;
+import de.mpiwg.gazetteer.db.DBSection;
+
+public class SortContentByDynasty implements Comparator<DBContents>{
+	
+	public int compare(DBContents o1, DBContents o2) {
+		if(o1.getSection() == null || o2.getSection() == null){
+			return o1.getId().compareTo(o2.getId());	
+		}
+		return o1.getSection().getBook().getDynasty().compareTo(o2.getSection().getBook().getDynasty());
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/de/mpiwg/web/fullTextSearch/SortContentById.java	Fri Dec 04 14:28:44 2015 +0100
@@ -0,0 +1,12 @@
+package de.mpiwg.web.fullTextSearch;
+
+import java.util.Comparator;
+
+import de.mpiwg.gazetteer.db.DBContents;
+
+public class SortContentById implements Comparator<DBContents>{
+	
+	public int compare(DBContents o1, DBContents o2) {
+		return o1.getId().compareTo(o2.getId());
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/de/mpiwg/web/fullTextSearch/SortContentByInx.java	Fri Dec 04 14:28:44 2015 +0100
@@ -0,0 +1,16 @@
+package de.mpiwg.web.fullTextSearch;
+
+import java.util.Comparator;
+
+import de.mpiwg.gazetteer.db.DBContents;
+
+
+public class SortContentByInx implements Comparator<DBContents>{
+	
+	public int compare(DBContents o1, DBContents o2) {
+		if(o1.getInx() == null || o2.getInx() == null){
+			return o1.getId().compareTo(o2.getId());	
+		}
+		return o1.getInx().compareTo(o2.getInx());
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/de/mpiwg/web/fullTextSearch/SortContentByLevel1.java	Fri Dec 04 14:28:44 2015 +0100
@@ -0,0 +1,16 @@
+package de.mpiwg.web.fullTextSearch;
+
+import java.util.Comparator;
+
+import de.mpiwg.gazetteer.db.DBContents;
+import de.mpiwg.gazetteer.db.DBSection;
+
+public class SortContentByLevel1 implements Comparator<DBContents>{
+	
+	public int compare(DBContents o1, DBContents o2) {
+		if(o1.getSection() == null || o2.getSection() == null){
+			return o1.getId().compareTo(o2.getId());	
+		}
+		return o1.getSection().getBook().getLevel1().compareTo(o2.getSection().getBook().getLevel1());
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/de/mpiwg/web/fullTextSearch/SortContentByLevel2.java	Fri Dec 04 14:28:44 2015 +0100
@@ -0,0 +1,15 @@
+package de.mpiwg.web.fullTextSearch;
+
+import java.util.Comparator;
+
+import de.mpiwg.gazetteer.db.DBContents;
+
+public class SortContentByLevel2 implements Comparator<DBContents>{
+	
+	public int compare(DBContents o1, DBContents o2) {
+		if(o1.getSection() == null || o2.getSection() == null){
+			return o1.getId().compareTo(o2.getId());	
+		}
+		return o1.getSection().getBook().getLevel2().compareTo(o2.getSection().getBook().getLevel2());
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/de/mpiwg/web/fullTextSearch/SortContentByPeriod.java	Fri Dec 04 14:28:44 2015 +0100
@@ -0,0 +1,16 @@
+package de.mpiwg.web.fullTextSearch;
+
+import java.util.Comparator;
+
+import de.mpiwg.gazetteer.db.DBContents;
+
+public class SortContentByPeriod implements Comparator<DBContents>{
+	
+	public int compare(DBContents o1, DBContents o2) {
+		if(o1.getSection() == null || o2.getSection() == null){
+			return o1.getId().compareTo(o2.getId());	
+		}
+		return o1.getSection().getBook().getPeriod().compareTo(o2.getSection().getBook().getPeriod());
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/de/mpiwg/web/fullTextSearch/SortContentByStartPage.java	Fri Dec 04 14:28:44 2015 +0100
@@ -0,0 +1,17 @@
+package de.mpiwg.web.fullTextSearch;
+
+import java.util.Comparator;
+
+import de.mpiwg.gazetteer.db.DBContents;
+
+public class SortContentByStartPage implements Comparator<DBContents>{
+	
+	public int compare(DBContents o1, DBContents o2) {
+		if(o1.getSection() == null || o2.getSection() == null){
+			return o1.getId().compareTo(o2.getId());	
+		}
+		return o1.getSection().getStart_page().compareTo(o2.getSection().getStart_page());
+	}
+	
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/de/mpiwg/web/jsp/FullTextSearchPage.java	Fri Dec 04 14:28:44 2015 +0100
@@ -0,0 +1,662 @@
+package de.mpiwg.web.jsp;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+
+import de.mpiwg.gazetteer.bo.LGBranch;
+import de.mpiwg.gazetteer.bo.LGFile;
+import de.mpiwg.gazetteer.bo.LGFullTextSearchFile;
+import de.mpiwg.gazetteer.dataverse.bo.VDCUser;
+import de.mpiwg.gazetteer.db.DBContents;
+import de.mpiwg.gazetteer.db.DBSection;
+import de.mpiwg.gazetteer.utils.DBService;
+import de.mpiwg.gazetteer.utils.DataProvider;
+import de.mpiwg.web.fullTextSearch.SortContentByAdminType;
+import de.mpiwg.web.fullTextSearch.SortContentByBookId;
+import de.mpiwg.web.fullTextSearch.SortContentByBookName;
+import de.mpiwg.web.fullTextSearch.SortContentByDynasty;
+import de.mpiwg.web.fullTextSearch.SortContentById;
+import de.mpiwg.web.fullTextSearch.SortContentByInx;
+import de.mpiwg.web.fullTextSearch.SortContentByLevel1;
+import de.mpiwg.web.fullTextSearch.SortContentByLevel2;
+import de.mpiwg.web.fullTextSearch.SortContentByPeriod;
+import de.mpiwg.web.fullTextSearch.SortContentByStartPage;
+import de.mpiwg.web.search.SortSectionByAdminType;
+import de.mpiwg.web.search.SortSectionByAuthor;
+import de.mpiwg.web.search.SortSectionByBookId;
+import de.mpiwg.web.search.SortSectionByBookName;
+import de.mpiwg.web.search.SortSectionByDynasty;
+import de.mpiwg.web.search.SortSectionByEdition;
+import de.mpiwg.web.search.SortSectionById;
+import de.mpiwg.web.search.SortSectionByLevel1;
+import de.mpiwg.web.search.SortSectionByPeriod;
+import de.mpiwg.web.search.SortSectionByStartPage;
+import de.mpiwg.web.search.SortSectionByVolume;
+
+public class FullTextSearchPage extends AbstractJSPPage{
+	
+	private static Logger logger = Logger.getLogger(FullTextSearchPage.class);
+	
+	public static String bean = "fullTextSearchBean";
+	public static String page = "pages/fullTextSearch.jsp";
+
+	
+	private List<DBContents> completeList;
+	private List<DBContents> filteredList;
+	private List<DBContents> displayList;
+	
+	private String searchTerm = new String();
+
+	private String dynastyFilter = new String();
+	private String adminTypeFilter = new String();
+	private String level1Filter = new String();
+	
+	private DataPaginator paginator = new DataPaginator();
+	private String searchMessage;
+	private String filteringMessage;
+
+	private String bookIdFilter = new String();
+	private String bookNameFilter = new String();
+	private String level2Filter = new String();
+	private String periodFilter = new String();
+	private String sectionNameFilter = new String();
+	
+	
+	private Map<Long, List<LGFullTextSearchFile>> filesMap;
+	private List<LGFullTextSearchFile> fileList = null;
+	private String fileName = new String();
+	
+	private String focusedContentId = new String();
+	
+	@Override
+	public void init(){
+		super.init();
+	}
+	
+	public void loadParameters(HttpServletRequest request, HttpServletResponse response){
+		this.request = request;
+		this.response = response;
+		
+		this.searchTerm = getParameter("searchTerm");
+		
+		this.bookIdFilter = getParameter("bookIdFilter");
+		this.bookNameFilter = getParameter("bookNameFilter");
+		
+		this.dynastyFilter = getParameter("dynastyFilter");
+		this.adminTypeFilter = getParameter("adminTypeFilter");
+		this.level1Filter = getParameter("level1Filter");
+		this.level2Filter = getParameter("level2Filter");
+		
+		this.periodFilter = getParameter("periodFilter");
+		this.sectionNameFilter = getParameter("sectionNameFilter");
+
+		this.fileName = getParameter("fileName");
+			
+		this.focusedContentId = getParameter("focusedContentId");
+		
+	}
+
+	
+	public void search(){		
+		logger.debug("Searching: " + this.searchTerm);
+		
+		this.dynastyFilter = new String();
+		this.level1Filter = new String();
+		this.adminTypeFilter = new String();
+		
+		if(StringUtils.isNotEmpty(this.searchTerm)){
+			try {
+				List<String> terms = splitTerms();
+				System.out.println("Full Text Search: " + terms.toString());
+				
+				this.completeList = DBService.searchFullText(terms);	
+				
+				if (this.completeList != null ){
+					Collections.sort(this.completeList);
+					filter();
+				}
+						
+			} catch (Exception e) {
+				internalError(e);
+			}			
+		}
+	}
+	
+	
+	public void forceLoadFileList(){
+		logger.debug("forceLoadFileList");
+		logger.debug(this.getSearchTerm());
+		
+		if(getSessionBean().getUser() != null){
+			//logger.debug("userId="+ getSessionBean().getUser().getId());
+			
+			// set FileList for the user
+			DataProvider.getInstance().setFullTextSearchFileMap(null);	// set fullTextSearchFileMap to null, to force reload
+			this.setFileList(DataProvider.getInstance().getSearchFileList4User(getSessionBean().getUser().getId()));						
+		}
+
+	}
+	
+	
+	
+	public void save() {	
+		logger.debug("saving table...");
+		logger.debug(this.getFileName() + ", " + this.getSearchTerm() + ", userId= "+ getSessionBean().getUser().getId());
+		
+		//logger.debug(this.getFilteredList());
+		
+		if (StringUtils.equals(this.getFileName(), "") ) {
+			addMsg("Save failed. Table name cannot be empty.");
+			return;
+		}
+		
+		
+		/* Update db table `LGFullTextSearchFile`: new row with userId, file name, ...*/		
+		
+		Long userId = getSessionBean().getUser().getId();
+		
+		LGFullTextSearchFile file;
+		try {
+			// save only those contents that are not removed
+			List<DBContents> cleanList = new ArrayList<DBContents>();
+			for (DBContents t: this.getFilteredList()) {
+				if (!t.isRemoved()) {
+					cleanList.add(t);
+				}
+			}
+			
+			file = DataProvider.getInstance().saveLGFullTextSearchFile(cleanList, userId, this.getFileName(), this.getSearchTerm());
+			addMsg("The table has been saved!");
+
+			logger.debug(file.getInfo());
+			
+		} catch (Exception e) {
+			addMsg("Saving fails!");
+			e.printStackTrace();
+			internalError(e);
+		}
+		
+		// update FileList
+		DataProvider.getInstance().setFullTextSearchFileMap(null);	// set fullTextSearchFileMap to null, to force reload
+		this.setFileList(DataProvider.getInstance().getSearchFileList4User(getSessionBean().getUser().getId()));
+	}
+	
+	
+
+	public void filter(){	
+		this.filteredList = new ArrayList<DBContents>();
+		for(DBContents content : this.completeList){
+			if(!this.filteredList.contains(content)){
+					
+				if( (StringUtils.isEmpty(dynastyFilter) || StringUtils.startsWith(content.getSection().getBook().getDynasty(), dynastyFilter)) &&
+						(StringUtils.isEmpty(level1Filter) || StringUtils.startsWith(content.getSection().getBook().getLevel1(), level1Filter)) &&
+						(StringUtils.isEmpty(level2Filter) || StringUtils.startsWith(content.getSection().getBook().getLevel2(), level2Filter)) &&
+						(StringUtils.isEmpty(adminTypeFilter) || StringUtils.startsWith(content.getSection().getBook().getAdmin_type(), adminTypeFilter)) && 
+						(StringUtils.isEmpty(bookIdFilter) || StringUtils.startsWith(content.getBookId(), bookIdFilter)) &&
+						(StringUtils.isEmpty(bookNameFilter) || StringUtils.startsWith(content.getSection().getBook().getName(), bookNameFilter)) &&
+						(StringUtils.isEmpty(sectionNameFilter) || StringUtils.startsWith(content.getSection().getName(), sectionNameFilter)) 
+									
+						){
+					this.filteredList.add(content);
+				}	
+			}
+		}
+			
+		if(completeList.size() > 0){
+			this.searchMessage = completeList.size() + " section(s) found for the term(s): " + this.searchTerm;
+			this.filteringMessage = this.filteredList.size() + " section(s) listed after the filtering";		
+			
+			this.paginator.setCurrentPage(0);
+			this.paginator.resetNumberOfPages(filteredList.size());
+				
+		}else{
+			this.searchMessage = "No sections found for the term(s): " + this.searchTerm;
+			this.filteredList = null;
+			this.filteringMessage = "";
+				
+			this.paginator.setCurrentPage(0);
+			this.paginator.resetNumberOfPages(0);
+		}
+			
+		this.updateCurrentSections();
+		
+	}
+	
+	
+	private void updateCurrentSections() {
+		/*
+		this.paginator.initCount();
+		int startRecord = this.paginator.getCurrentPage()
+				* this.paginator.getItemsPerPage();
+		if(this.paginator.getNumberOfPages() == 0){
+			this.displayList = new ArrayList<DBContents>();
+		}else if((this.paginator.getCurrentPage() + 1) == this.paginator.getNumberOfPages()){
+			int mod = this.filteredList.size() % paginator.getItemsPerPage();
+			if(mod == 0){
+				this.displayList = filteredList.subList(startRecord, startRecord + this.paginator.getItemsPerPage());
+			}else{
+				this.displayList = filteredList.subList(startRecord, startRecord + mod);	
+			}
+			
+		}else{
+			this.displayList = filteredList.subList(startRecord, startRecord + this.paginator.getItemsPerPage());	
+		}
+		
+		for(DBContents content : this.displayList){
+			//section.setBranches(this.branchesMap.get(section.getId()));
+		}
+		*/
+	}
+	
+	
+	private List<String> splitTerms(){
+		List<String> rs = new ArrayList<String>();
+
+		String[] array = this.searchTerm.split(",");
+		
+		for(String tmp : array){
+			tmp = tmp.replace(" ", "");
+			if(StringUtils.isNotEmpty(tmp)){
+				rs.add(tmp);	
+			}
+		}
+		return rs;
+	}
+
+	public List<String> suggestDynasty(String term, int limit){
+		List<String> list = new ArrayList<String>();
+		for(DBContents content : this.completeList){
+			String dynasty = content.getSection().getBook().getDynasty();
+			if(!list.contains(dynasty) && dynasty.startsWith(term)){
+				list.add(dynasty);
+			}
+			if(limit == list.size()){
+				break;
+			}	
+		}
+		return list;
+	}
+	
+	public List<String> suggestLevel1(String term, int limit){
+		List<String> list = new ArrayList<String>();
+		for(DBContents content : this.completeList){
+			String level1 = content.getSection().getBook().getLevel1();
+			if(!list.contains(level1) && level1.startsWith(term)){
+				list.add(level1);
+			}
+			if(limit == list.size()){
+				break;
+			}	
+		}
+		return list;
+	}
+	
+	public List<String> suggestAdminType(String term, int limit){
+		List<String> list = new ArrayList<String>();
+		for(DBContents content : this.completeList){
+			String adminType = content.getSection().getBook().getAdmin_type();
+			if(!list.contains(adminType) && adminType.startsWith(term)){
+				list.add(adminType);
+			}
+			if(limit == list.size()){
+				break;
+			}	
+		}
+		return list;
+	}
+	
+
+
+	public List<LGFullTextSearchFile> getFileList() {
+		return fileList;
+	}
+
+	public void setFileList(List<LGFullTextSearchFile> fileList) {
+		this.fileList = fileList;
+	}
+
+
+
+	public List<DBContents> getCompleteList() {
+		return completeList;
+	}
+
+
+	public Map<Long, List<LGFullTextSearchFile>> getFilesMap() {
+		return filesMap;
+	}
+
+	public void setFilesMap(Map<Long, List<LGFullTextSearchFile>> filesMap) {
+		this.filesMap = filesMap;
+	}
+
+	public void setCompleteList(List<DBContents> completeList) {
+		this.completeList = completeList;
+	}
+
+
+	public String getSearchTerm() {
+		return searchTerm;
+	}
+
+
+	public void setSearchTerm(String searchTerm) {
+		this.searchTerm = searchTerm;
+	}
+
+	public List<DBContents> getFilteredList() {
+		return filteredList;
+	}
+
+	public void setFilteredList(List<DBContents> filteredList) {
+		this.filteredList = filteredList;
+	}
+
+	
+	public List<DBContents> getDisplayList() {
+		return displayList;
+	}
+
+	public void setDisplayList(List<DBContents> displayList) {
+		this.displayList = displayList;
+	}
+	
+
+	public DataPaginator getPaginator() {
+		return paginator;
+	}
+
+	public void setPaginator(DataPaginator paginator) {
+		this.paginator = paginator;
+	}
+	
+	public void firstPage() {
+		this.paginator.first();
+		this.updateCurrentSections();
+	}
+
+	public void lastPage() {
+		this.paginator.last();
+		this.updateCurrentSections();
+	}
+
+	public void fastForward() {
+		this.paginator.fastForward();
+		this.updateCurrentSections();
+	}
+
+	public void fastRewind() {
+		this.paginator.fastRewind();
+		this.updateCurrentSections();
+	}
+
+	public void previousPage() {
+		this.paginator.previous();
+		this.updateCurrentSections();
+	}
+
+	public void nextPage() {
+		this.paginator.next();
+		this.updateCurrentSections();
+	}
+
+	public String getSearchMessage() {
+		return searchMessage;
+	}
+
+	public void setSearchMessage(String searchMessage) {
+		this.searchMessage = searchMessage;
+	}
+
+	public String getFilteringMessage() {
+		return filteringMessage;
+	}
+
+	public void setFilteringMessage(String filteringMessage) {
+		this.filteringMessage = filteringMessage;
+	}	
+	
+	public String getFileName() {
+		return fileName;
+	}
+
+	public void setFileName(String fileName) {
+		this.fileName = fileName;
+	}
+
+	public String getDynastyFilter() {
+		return dynastyFilter;
+	}
+
+	public void setDynastyFilter(String dynastyFilter) {
+		this.dynastyFilter = dynastyFilter;
+	}
+
+	public String getAdminTypeFilter() {
+		return adminTypeFilter;
+	}
+
+	public void setAdminTypeFilter(String adminTypeFilter) {
+		this.adminTypeFilter = adminTypeFilter;
+	}
+
+	public String getLevel1Filter() {
+		return level1Filter;
+	}
+
+	public void setLevel1Filter(String level1Filter) {
+		this.level1Filter = level1Filter;
+	}
+	
+	
+	
+	/////// Sorting
+
+	public String getFocusedContentId() {
+		return focusedContentId;
+	}
+
+	public void setFocusedContentId(String focusedContentId) {
+		this.focusedContentId = focusedContentId;
+	}
+
+	public String getBookIdFilter() {
+		return bookIdFilter;
+	}
+
+	public void setBookIdFilter(String bookIdFilter) {
+		this.bookIdFilter = bookIdFilter;
+	}
+
+	public String getBookNameFilter() {
+		return bookNameFilter;
+	}
+
+	public void setBookNameFilter(String bookNameFilter) {
+		this.bookNameFilter = bookNameFilter;
+	}
+
+	public String getLevel2Filter() {
+		return level2Filter;
+	}
+
+	public void setLevel2Filter(String level2Filter) {
+		this.level2Filter = level2Filter;
+	}
+
+	public String getPeriodFilter() {
+		return periodFilter;
+	}
+
+	public void setPeriodFilter(String periodFilter) {
+		this.periodFilter = periodFilter;
+	}
+
+	public String getSectionNameFilter() {
+		return sectionNameFilter;
+	}
+
+	public void setSectionNameFilter(String sectionNameFilter) {
+		this.sectionNameFilter = sectionNameFilter;
+	}
+
+	public void sortByBookNameUp(){
+		Collections.sort(this.completeList, new SortContentByBookName());
+		filter();
+	}
+	
+	public void sortByBookNameDown(){
+		Collections.sort(this.completeList, new SortContentByBookName());
+		Collections.reverse(this.completeList);
+		filter();
+	}
+	
+	public void sortBySectionNameUp(){
+		Collections.sort(this.completeList);
+		filter();
+	}
+	
+	public void sortBySectionNameDown(){
+		Collections.sort(this.completeList);
+		Collections.reverse(this.completeList);
+		filter();
+	}
+	
+	
+	
+	public void sortByPeriodUp(){
+		Collections.sort(this.completeList, new SortContentByPeriod());
+		filter();
+	}
+	
+	public void sortByPeriodDown(){
+		Collections.sort(this.completeList, new SortContentByPeriod());
+		Collections.reverse(this.completeList);
+		filter();
+	}
+	
+	
+	
+	public void sortByIdUp(){
+		Collections.sort(this.completeList, new SortContentById());
+		this.filter();
+	}
+	
+	public void sortByIdDown(){
+		Collections.sort(this.completeList, new SortContentById());
+		Collections.reverse(completeList);
+		this.filter();
+	}
+	
+	
+	public void sortByDynastyUp(){
+		Collections.sort(this.completeList, new SortContentByDynasty());
+		filter();
+	}
+	
+	public void sortByDynastyDown(){
+		Collections.sort(this.completeList, new SortContentByDynasty());
+		Collections.reverse(completeList);
+		filter();
+	}
+	
+	public void sortByBookIdUp(){
+		Collections.sort(this.completeList, new SortContentByBookId());
+		filter();
+	}
+	
+	public void sortByBookIdDown(){
+		Collections.sort(this.completeList, new SortContentByBookId());
+		Collections.reverse(completeList);
+		filter();
+	}
+	
+	public void sortByLevel1Up(){
+		Collections.sort(this.completeList, new SortContentByLevel1());
+		filter();
+	}
+	
+	public void sortByLevel1Down(){
+		Collections.sort(this.completeList, new SortContentByLevel1());
+		Collections.reverse(completeList);
+		filter();
+	}
+	public void sortByLevel2Up(){
+		Collections.sort(this.completeList, new SortContentByLevel2());
+		filter();
+	}
+	
+	public void sortByLevel2Down(){
+		Collections.sort(this.completeList, new SortContentByLevel2());
+		Collections.reverse(completeList);
+		filter();
+	}
+	
+	public void sortByAdminTypeUp(){
+		Collections.sort(this.completeList, new SortContentByAdminType());
+		filter();
+	}
+	
+	public void sortByAdminTypeDown(){
+		Collections.sort(this.completeList, new SortContentByAdminType());
+		Collections.reverse(completeList);
+		filter();
+	}
+	
+	public void sortByStartPageUp(){
+		Collections.sort(this.completeList, new SortContentByStartPage());
+		filter();
+	}
+	
+	public void sortByStartPageDown(){
+		Collections.sort(this.completeList, new SortContentByStartPage());
+		Collections.reverse(completeList);
+		filter();
+	}
+	public void sortByInxUp(){
+		Collections.sort(this.completeList, new SortContentByInx());
+		filter();
+	}
+	
+	public void sortByInxDown(){
+		Collections.sort(this.completeList, new SortContentByInx());
+		Collections.reverse(completeList);
+		filter();
+	}
+
+	public void removeFocusedContent(boolean status) {
+
+		// TODO set isRemove for the content with id=this.focusedContentId
+		// status is true: remove; 
+		// status is false: recover (unremove)
+		
+		for (DBContents content: this.completeList) {
+			if (StringUtils.equals(content.getId().toString(), this.focusedContentId)) {
+			
+			//if(content.getId().toString() == this.focusedContentId) {
+				content.setRemoved(status);
+				logger.debug("set remove content id=" + content.getId().toString());
+				break;
+			}
+		}
+		
+		
+		
+		Collections.sort(this.completeList);
+		filter();
+	}
+
+
+	
+
+	
+}
--- a/src/main/java/de/mpiwg/web/jsp/JSPProxy.java	Fri Nov 20 15:37:04 2015 +0100
+++ b/src/main/java/de/mpiwg/web/jsp/JSPProxy.java	Fri Dec 04 14:28:44 2015 +0100
@@ -60,8 +60,8 @@
 				}
 				
 				return CreateFilePage.page;
-				
-			}else if(StringUtils.equals(bean, HomePage.bean)){
+	
+			} else if(StringUtils.equals(bean, HomePage.bean)){
 				
 				getSessionBean().getHomePage().loadParameters(request, response);
 				
@@ -141,7 +141,6 @@
 					getSessionBean().logout();
 				}
 				
-				//return BooksPage.page;	// TMP: make the default page after login to be "Books" page for TOC editing task
 				return "pages/home.jsp";
 				
 			}else if(StringUtils.equals(bean, SearchPage.bean)){
@@ -209,6 +208,83 @@
 					
 				return SearchPage.page;
 				
+			} else if(StringUtils.equals(bean, FullTextSearchPage.bean)){
+				getSessionBean().getFullTextSearchPage().loadParameters(request, response);
+					
+				if(StringUtils.equals(action, "search")){
+					getSessionBean().getFullTextSearchPage().search();
+				} else if(StringUtils.equals(action, "filter")){
+					getSessionBean().getFullTextSearchPage().filter();
+				} else if(StringUtils.equals(action, "save")){
+					getSessionBean().getFullTextSearchPage().save();
+				} else if(StringUtils.equals(action, "removeFocusedContent")){
+					getSessionBean().getFullTextSearchPage().removeFocusedContent(true);
+				} else if(StringUtils.equals(action, "recoverFocusedContent")){
+					getSessionBean().getFullTextSearchPage().removeFocusedContent(false);
+				
+				
+				//PAGINATOR
+				} else if(StringUtils.equals(action, "firstPage")){
+					getSessionBean().getFullTextSearchPage().firstPage();
+				} else if(StringUtils.equals(action, "fastRewind")){
+					getSessionBean().getFullTextSearchPage().fastRewind();
+				} else if(StringUtils.equals(action, "previousPage")){
+					getSessionBean().getFullTextSearchPage().previousPage();
+				} else if(StringUtils.equals(action, "nextPage")){
+					getSessionBean().getFullTextSearchPage().nextPage();
+				} else if(StringUtils.equals(action, "fastForward")){
+					getSessionBean().getFullTextSearchPage().fastForward();
+				} else if(StringUtils.equals(action, "lastPage")){
+					getSessionBean().getFullTextSearchPage().lastPage();
+				
+				//SORTING
+				} else if(StringUtils.equals(action, "sortByInxUp")){
+					getSessionBean().getFullTextSearchPage().sortByInxUp();
+				} else if(StringUtils.equals(action, "sortByInxDown")){
+					getSessionBean().getFullTextSearchPage().sortByInxDown();
+				} else if(StringUtils.equals(action, "sortByBookIdUp")){
+					getSessionBean().getFullTextSearchPage().sortByBookIdUp();
+				} else if(StringUtils.equals(action, "sortByBookIdDown")){
+					getSessionBean().getFullTextSearchPage().sortByBookIdDown();
+				} else if(StringUtils.equals(action, "sortByBookNameUp")){
+					getSessionBean().getFullTextSearchPage().sortByBookNameUp();
+				} else if(StringUtils.equals(action, "sortByBookNameDown")){
+					getSessionBean().getFullTextSearchPage().sortByBookNameDown();
+				} else if(StringUtils.equals(action, "sortByDynastyUp")){
+					getSessionBean().getFullTextSearchPage().sortByDynastyUp();
+				} else if(StringUtils.equals(action, "sortByDynastyDown")){
+					getSessionBean().getFullTextSearchPage().sortByDynastyDown();
+				} else if(StringUtils.equals(action, "sortByPeriodUp")){
+					getSessionBean().getFullTextSearchPage().sortByPeriodUp();
+				} else if(StringUtils.equals(action, "sortByPeriodDown")){
+					getSessionBean().getFullTextSearchPage().sortByPeriodDown();
+				} else if(StringUtils.equals(action, "sortBySectionNameUp")){
+					getSessionBean().getFullTextSearchPage().sortBySectionNameUp();
+				} else if(StringUtils.equals(action, "sortBySectionNameDown")){
+					getSessionBean().getFullTextSearchPage().sortBySectionNameDown();
+				
+				} else if(StringUtils.equals(action, "sortByLevel1Up")){
+					getSessionBean().getFullTextSearchPage().sortByLevel1Up();
+				} else if(StringUtils.equals(action, "sortByLevel1Down")){
+					getSessionBean().getFullTextSearchPage().sortByLevel1Down();
+				} else if(StringUtils.equals(action, "sortByLevel2Up")){
+					getSessionBean().getFullTextSearchPage().sortByLevel2Up();
+				} else if(StringUtils.equals(action, "sortByLevel2Down")){
+					getSessionBean().getFullTextSearchPage().sortByLevel2Down();
+				} else if(StringUtils.equals(action, "sortByAdminTypeUp")){
+					getSessionBean().getFullTextSearchPage().sortByAdminTypeUp();
+				} else if(StringUtils.equals(action, "sortByAdminTypeDown")){
+					getSessionBean().getFullTextSearchPage().sortByAdminTypeDown();
+				
+				} else if(StringUtils.equals(action, "sortByStartPageUp")){
+					getSessionBean().getFullTextSearchPage().sortByStartPageUp();
+				} else if(StringUtils.equals(action, "sortByStartPageDown")){
+					getSessionBean().getFullTextSearchPage().sortByStartPageDown();
+				} 
+					
+				
+				return FullTextSearchPage.page;
+				
 			} else if(StringUtils.equals(bean, BooksPage.bean)){
 				getSessionBean().getBooksPage().loadParameters(request, response);
 				
--- a/src/main/java/de/mpiwg/web/jsp/SearchPage.java	Fri Nov 20 15:37:04 2015 +0100
+++ b/src/main/java/de/mpiwg/web/jsp/SearchPage.java	Fri Dec 04 14:28:44 2015 +0100
@@ -35,10 +35,10 @@
 	public static String bean = "searchBean";
 	public static String page = "pages/search.jsp";
 	
+	
 	private static Integer SEARCH_IN_SECTION_NAME = 0;
 	private static Integer SEARCH_IN_BOOK_NAME = 1;
-	private static Integer SEARCH_FULL_TEXT = 2;
-	
+	//private static Integer SEARCH_FULL_TEXT = 2;
 	
 	
 	private Map<Long, List<LGBranch>> branchesMap;
@@ -57,6 +57,8 @@
 	private String searchMessage;
 	private String filteringMessage;
 	
+
+	
 	@Override
 	public void init(){
 		super.init();
@@ -74,7 +76,7 @@
 			
 	}
 	
-	public void search(){
+	public void search(){		
 		logger.debug("Searching: " + this.searchTerm);
 		
 		this.dynastyFilter = new String();
@@ -87,20 +89,21 @@
 				List<String> terms = splitTerms();
 				
 				if(SEARCH_IN_SECTION_NAME.equals(this.searchIn)){
-					System.out.println("Search in Section Name");
+					System.out.println("Section Search in Section Name");
 					this.completeSectionList = DBService.searchSection(terms);
+					
 				}else if(SEARCH_IN_BOOK_NAME.equals(this.searchIn)){
-					System.out.println("Search in Book Name");
+					System.out.println("Section Search in Book Name");
 					this.completeSectionList = DBService.searchBook(terms, "name");
-				}/*else if(SEARCH_FULL_TEXT.equals(this.searchIn)){
-					System.out.println("Search Full Text");
-					DBService.searchFullText(terms);
-				}*/
+
+				} /*else if (SEARCH_FULL_TEXT.equals(this.searchIn)) {
+					System.out.println("Full Text Search");
+					DBService.searchFullText(terms);	
+				}
+				*/
 				
 				Collections.sort(this.completeSectionList);
-				
 				filter();
-
 								
 			} catch (Exception e) {
 				internalError(e);
@@ -108,11 +111,11 @@
 		}
 	}
 	
-	public void filter(){
+	public void filter(){	
 		this.filteredSectionList = new ArrayList<DBSection>();
 		for(DBSection section : this.completeSectionList){
 			if(!this.filteredSectionList.contains(section)){
-				
+					
 				if( (StringUtils.isEmpty(dynastyFilter) || StringUtils.startsWith(section.getBook().getDynasty(), dynastyFilter)) &&
 						(StringUtils.isEmpty(level1Filter) || StringUtils.startsWith(section.getBook().getLevel1(), level1Filter)) &&
 						(StringUtils.isEmpty(adminTypeFilter) || StringUtils.startsWith(section.getBook().getAdmin_type(), adminTypeFilter))
@@ -121,17 +124,25 @@
 				}	
 			}
 		}
-		
+			
 		if(completeSectionList.size() > 0){
 			this.searchMessage = completeSectionList.size() + " section(s) found for the term(s): " + this.searchTerm;
 			this.filteringMessage = this.filteredSectionList.size() + " section(s) listed after the filtering";
+
 			this.paginator.setCurrentPage(0);
 			this.paginator.resetNumberOfPages(filteredSectionList.size());
-			this.updateCurrentSections();
+							
 		}else{
 			this.searchMessage = "No sections found for the term(s): " + this.searchTerm;
 			this.filteredSectionList = null;
+			this.filteringMessage = "";
+				
+			this.paginator.setCurrentPage(0);
+			this.paginator.resetNumberOfPages(0);
 		}
+			
+		this.updateCurrentSections();
+			
 	}
 	
 	private void updateCurrentSections() {
--- a/src/main/java/de/mpiwg/web/jsp/SessionBean.java	Fri Nov 20 15:37:04 2015 +0100
+++ b/src/main/java/de/mpiwg/web/jsp/SessionBean.java	Fri Dec 04 14:28:44 2015 +0100
@@ -32,7 +32,7 @@
 	private BranchPage branchPage = new BranchPage();
 	
 	private BooksPage booksPage = new BooksPage();
-	
+	private FullTextSearchPage fullTextSearchPage = new FullTextSearchPage();
 	
 	public SessionBean(){
 		logger.info("\n\n### SessionBean #####\n\n");
@@ -180,6 +180,16 @@
 	public void setBranchPage(BranchPage branchPage) {
 		this.branchPage = branchPage;
 	}
-	
-	
+
+
+	public FullTextSearchPage getFullTextSearchPage() {
+		return fullTextSearchPage;
+	}
+
+
+	public void setFullTextSearchPage(FullTextSearchPage fullTextSearchPage) {
+		this.fullTextSearchPage = fullTextSearchPage;
+	}
+
+
 }
--- a/src/main/resources/config.properties	Fri Nov 20 15:37:04 2015 +0100
+++ b/src/main/resources/config.properties	Fri Dec 04 14:28:44 2015 +0100
@@ -1,5 +1,7 @@
 db_gazetter_name=Gazetteer
 files_root=/gazetteer-server/data
+ftsearch_root=/gazetteer-server/ftsearch-data
+
 
 #Local
 db_gazetter_username=root
@@ -11,6 +13,8 @@
 toc_interface=http://localhost:1080/localgazetteers-dev/LGToc
 extraction_interface=http://localhost:1080/localgazetteers-dev/extraction-interface
 localgazetteers_dvId=185
+lgmap=http://localhost:1080/localgazetteers-dev/LGMap/map.php?mode=1
+lgmap_datasets=/Applications/MAMP/htdocs/localgazetteers-dev/LGMap/datasets
 
 # Production one:
 #localgazetteers-dev -> localgazetteers
@@ -22,6 +26,9 @@
 #root_server=http://localgazetteers.mpiwg-berlin.mpg.de/LGServices
 #toc_interface=http://localgazetteers.mpiwg-berlin.mpg.de/LGToc
 #extraction_interface=http://localgazetteers.mpiwg-berlin.mpg.de/extraction-interface
+#lgmap=http://localgazetteers.mpiwg-berlin.mpg.de/LGMap/map.php?mode=1
+#lgmap_datasets=/var/www/html/LGMap/datasets
+
 
 # Development one:
 #localgazetteers -> localgazetteers-test
@@ -34,3 +41,5 @@
 #toc_interface=http://localgazetteers-test.mpiwg-berlin.mpg.de/LGToc
 #extraction_interface=http://localgazetteers-test.mpiwg-berlin.mpg.de/extraction-interface
 #localgazetteers_dvId=2
+#lgmap=http://localgazetteers-test.mpiwg-berlin.mpg.de/LGMap/map.php?mode=1
+#lgmap_datasets=/var/www/html/LGMap/datasets
--- a/src/main/resources/hibernate.cfg.xml	Fri Nov 20 15:37:04 2015 +0100
+++ b/src/main/resources/hibernate.cfg.xml	Fri Dec 04 14:28:44 2015 +0100
@@ -36,7 +36,8 @@
 		 -->
 	    <mapping class="de.mpiwg.gazetteer.bo.LGBranch"/>
 	    <mapping class="de.mpiwg.gazetteer.bo.LGFile"/>
-		<mapping class="de.mpiwg.gazetteer.bo.Sequence"/>	    
+		<mapping class="de.mpiwg.gazetteer.bo.LGFullTextSearchFile"/>
+		<mapping class="de.mpiwg.gazetteer.bo.Sequence"/>	  	    
 	</session-factory>
 
 </hibernate-configuration>
--- a/src/main/webapp/componentes/paginator.jsp	Fri Nov 20 15:37:04 2015 +0100
+++ b/src/main/webapp/componentes/paginator.jsp	Fri Dec 04 14:28:44 2015 +0100
@@ -31,6 +31,9 @@
 				<% } else if (formName.equals("searchForm")) { %>
 					<%=sessionBean.getSearchPage().getPaginator().getRecordStatus() %>
 					
+				<% } else if (formName.equals("fullTextSearchForm")) { %>
+					<%=sessionBean.getFullTextSearchPage().getPaginator().getRecordStatus() %>
+				
 				<% } %>
 				</td>
 				<td>
--- a/src/main/webapp/componentes/template.jsp	Fri Nov 20 15:37:04 2015 +0100
+++ b/src/main/webapp/componentes/template.jsp	Fri Dec 04 14:28:44 2015 +0100
@@ -5,8 +5,7 @@
   	<script>
   		$(function() {
     		$( "#msgDialog" ).dialog();
-  		});
-  		
+  		}); 		
   	</script>	
 	
 	<div id="msgDialog" title="Message">
@@ -19,12 +18,21 @@
 	<% sessionBean.resetMsgList(); %>
 <% } %>
 	
+	<script>
+		$(document).ready(function() {
+			$("#loading").hide();
+		});
+
+  	</script>
+	
+	
+	<div id="loading">Loading...</div>
+	
 	<div id="header">
 		<div id="logo">
-			<h1><a href="#"><label class="titelPage">Local Gazetter Server</label></a></h1>
+			<h1><a href="#"><label class="titelPage">Local Gazetteers Services</label></a></h1>
 		</div>
 	</div>
-	
 	<div id="login">
 		<div id="loginContent">
 		
@@ -53,7 +61,7 @@
 					<td>Logged in as: <%=sessionBean.getUser().getUserName() %></td>
 					<td>|</td>
 					<td><input type="submit" value="logout" class="link" onclick="setAction('logout', 'loginForm');"/></td>
-					<td><a href="<%=sessionBean.getApplicationBean().getDvnRootServer() %>" target="_blank">Go To Dataverse</a></td>
+					<td><a href="<%=sessionBean.getApplicationBean().getDvnRootServer() %>" target="_blank">Go To LGDataverse</a></td>
 				</tr>
 			</table>
 			<% } %>
@@ -64,7 +72,13 @@
 	
 	<div class="menu">
 	    <a href="<%=sessionBean.getApplicationBean().getRootServer()%>/pages/home.jsp">Tasks</a>
+	    <!-- 
 	    <a href="<%=sessionBean.getApplicationBean().getRootServer()%>/pages/createFile.jsp" class="current">Create File</a>
+	    -->
 	    <a href="<%=sessionBean.getApplicationBean().getRootServer()%>/pages/search.jsp">Search</a>
+	    <a href="<%=sessionBean.getApplicationBean().getRootServer()%>/pages/fullTextSearch.jsp">Full Text Search</a>
 	    <a href="<%=sessionBean.getApplicationBean().getRootServer()%>/pages/books.jsp">Books</a>
-	</div>	
\ No newline at end of file
+	</div>	
+	
+	
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/webapp/pages/fullTextSearch.jsp	Fri Dec 04 14:28:44 2015 +0100
@@ -0,0 +1,604 @@
+<%@page import="de.mpiwg.gazetteer.bo.LGBranch"%>
+<%@page import="org.apache.commons.lang.StringUtils"%>
+<%@page import="de.mpiwg.gazetteer.db.DBSection"%>
+<%@page import="de.mpiwg.gazetteer.db.DBContents"%>
+<%@page import="de.mpiwg.gazetteer.bo.LGFullTextSearchFile"%>
+
+
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
+
+<jsp:useBean id="sessionBean" class="de.mpiwg.web.jsp.SessionBean" scope="session" />
+	
+
+<html>
+
+<head>
+
+	<jsp:include page="../componentes/headContent.jsp"/>	
+
+	<script>	
+		$(function() {
+		    $( "#dialogMoreInfo" ).dialog({
+		        autoOpen: false,
+		        modal: true,
+		        position: { my: "center", at: "top", of: window },
+		        
+		      });
+		    
+		    var dialogSave = $("#dialogSave").dialog(
+					{autoOpen: false}
+		  	);	   
+			$("#saveResult").button().on( "click", function() {
+				// append searchTerm into the form 		
+				$('<input>').attr({
+				    type: 'hidden',
+				    name: 'searchTerm',
+				    value: $("#searchTerm").val()
+				}).appendTo('form[name="saveTableForm"]');
+				
+				
+				dialogSave.dialog( "open" );
+			});
+			
+			var dialogViewSavedResult = $("#dialogViewSavedResult").dialog(
+					{autoOpen: false}
+		  	);	  
+			$("#viewSavedResult").button().on( "click", function() {
+				dialogViewSavedResult.dialog( "open" );
+			});
+			
+			
+		});
+		
+		// enter pressed event, we don't want to always go to "search".
+		$(document).keypress(
+			function(event){
+				if (event.which == '13') {	// enter pressed
+					// if any of the filter fields is filled in, filter first; otherwize, go to search
+					$(".filterInput" ).each(function( i ) {
+						//console.log( this.value );
+						if (this.value != "") {
+							//console.log('filtering' + i);
+							setAction('filter', 'fullTextSearchForm');
+							$("#fullTextSearchForm").submit();
+							return false;		
+						}
+						
+					});
+			    }
+		});
+		
+		$(document).ready(function(){
+			highlightKeywords();
+		})
+
+		function highlightKeywords()	// highlight keywords in content column, with class="content"
+		{	
+			if ($("#searchTerm")[0] == undefined ){
+				return;
+			}
+			var keywords = $("#searchTerm")[0].value;
+			var keywordsArray = keywords.split(", ");
+			//console.log("keywordsArray: "+keywordsArray);
+			
+			var content = $(".content");
+			for (var i = 0; i < content.length; i++) {
+				// find keywords in content[i]
+				var text = content[i].innerHTML;
+				for (var j = 0; j < keywordsArray.length; j++) {
+					var index = text.indexOf(keywordsArray[j]);
+					if (index >= 0) {
+						text = text.substring(0,index) + "<span class='highlight'>" + text.substring(index, index+keywordsArray[j].length) + "</span>" + text.substring(index+keywordsArray[j].length);
+						content[i].innerHTML = text;
+					}		
+				};
+
+			};
+		}
+		
+	</script>
+</head>
+
+<body>
+
+	<jsp:include page="../componentes/template.jsp"/>
+
+	<div id="dialogMoreInfo" title="Full Text Search Details"> </div>
+	
+	<div id="page">
+	
+		<% if(sessionBean.getUser() == null) { %>
+			<label>You must login!</label>
+		<% } else { 
+			
+			if (sessionBean.getFullTextSearchPage().getFileList() == null){ 
+				sessionBean.getFullTextSearchPage().loadParameters(request, response);
+				sessionBean.getFullTextSearchPage().forceLoadFileList();
+			}
+			
+			%>
+			
+			<div id="dialogSave" title="Save Table:">
+				<form name="saveTableForm" id="saveTableForm"
+						action="<%= sessionBean.getApplicationBean().getRootServer()%>/proxy.jsp"
+						method="post">
+						<input name="bean" type="hidden" value="fullTextSearchBean" /> 
+					<table>
+						<tr>
+							<td>
+								<input id="fileName" name="fileName" type="text" placeholder="table name"/>
+							</td>
+							<td>
+								<button onclick="setAction('save', 'saveTableForm'); document.getElementById('saveTableForm').submit();">Save</button> 
+								
+							</td>
+						</tr>						
+					</table>	
+				</form>
+			 	
+			</div>	
+			
+			<div id="dialogViewSavedResult" title="Saved Table List:">
+		
+				<table class="pageTable">
+					<tr>
+						<td class="tableTitle">Table name</td>
+						<td class="tableTitle">html</td>
+					</tr>
+					
+					<% for (LGFullTextSearchFile aFile : sessionBean.getFullTextSearchPage().getFileList() ){%>
+					<tr>
+						<td><%= aFile.getFileName() %></td>
+						<!-- getFullTextSearchFileText?fileId= &userId= -->
+						<td>
+							<a href="<%=sessionBean.getApplicationBean().getRootServer() %>/rest/text/getFullTextSearchHtmlFile?fileId=<%= aFile.getId() %>"
+										target="_blank">
+								<img alt="Show text in html" src="<%=sessionBean.getApplicationBean().getShowImage()%>"/>	
+							</a>
+						</td>
+						
+					</tr>
+					<% } %>
+					
+				</table>
+				
+		
+			</div>	
+		<label class="subTitel">Full Text Search</label> 
+
+			<form name="fullTextSearchForm" id="fullTextSearchForm"
+				action="<%=sessionBean.getApplicationBean().getRootServer()%>/proxy.jsp"
+				method="post"
+				class="contentForm">
+				<input name="bean" type="hidden" value="fullTextSearchBean" /> 
+	
+				<table style="width: 300px; margin-left: auto;margin-right: auto;">
+				<tr>
+					<td>
+						<input
+							id="searchTerm"
+							name="searchTerm"
+							type="text"
+							class="searchInput"
+							value="<%=sessionBean.getFullTextSearchPage().getSearchTerm()%>" />				
+					</td>
+					<td>
+						<input id="search"
+							type="image" 
+							onclick="setAction('search', 'fullTextSearchForm');"
+							src="<%=sessionBean.getApplicationBean().getSearchImage()%>"/>
+					</td>
+		
+				</tr>
+				
+				<tr><td><label class="label"><%= (StringUtils.isNotEmpty(sessionBean.getFullTextSearchPage().getSearchMessage())) ? sessionBean.getFullTextSearchPage().getSearchMessage() : ""%></label></td></tr>
+				<tr><td><label class="label"><%= (StringUtils.isNotEmpty(sessionBean.getFullTextSearchPage().getFilteringMessage())) ? sessionBean.getFullTextSearchPage().getFilteringMessage() : ""%></label></td></tr>
+				<tr><td>
+					<button id="saveResult" type="button" class="lgButton">Save Table</button>								
+					<button id="viewSavedResult" type="button" class="lgButton">View Saved Table(s)</button>
+				</td></tr>
+					
+				
+				</table>
+		
+		
+	
+			<%
+				if (sessionBean.getFullTextSearchPage().getCompleteList() != null) {
+			%>
+	
+			<!-- 
+			<jsp:include page="../componentes/paginator.jsp">
+				<jsp:param name="formName" value="fullTextSearchForm"/>
+			</jsp:include> 
+			-->
+			
+			<div class="tableDiv double-scroll">
+				<table class="pageTable">
+					<tbody>
+						<tr>
+							<th>
+								<table class="sortTable">
+									<tr>
+										<td><label class="tableTitle">#</label></td>
+										<td>
+											<table>
+												<tr><td>
+													<input type="image" 
+														onclick="setAction('sortByInxUp', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getUpImage()%>"/>										
+												</td></tr>
+												<tr><td>
+													<input type="image"
+														onclick="setAction('sortByInxDown', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getDownImage()%>"/>																				
+												</td></tr>
+											</table>
+										</td>
+									</tr>
+								</table>
+							</th>
+							<th>
+								<table class="sortTable">
+									<tr>
+										<td><label class="tableTitle">Book Id</label></td>
+										<td>
+											<table>
+												<tr><td>
+													<input type="image" 
+														onclick="setAction('sortByBookIdUp', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getUpImage()%>"/>										
+												</td></tr>
+												<tr><td>
+													<input type="image"
+														onclick="setAction('sortByBookIdDown', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getDownImage()%>"/>																				
+												</td></tr>
+											</table>
+										</td>
+									</tr>
+									<tr>
+										<td>
+											<input type="text" class="filterInput" name="bookIdFilter" id="bookIdFilter" value="<%= sessionBean.getFullTextSearchPage().getBookIdFilter()%>" size="8"/>
+										</td>									
+										<td>
+											<input type="image"
+												onclick="setAction('filter', 'fullTextSearchForm');"
+												src="<%=sessionBean.getApplicationBean().getFilterImage()%>"/>
+										</td>							
+									</tr>
+								</table>
+							</th>
+							<th>
+								<table class="sortTable">
+									<tr>
+										<td><label class="tableTitle">Book Name</label></td>
+										<td>
+											<table>
+												<tr><td>
+													<input type="image"
+														onclick="setAction('sortByBookNameUp', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getUpImage()%>"/>										
+												</td></tr>
+												<tr><td>
+													<input type="image"
+														onclick="setAction('sortByBookNameDown', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getDownImage()%>"/>																	
+												</td></tr>
+											</table>
+										</td>
+									</tr>
+									<tr>
+										<td>
+											<input type="text" class="filterInput" name="bookNameFilter" id="bookNameFilter" value="<%= sessionBean.getFullTextSearchPage().getBookNameFilter()%>" size="8"/>
+										</td>									
+										<td>
+											<input type="image"
+												onclick="setAction('filter', 'fullTextSearchForm');"
+												src="<%=sessionBean.getApplicationBean().getFilterImage()%>"/>
+										</td>							
+									</tr>
+								</table>					
+							</th>
+							<th>
+								<table class="sortTable">
+									<tr>
+										<td><label class="tableTitle">Level 1</label></td>
+										<td>
+											<table>
+												<tr><td>
+													<input type="image"
+														onclick="setAction('sortByLevel1Up', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getUpImage()%>"/>
+												</td></tr>
+												<tr><td>
+													<input type="image"
+														onclick="setAction('sortByLevel1Down', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getDownImage()%>"/>																				
+												</td></tr>
+											</table>
+										</td>
+									</tr>
+									<tr>
+										<td>
+											<input type="text" class="filterInput" name="level1Filter" id="level1Filter" value="<%= sessionBean.getFullTextSearchPage().getLevel1Filter()%>" size="8"/>
+										</td>									
+										<td>
+											<input type="image"
+												onclick="setAction('filter', 'fullTextSearchForm');"
+												src="<%=sessionBean.getApplicationBean().getFilterImage()%>"/>
+										</td>							
+									</tr>								
+								</table>	
+							</th>
+							<th>
+								<table class="sortTable">
+									<tr>
+										<td><label class="tableTitle">Level 2</label></td>
+										<td>
+											<table>
+												<tr><td>
+													<input type="image"
+														onclick="setAction('sortByLevel2Up', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getUpImage()%>"/>
+												</td></tr>
+												<tr><td>
+													<input type="image"
+														onclick="setAction('sortByLevel2Down', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getDownImage()%>"/>																				
+												</td></tr>
+											</table>
+										</td>
+									</tr>
+									<tr>
+										<td>
+											<input type="text" class="filterInput" name="level2Filter" id="level2Filter" value="<%= sessionBean.getFullTextSearchPage().getLevel2Filter()%>" size="8"/>
+										</td>									
+										<td>
+											<input type="image"
+												onclick="setAction('filter', 'fullTextSearchForm');"
+												src="<%=sessionBean.getApplicationBean().getFilterImage()%>"/>
+										</td>							
+									</tr>								
+								</table>
+							</th>
+							<th>
+								<table class="sortTable">
+									<tr>
+										<td><label class="tableTitle">Dynasty</label></td>
+										<td>
+											<table>
+												<tr><td>
+													<input type="image"
+														onclick="setAction('sortByDynastyUp', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getUpImage()%>"/>
+												</td></tr>
+												<tr><td>
+													<input type="image"
+														onclick="setAction('sortByDynastyDown', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getDownImage()%>"/>																				
+												</td></tr>
+											</table>
+										</td>
+									</tr>
+									<tr>
+										<td>
+											<input type="text" class="filterInput" name="dynastyFilter" id="dynastyFilter" value="<%= sessionBean.getFullTextSearchPage().getDynastyFilter()%>" size="8"/>
+										</td>									
+										<td>
+											<input type="image"
+												onclick="setAction('filter', 'fullTextSearchForm');"
+												src="<%=sessionBean.getApplicationBean().getFilterImage()%>"/>
+										</td>							
+									</tr>
+								</table>					
+							</th>
+							<th>
+								<table class="sortTable">
+									<tr>
+										<td><label class="tableTitle">Period</label></td>
+										<td>
+											<table>
+												<tr><td>
+													<input type="image"
+														onclick="setAction('sortByPeriodUp', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getUpImage()%>"/>										
+												</td></tr>
+												<tr><td>
+													<input type="image"
+														onclick="setAction('sortByPeriodDown', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getDownImage()%>"/>																				
+												</td></tr>
+											</table>
+										</td>
+									</tr>
+									<tr>
+										<td>
+											<input type="text" class="filterInput" name="periodFilter" id="periodFilter" value="<%= sessionBean.getFullTextSearchPage().getPeriodFilter()%>" size="8"/>
+										</td>									
+										<td>
+											<input type="image"
+												onclick="setAction('filter', 'fullTextSearchForm');"
+												src="<%=sessionBean.getApplicationBean().getFilterImage()%>"/>
+										</td>							
+									</tr>
+								</table>						
+							</th>
+							<th>
+								<table class="sortTable">
+									<tr>
+										<td><label class="tableTitle">Admin Type</label></td>
+										<td>
+											<table>
+												<tr><td>
+													<input type="image"
+														onclick="setAction('sortByAdminTypeUp', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getUpImage()%>"/>
+												</td></tr>
+												<tr><td>
+													<input type="image"
+														onclick="setAction('sortByAdminTypeDown', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getDownImage()%>"/>																				
+												</td></tr>
+											</table>
+										</td>
+									</tr>
+									<tr>
+										<td>
+											<input type="text" class="filterInput" name="adminTypeFilter" id="adminTypeFilter" value="<%= sessionBean.getFullTextSearchPage().getAdminTypeFilter()%>" size="8"/>
+										</td>									
+										<td>
+											<input type="image"
+												onclick="setAction('filter', 'fullTextSearchForm');"
+												src="<%=sessionBean.getApplicationBean().getFilterImage()%>"/>
+										</td>							
+									</tr>								
+								</table>	
+							</th>
+							
+							<th>
+								<table class="sortTable">
+									<tr>
+										<td><label class="tableTitle">Section Name</label></td>
+										<td>
+											<table>
+												<tr><td>
+													<input type="image"
+														onclick="setAction('sortBySectionNameUp', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getUpImage()%>"/>										
+												</td></tr>
+												<tr><td>
+													<input type="image"
+														onclick="setAction('sortBySectionNameDown', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getDownImage()%>"/>																				
+												</td></tr>
+											</table>
+										</td>
+									</tr>
+									<tr>
+										<td>
+											<input type="text" class="filterInput" name="sectionNameFilter" id="sectionNameFilter" value="<%= sessionBean.getFullTextSearchPage().getSectionNameFilter()%>" size="8"/>
+										</td>									
+										<td>
+											<input type="image"
+												onclick="setAction('filter', 'fullTextSearchForm');"
+												src="<%=sessionBean.getApplicationBean().getFilterImage()%>"/>
+										</td>							
+									</tr>
+								</table>	
+							</th>
+							<th>
+								<table class="sortTable">
+									<tr>
+										<td><label class="tableTitle">Section Pages</label></td>
+										<td>
+											<table>
+												<tr><td>
+													<input type="image"
+														onclick="setAction('sortBySectionNameUp', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getUpImage()%>"/>										
+												</td></tr>
+												<tr><td>
+													<input type="image"
+														onclick="setAction('sortBySectionNameDown', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getDownImage()%>"/>																				
+												</td></tr>
+											</table>
+										</td>
+									</tr>
+								</table>	
+							</th>
+							<th>
+								<table class="sortTable">
+									<tr>
+										<td><label class="tableTitle">Page</label></td>
+										<td>
+											<table>
+												<tr><td>
+													<input type="image"
+														onclick="setAction('sortByStartPageUp', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getUpImage()%>"/>										
+												</td></tr>
+												<tr><td>
+													<input type="image"
+														onclick="setAction('sortByStartPageDown', 'fullTextSearchForm');"
+														src="<%=sessionBean.getApplicationBean().getDownImage()%>"/>																				
+												</td></tr>
+											</table>
+										</td>
+									</tr>
+								</table>							
+							
+							</th>
+							<th style="min-width:300px"><label class="tableTitle">Content</label></th>
+							<th><label class="tableTitle">Select rows</th>
+						</tr>
+					
+		
+						<%
+						for (DBContents content : sessionBean.getFullTextSearchPage().getFilteredList() ) {
+						%>
+						
+						<% if ( content.isRemoved() ) { %>
+						<tr class="removedContent">	
+						<% } else { %>
+						<tr>	
+						<% } %>
+									
+							<td><%=content.getInx() %></td>
+							<td><%=content.getSection().getBook().getId() %></td>
+							<td><%=content.getSection().getBook().getName()%></td>
+							<td><%=content.getSection().getBook().getLevel1()%></td>
+							<td><%=content.getSection().getBook().getLevel2()%></td>
+							<td><%=content.getSection().getBook().getDynasty()%></td>
+							<td><%=content.getSection().getBook().getPeriod()%></td>
+							<td><%=content.getSection().getBook().getAdmin_type() %></td>
+							<td><%=content.getSection().getName() %></td>
+							<td><%=content.getSection().getPages()%></td>
+							<td><%=content.getPage() %></td>
+							<td class="content"><%=content.getContent()%></td>
+							<td>
+								<% if ( content.isRemoved() ) { %>
+	
+									<input type="image"	onclick="setAction0('recoverFocusedContent', 'fullTextSearchForm', 'focusedContentId', '<%=content.getId() %>');"	
+										src="<%=sessionBean.getApplicationBean().getCheckboxUncheckedImage()%>" width="20" height="20"/>
+										
+								<% } else { %>
+							
+									<input type="image" onclick="setAction0('removeFocusedContent', 'fullTextSearchForm', 'focusedContentId', '<%=content.getId() %>');"	
+										src="<%=sessionBean.getApplicationBean().getCheckboxCheckedImage()%>" width="20" height="20"/>
+								
+								<% } %>
+							
+									
+								
+							</td>
+							
+						</tr>
+						<%
+							}
+						%>
+						
+					</tbody>
+				</table>
+		
+				
+			<%
+				}
+			%>
+			
+			</div>
+			<!-- 
+			<jsp:include page="../componentes/paginator.jsp">
+				<jsp:param name="formName" value="fullTextSearchForm"/>
+			</jsp:include> 
+			 -->
+			 
+		</form>
+	
+		<% } %>
+		
+	</div>
+
+</body>
+</html>
--- a/src/main/webapp/pages/home.jsp	Fri Nov 20 15:37:04 2015 +0100
+++ b/src/main/webapp/pages/home.jsp	Fri Dec 04 14:28:44 2015 +0100
@@ -101,7 +101,7 @@
 									</tr>
 									<tr>
 										<td>
-											<input type="text" name="bookIdFilter" value="<%= sessionBean.getHomePage().getBookIdFilter()%>" size="10"/>
+											<input type="text" name="bookIdFilter" value="<%= sessionBean.getHomePage().getBookIdFilter()%>" size="8"/>
 										</td>									
 										<td>
 											<input type="image"
@@ -132,7 +132,7 @@
 									</tr>
 									<tr>
 										<td>
-											<input type="text" name="bookNameFilter" value="<%= sessionBean.getHomePage().getBookNameFilter()%>" size="10"/>
+											<input type="text" name="bookNameFilter" value="<%= sessionBean.getHomePage().getBookNameFilter()%>" size="8"/>
 										</td>									
 										<td>
 											<input type="image"
@@ -164,7 +164,7 @@
 									</tr>
 									<tr>
 										<td>
-											<input type="text" name="level1Filter" value="<%= sessionBean.getHomePage().getLevel1Filter()%>" size="10"/>
+											<input type="text" name="level1Filter" value="<%= sessionBean.getHomePage().getLevel1Filter()%>" size="8"/>
 										</td>									
 										<td>
 											<input type="image"
@@ -195,7 +195,7 @@
 									</tr>
 									<tr>
 										<td>
-											<input type="text" name="level2Filter" value="<%= sessionBean.getHomePage().getLevel2Filter()%>" size="10"/>
+											<input type="text" name="level2Filter" value="<%= sessionBean.getHomePage().getLevel2Filter()%>" size="8"/>
 										</td>									
 										<td>
 											<input type="image"
@@ -226,7 +226,7 @@
 									</tr>
 									<tr>
 										<td>
-											<input type="text" name="dynastyFilter" value="<%= sessionBean.getHomePage().getDynastyFilter()%>" size="10"/>
+											<input type="text" name="dynastyFilter" value="<%= sessionBean.getHomePage().getDynastyFilter()%>" size="8"/>
 										</td>									
 										<td>
 											<input type="image"
@@ -257,7 +257,7 @@
 									</tr>
 									<tr>
 										<td>
-											<input type="text" name="periodFilter" value="<%= sessionBean.getHomePage().getPeriodFilter()%>" size="10"/>
+											<input type="text" name="periodFilter" value="<%= sessionBean.getHomePage().getPeriodFilter()%>" size="8"/>
 										</td>									
 										<td>
 											<input type="image"
@@ -288,7 +288,7 @@
 									</tr>
 									<tr>
 										<td>
-											<input type="text" name="adminTypeFilter" value="<%= sessionBean.getHomePage().getAdminTypeFilter()%>" size="10"/>
+											<input type="text" name="adminTypeFilter" value="<%= sessionBean.getHomePage().getAdminTypeFilter()%>" size="8"/>
 										</td>									
 										<td>
 											<input type="image"
@@ -319,7 +319,7 @@
 									</tr>
 									<tr>
 										<td>
-											<input type="text" name="sectionNameFilter" value="<%= sessionBean.getHomePage().getSectionNameFilter()%>" size="10"/>
+											<input type="text" name="sectionNameFilter" value="<%= sessionBean.getHomePage().getSectionNameFilter()%>" size="8"/>
 										</td>									
 										<td>
 											<input type="image"
@@ -371,7 +371,7 @@
 									</tr>
 									<tr>
 										<td>
-											<input type="text" name="labelFilter" size="20" value="<%= sessionBean.getHomePage().getLabelFilter()%>" size="10"/>
+											<input type="text" name="labelFilter" size="20" value="<%= sessionBean.getHomePage().getLabelFilter()%>" size="8"/>
 										</td>									
 										<td>
 											<input type="image"
--- a/src/main/webapp/pages/search.jsp	Fri Nov 20 15:37:04 2015 +0100
+++ b/src/main/webapp/pages/search.jsp	Fri Dec 04 14:28:44 2015 +0100
@@ -152,6 +152,8 @@
 			});		
 				
 		});
+	
+		
 	</script>
 </head>
 
@@ -171,7 +173,7 @@
 			
 		<label class="subTitel">Search for Sections</label>
 		
-		<form name="searchForm"
+		<form name="searchForm" id="searchForm"
 			action="<%=sessionBean.getApplicationBean().getRootServer()%>/proxy.jsp"
 			method="post"
 			class="contentForm">
@@ -283,7 +285,7 @@
 									</tr>
 									<tr>
 										<td>
-											<input type="text" name="level1Filter" id="level1Filter" value="<%= sessionBean.getSearchPage().getLevel1Filter()%>"/>
+											<input type="text" class="filterInput" name="level1Filter" id="level1Filter" value="<%= sessionBean.getSearchPage().getLevel1Filter()%>"/>
 										</td>									
 										<td>
 											<input type="image"
@@ -315,7 +317,7 @@
 									</tr>
 									<tr>
 										<td>
-											<input type="text" name="dynastyFilter" id="dynastyFilter" value="<%= sessionBean.getSearchPage().getDynastyFilter()%>"/>
+											<input type="text" class="filterInput" name="dynastyFilter" id="dynastyFilter" value="<%= sessionBean.getSearchPage().getDynastyFilter()%>"/>
 										</td>									
 										<td>
 											<input type="image"
@@ -367,7 +369,7 @@
 									</tr>
 									<tr>
 										<td>
-											<input type="text" name="adminTypeFilter" id="adminTypeFilter" value="<%= sessionBean.getSearchPage().getAdminTypeFilter()%>"/>
+											<input type="text" class="filterInput" name="adminTypeFilter" id="adminTypeFilter" value="<%= sessionBean.getSearchPage().getAdminTypeFilter()%>"/>
 										</td>									
 										<td>
 											<input type="image"
Binary file src/main/webapp/resources/.DS_Store has changed
--- a/src/main/webapp/resources/css/style.css	Fri Nov 20 15:37:04 2015 +0100
+++ b/src/main/webapp/resources/css/style.css	Fri Dec 04 14:28:44 2015 +0100
@@ -266,4 +266,28 @@
 {
     width:2px;
     padding:0 0;
+}
+
+#loading{
+	position:fixed;
+	z-index:2000;
+	background:rgba(0,0,0,0.5);
+	color:#fff;
+	width:100vw;
+	height:100vh;
+	padding:300px 0;
+	text-align:center;
+	vertical-align:middle;
+	font-size:20px;
+	line-height:20px;
+}
+
+.highlight
+{
+	background-color:yellow;
+}
+.removedContent {
+	opacity:0.3;
+	color: transparent;
+	text-shadow: 0 0 3px rgba(0,0,0,0.8);
 }
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/webapp/resources/js/LGSearch.js	Fri Dec 04 14:28:44 2015 +0100
@@ -0,0 +1,27 @@
+$(document).ready(function(){
+	highlightKeywords();
+})
+
+function highlightKeywords()	// highlight keywords in content column, with class="content"
+{	
+	if ($("#searchTerm")[0] == undefined ){
+		return;
+	}
+	
+	var keywords = $("#searchTerm")[0].innerHTML;
+	var keywordsArray = keywords.split(", ");
+	
+	var content = $(".content");
+	for (var i = 0; i < content.length; i++) {
+		// find keywords in content[i]
+		var text = content[i].innerHTML;
+		for (var j = 0; j < keywordsArray.length; j++) {
+			var index = text.indexOf(keywordsArray[j]);
+			if (index >= 0) {
+				text = text.substring(0,index) + "<span class='highlight'>" + text.substring(index, index+keywordsArray[j].length) + "</span>" + text.substring(index+keywordsArray[j].length);
+				content[i].innerHTML = text;
+			}		
+		};
+
+	};
+}
\ No newline at end of file
--- a/src/main/webapp/resources/js/general.js	Fri Nov 20 15:37:04 2015 +0100
+++ b/src/main/webapp/resources/js/general.js	Fri Dec 04 14:28:44 2015 +0100
@@ -101,4 +101,5 @@
 
 $(document).ready(function() {
 	$('.double-scroll').doubleScroll();
+
 });
\ No newline at end of file
--- a/src/main/webapp/resources/js/proxyMethods.js	Fri Nov 20 15:37:04 2015 +0100
+++ b/src/main/webapp/resources/js/proxyMethods.js	Fri Dec 04 14:28:44 2015 +0100
@@ -1,6 +1,7 @@
-
 
 function setAction(action, formName){
+	$("#loading").show();
+  	
 	var theForm = document.forms[formName];
     var input = document.createElement('input');
     input.type = 'hidden';