/**
 * Der Builder erzeugt alle HTML-Objekte und führt notwendige
 * DOM-Operationen für die Searchalyzr Schnittstelle aus.
 * 
 * Die Struktur der erzeugten Objekte kann nach außen unbekannt bleiben
 * ("Blackbox"-Objekt), da der Builder letztlich der einzige ist, der
 * auf den Objekten arbeitet.
 *  
 */
var SearchalyzrBuilder = Class.create({	
	translate: function(key) {
		var v = null;
		if (this.l10n && this.l10n[key]) {
			v = this.l10n[key];
		} else if (this._l10n[key]) {
			v = this._l10n[key];
		}
		if (Object.isUndefined(v))
			throw new "Missing l10n key: " + key;
		return v;
	},

	/**
	 * Builder initialisieren.
	 * 
	 * @param l10n [Object]
	 * Lokales Sprachset.
	 */
	initialize: function(l10n) {
		this._l10n = {
				resultHeadline: "Suchergebnis",
				prevPage: "zur vorherigen Seite wechseln",
				nextPage: "zur nächsten Seite wechseln",
				show: "zeige",
				to: "bis",
				from: "von",
				entries: "Treffer",
				emptyMessage: "In dem System \"#{systemLabel}\" wurden keine passenden Einträge gefunden.",
				ascending: 'Aufsteigend',
				descending: 'Absteigend'
		};
		
		this.l10n = l10n;
	},
	
	/**
	 * UrlOverwrite-Attribut für die Links
	 */
	urlOverwrite: null,

	/**
	 * Setzt das UrlOverwrite-Attribut für die Links
	 *
	 * @param urlOverwrite [Object]
	 */
	setUrlOverwrite: function (urlOverwrite) {
		this.urlOverwrite =  urlOverwrite;
	},

	/**
	 * Gibt den Image-Pfad für den Sortierreihenfolge-Indicator zurück.
	 * 
	 * @return String
	 */
	getAscendingIndicator: function(active, ascending) {
		return Builder.node('span', {
			'class': 'sorting',
			'title': (active ? (ascending ? this.translate('ascending') : this.translate('descending')) : '')
		}, [
		    (active ? (ascending ? '\u25B2' : '\u25BC') : '\u2666' )
		]);
	},

	/**
	 * Gibt den Image-Pfad für den Lade-Indicator zurück.
	 * 
	 * @return String
	 */
	getLoadingIndicatorImage: function() {
		return '/jslib/searchalyzr/loading.gif';
	},
	
	/**
	 * Fügt den Tab in einen Container hinzu.
	 *
	 * @param container [DOMObject]
	 * Container Element, das die Tab-Container halten soll.
	 *
	 * @param title [Object]
	 * Tab-Title-Element, das mit createTabTitle erzeugt wurde.
	 *
	 * @param content [Object]
	 * Tab-Title-Element, das mit createTabContent erzeugt wurde.
	 *
	 * @return void
	 */
	addTabContainer: function(container, title, content) {
		Element.extend(container);
		Element.extend(title);
		Element.extend(content);
		container.insert(title);
		container.insert(content);
	},

	/**
	 * Erzeugt einen Container für die Tab-Title's.
	 *
	 * @return Object
	 */
	createTabTitleContainer: function() {
		return Builder.node(
				'div',
				{ className: 'tabContainer' });
	},

	/**
	 * Erzeugt ein Tab-Title-Element.
	 *
	 * @return Object (kein DOM-Node, sondern "Benutzer-"objekt)s
	 */
	createTabTitle: function(system, label) {
		var title = {};
		title.label = Builder.node('span', [ label ]);
		title.loader = Builder.node('img', { src: this.getLoadingIndicatorImage(), alt: '', 'class': 'loading-indicator', style: 'display: none;' });
		title.counter = Builder.node('span', { 'class': 'counter' })
		title.container = Builder.node(
				'div',
				{ 'id': system + '-tab', className: 'tabTitle' },
				[ title.label, title.loader, title.counter ]);
		Element.extend(title.label);
		Element.extend(title.loader);
		Element.extend(title.counter);
		Element.extend(title.container);
		/*if (!window.oldAlert) {
			window.oldAlert = window.alert;
			window.alert=function(m){
				window.oldAlert(m + "\n" + arguments.callee.caller.toString());
			};
		}*/
		return title;
	},

	/**
	 * Fügt das Tab-Title-Element einen Action Listener hinzu.
	 *
	 * @param title [Object]
	 * Tab-Title-Element, das mit createTabTitle erzeugt wurde.
	 *
	 * @param func [function]
	 * Event-Handler-Function.
	 *
	 * @return void
	 */
	addTabAction: function(title, func) {
		Event.observe(title.container, 'click', func);
	},
	
	/**
	 * Aktualisiert den Count Wert im Tab Title.
	 *
	 * @param title [Object]
	 * Tab-Title-Element, das mit createTabTitle erzeugt wurde.
	 *
	 * @param count [integer]
	 * Count-Wert.
	 *
	 * @return void
	 */
	updateTabCount: function(title, count) {
		title.loader.hide();
		title.counter.show();
		title.counter.update(' (' + count + ')');
	},

	/**
	 * Lässt den Count-Wert verschwinden und zeigt den Ladeindicator an.
	 *
	 * @param title [Object]
	 * Tab-Title-Element, das mit createTabTitle erzeugt wurde.
	 *
	 * @return void
	 */
	setTabLoading: function(title) {
		Element.extend(title.loader);
		Element.extend(title.counter);
		title.loader.show();
		title.counter.hide();
	},

	/**
	 * Erzeugt einen Container für die Tab-Contents's.
	 *
	 * @return Object
	 */
	createTabContentContainer: function() {
		return Builder.node(
				'div',
				{ className: 'tabContentContainer' });
	},

	/**
	 * Erzeugt ein Tab-Content-Element.
	 *
	 * @return DOMObject
	 */
	createTabContent: function(system, label, header) {
		var div = Builder.node(
				'div',
				{ 'id': system + '-content', className: 'tabContent' });
		var div2 = Builder.node('div');
		if (header)
			div2.innerHTML = header;
		div.appendChild(div2);
		return div;		
	},

	/**
	 * Fügt das Tab-Title-Element in den Tab-Title-Container ein.
	 *
	 * @param container [Object]
	 * Tab-Container-Element, das mit createTabTitleContainer erzeugt wurde.
	 *
	 * @param tab [Object]
	 * Tab-Title-Element, das mit createTabTitle erzeugt wurde.
	 *
	 * @return void
	 */
	addTabTitle: function(container, tab) {
		Element.extend(container);
		Element.extend(tab.container);
		container.insert(tab.container);
	},

	/**
	 * Fügt das Tab-Content-Element in den Tab-Content-Container ein.
	 *
	 * @param container [Object]
	 * Tab-Container-Element, das mit createTabContentContainer erzeugt wurde.
	 *
	 * @param tab [Object]
	 * Tab-Title-Element, das mit createTabContent erzeugt wurde.
	 *
	 * @return void
	 */
	addTabContent: function(container, tab) {
		Element.extend(container);
		Element.extend(tab);
		container.insert(tab);
	},

	/**
	 * Prüft ob der Tab sichtbar ist.
	 *
	 * @param title [Object]
	 * Tab-Title-Element, das mit createTabTitle erzeugt wurde.
	 *
	 * @param content [Object]
	 * Tab-Title-Element, das mit createTabContent erzeugt wurde.
	 *
	 * @return boolean
	 */
	isTabVisible: function(title, content) {
		Element.extend(title.container);
		return title.container.visible();
	},

	/**
	 * Macht den Tab sichtbar.
	 *
	 * @param title [Object]
	 * Tab-Title-Element, das mit createTabTitle erzeugt wurde.
	 *
	 * @param content [Object]
	 * Tab-Title-Element, das mit createTabContent erzeugt wurde.
	 *
	 * @return void
	 */
	showTab: function(title, content) {
		Element.extend(title.container);
		Element.extend(content);
		title.container.show();
		content.show();
	},

	/**
	 * Macht den Tab unsichtbar.
	 *
	 * @param title [Object]
	 * Tab-Title-Element, das mit createTabTitle erzeugt wurde.
	 *
	 * @param content [Object]
	 * Tab-Title-Element, das mit createTabContent erzeugt wurde.
	 *
	 * @return void
	 */
	hideTab: function(title, content) {
		title.container.hide();
		content.hide();
	},

	/**
	 * Prüft ob der Tab aktiv ist.
	 *
	 * @param title [Object]
	 * Tab-Title-Element, das mit createTabTitle erzeugt wurde.
	 *
	 * @param content [Object]
	 * Tab-Title-Element, das mit createTabContent erzeugt wurde.
	 *
	 * @return boolean
	 */
	isTabActive: function(title, content) {
		return title.container.hasClassName('active-tab-title');
	},

	/**
	 * Aktiviert den Tab.
	 *
	 * @param title [Object]
	 * Tab-Title-Element, das mit createTabTitle erzeugt wurde.
	 *
	 * @param content [Object]
	 * Tab-Title-Element, das mit createTabContent erzeugt wurde.
	 *
	 * @return void
	 */
	activateTab: function(title, content) {
		title.container.addClassName('active-tab-title');
		content.addClassName('active-tab');
	},

	/**
	 * Deaktiviert den Tab.
	 *
	 * @param title [Object]
	 * Tab-Title-Element, das mit createTabTitle erzeugt wurde.
	 *
	 * @param content [Object]
	 * Tab-Title-Element, das mit createTabContent erzeugt wurde.
	 *
	 * @return void
	 */
	deactivateTab: function(title, content) {
		title.container.removeClassName('active-tab-title');
		content.removeClassName('active-tab');
	},

	/**
	 * Erzeugt einen Container für eine Kategorie mit Titel.
	 * 
	 * @param title [string]
	 * Titel der Kategorie.
	 *
	 * @return Object
	 */
	createCategory: function(title) {
		var category = {};
		category.title = title ? Builder.node('h1', [ title ]) : Builder.node('h1', { className: "empty" });
		category.container = Builder.node('div',
				{ className: 'category' },
				[ category.title ]);
		Element.extend(category.title);
		Element.extend(category.container);
		return category;
	},

	/**
	 * Fügt einen Kategorie-Container in den Kategorien-Container ein.
	 *
	 * @param tab [Object]
	 * Tab-Content-Element, das mit createTabContent erzeugt wurde.
	 *
	 * @param category [Object]
	 * Kategorie-Element, das mit createCategory erzeugt wurde.
	 *
	 * @return void
	 */
	addCategory: function(tab, category) {
		Element.extend(tab);
		Element.extend(category.container);
		tab.insert(category.container);
	},

	/**
	 * Fügt einen Kategorie-Container erneut in den Kategorien-Container ein,
	 * um eine korrekte Reihenfolge zu garantieren.
	 *
	 * @param tab [Object]
	 * Tab-Content-Element, das mit createTabContent erzeugt wurde.
	 *
	 * @param category [Object]
	 * Kategorie-Element, das mit createCategory erzeugt wurde.
	 *
	 * @return void
	 */
	readdCategory: function(tab, category) {
		Element.extend(tab);
		Element.extend(category.container);
		tab.insert(category.container);
	},
	
	/**
	 * Versteckt den Kategorie-Container.
	 *
	 * @param category [Object]
	 * Kategorie-Element, das mit createCategory erzeugt wurde.
	 *
	 * @return void
	 */
	hideCategory: function(category) {
		category.container.hide();
	},
	
	/**
	 * Zeigt den Kategorie-Container wieder an.
	 *
	 * @param category [Object]
	 * Kategorie-Element, das mit createCategory erzeugt wurde.
	 *
	 * @return void
	 */
	showCategory: function(category) {
		category.container.show();
	},

	/**
	 * Prüft ob der Kategorie-Container sichtbar ist.
	 *
	 * @param category [Object]
	 * Kategorie-Element, das mit createCategory erzeugt wurde.
	 *
	 * @return boolean
	 */
	isCategoryVisible: function(category) {
		return category.container.visible();
	},
	
	/**
	 * Versteckt den Kategorie-Title.
	 *
	 * @param category [Object]
	 * Kategorie-Element, das mit createCategory erzeugt wurde.
	 *
	 * @return void
	 */
	hideCategoryTitle: function(category) {
		category.title.hide();
	},
	
	/**
	 * Zeigt den Kategorie-Titel wieder an.
	 *
	 * @param category [Object]
	 * Kategorie-Element, das mit createCategory erzeugt wurde.
	 *
	 * @return void
	 */
	showCategoryTitle: function(category) {
		category.title.show();
	},
	
	/**
	 * Erzeugt ein Indicator-Element, dass anzeigt, dass der Tab leer ist.
	 * 
	 * @return Object
	 */
	createEmptyIndicator: function(id, label, url) {
		var template = new Template(this.translate('emptyMessage'));
		var fields = { systemId: id, systemLabel: label, systemUrl: url };
		return Builder.node('div',
				{ 'class': 'empty-indicator', style: 'display: none' },
				[ template.evaluate(fields) ]);
	},
	
	/**
	 * Versteckt den 'System hat keine Treffer'-Indicator.
	 * 
	 * @param [Object] indicator
	 * Der Indicator, der mit createEmptyIndicator erzeugt wurde.
	 * 
	 * @return void
	 */
	hideEmptyIndicator: function(indicator) {
		indicator.hide();
	},

	/**
	 * Zeigt den 'System hat keine Treffer'-Indicator wieder an.
	 * 
	 * @param [Object] indicator
	 * Der Indicator, der mit createEmptyIndicator erzeugt wurde.
	 * 
	 * @return void
	 */
	showEmptyIndicator: function(indicator) {
		indicator.show();
	},

	/**
	 * Fügt den 'System hat keine Treffer'-Indicator dem System-Container hinzu.
	 * 
	 * @param tab [Object]
	 * Tab-Content-Element, das mit createTabContent erzeugt wurde.
	 *
	 * @param [Object] indicator
	 * Der Indicator, der mit createEmptyIndicator erzeugt wurde.
	 * 
	 * @return void
	 */
	addEmptyIndicator: function(tab, indicator) {
		tab.insert(indicator);
	},
	
	
	/**
	 * Erzeugt ein ErrorMessage-Element, dass im Fehlerfall angezeigt wird
	 * 
	 * @return Object
	 */
	createErrorMessage: function (id){
		return Builder.node('div',
				{ 'class': 'errorMessage', style: 'display: none' });
	},
	
	/**
	 * Versteckt die ErrorMessage
	 * 
	 * @param [Object] errorMessage
	 * Die ErrorMessage, der mit createErrorMessage erzeugt wurde.
	 * 
	 * @return void
	 */
	hideErrorMessage: function(errorMessage) {
		errorMessage.hide();
	},

	/**
	 * Zeigt die Error-Message wieder an.
	 * 
	 * @param [Object] errorMessage
	 * Das ErrorMessage Objekt, der mit createErrorMessage erzeugt wurde.
	 * 
	 * @param [String] message Die Fehlermeldung
	 * 
	 * @return void
	 */
	showErrorMessage: function(errorMessage, message) {
		errorMessage.update(message);
		errorMessage.show();
	},

	/**
	 * Fügt das ErrorMessage Objekt dem System-Container hinzu.
	 * 
	 * @param tab [Object]
	 * Tab-Content-Element, das mit createTabContent erzeugt wurde.
	 *
	 * @param [Object] errorMessage
	 * Das ErrorMessageObjekt, der mit createErrorMessage erzeugt wurde.
	 * 
	 * @return void
	 */
	addErrorMessage: function(tab, errorMessage) {
		tab.insert(errorMessage);
	},
	
	/**
	 * Erzeugt ein neues Tabellen-Element.
	 * 
	 * @return Object
	 */
	createTable: function() {
		return Builder.node('table');
	},

	/**
	 * Fügt die Tabelle einem Kategorie-Container hinzu.
	 * 
	 * @param container [Object]
	 * Kategorie-Element, das mit createCategory erzeugt wurde.
	 * 
	 * @param table [Object]
	 * Tabellen-Element, das mit createTable erzeugt wurde.
	 * 
	 * @return void
	 */
	addTable: function(container, table) {
		container.container.insert(table);
	},
	
	/**
	 * Versteckt die Tabelle.
	 * 
	 * @param table [Object]
	 * Tabellen-Element, das mit createTable erzeugt wurde.
	 * 
	 * @return void
	 */
	hideTable: function(table) {
		table.hide();
	},
	
	/**
	 * Zeigt die Tabelle wieder an.
	 * 
	 * @param table [Object]
	 * Tabellen-Element, das mit createTable erzeugt wurde.
	 * 
	 * @return void
	 */
	showTable: function(table) {
		table.show();
	},
	
	/**
	 * Aktualisiert die Tabellen-Header.
	 * 
	 * @param table [Object]
	 * Tabellen-Element, das mit createTable erzeugt wurde.
	 * 
	 * @param headers [Array<Object>]
	 * Header-Definitionen:
	 * [{ label: <string>, align: <string>, sortable: <boolean>, id: <string> }, ...]
	 * 
	 * @param sortable [Boolean]
	 * Bestimmt, ob Sortierung allgemein (Boolean) aktiviert werden soll.
	 * 
	 * @param sorting [Object]
	 * Aktuelle Sortierung, bestehend aus 2 Werten. columnId (String) für die Spalten-ID und ascending (Boolean) für die Reihenfolge.
	 * 
	 * @param sortFunction [Function]
	 * Funktion, die zum Sortieren aufgerufen wird, die Funktion muss 2 Parameter erwarten.
	 * Der erste setzt die Spalten-ID, der zweite ob das Ergebnis umgekehrt werden soll (=> false := Aufsteigend | true := Absteigend).
	 * 
	 * @return void
	 */
	updateTableHeaders: function(table, headers, sorting, container) {
		var self = this;
		if (!sorting) {
			sorting = { columnId: null, ascending: true };
		}
		// Tabellenkopf neu erstellen
		var tr = Builder.node('tr');
		Element.extend(tr);
		var active;
		$A(headers).each(function(item) {
			var th = Builder.node('th', { id: item.id }, [ item.label ])
			if (item.sortable === true) {
				th.addClassName('sortable');
				active = sorting.columnId == item.id;
				th.insert(self.getAscendingIndicator(active, sorting.ascending));
				th.addClassName('sorted-' + (active ? (sorting.ascending ? 'ascending' : 'descending') : 'unsorted'))
				th.container=container;
				th.sorting=sorting;
				th.columnId=item.id;
				Event.observe(th, 'click', function(event) {
					Event.stop(event);
					if (this.sorting.columnId == this.columnId){
						this.sorting.ascending = !this.sorting.ascending;
					} else {
						this.sorting.ascending = true;
						this.sorting.columnId = this.columnId;
					}
					this.container.refreshFunc();
				});
			}
			tr.insert(th);
		});
		
		// Tabellenkopf aktualisieren
		if (!table.tHead)
			table.insert(Builder.node('thead'));
		Element.extend(table.tHead);
		table.tHead.update(tr);
	},
	
	/**
	 * Aktualisiert die Tabellen-Daten.
	 * 
	 * @param table [Object]
	 * Tabellen-Element, das mit createTable erzeugt wurde.
	 * 
	 * @param rows [Array<Object>]
	 * Daten-Definitionen:
	 * [
	 *   {
	 *     cells: [
	 *       { linkTarget: <string>, text: <string>, style: <string>, link: <string> },
	 *       ...
	 *     ]
	 *   },
	 *   ...
	 * ]
	 * 
	 * @return void
	 */
	updateTableData: function(table, rows, system) {		
		var tbody = Builder.node('tbody');
		Element.extend(tbody);
		var n = 0;
		var that = this;
		rows.each(function(row) {
			Element.extend(row);
			var tr = Builder.node('tr', { className: (n++%2==0 ? 'even' : 'odd') });
			Element.extend(tr);
			var m = 0;
			row.cells.each(function(cell) {
				Element.extend(cell);
				var td = Builder.node('td', { className: (m++%2==0 ? 'even' : 'odd') });
				td.style.cssText = cell.style;
				Element.extend(td);
				var obj;
				if (cell.link) {
					if(system.linkTarget != null) {
						cell.linkTarget = system.linkTarget;
					}
					if (cell.link.indexOf('javascript:') != -1) {
						// bei javascript-links target entfernen
						cell.linkTarget = '';
					}
					if(system.isUrlOverwrite()) {
						if (cell.link.startsWith(system.getOldUrl())) {
							cell.link = cell.link.gsub(system.getOldUrl(), system.getNewUrl());
						}
					}
					obj = Builder.node(
							'a',
							{ href: cell.link, target: cell.linkTarget});
				} else {
					obj = Builder.node('span');					
					if(system.isUrlOverwrite()) {						
						cell.text = cell.text.gsub(system.getOldUrl(), system.getNewUrl());						
					}
					if(system.linkTarget != null) {
						var newTarget = "target=\"" + system.linkTarget + "\"";
						var newTargetWithAnchor = "<a " + newTarget;
						var regex = /target="[^"]+"/ig;
						if(regex.test(cell.text)) {
							cell.text = cell.text.replace(regex, newTarget);
						} else {
							var regex2=/<a/ig;
							cell.text = cell.text.replace(regex2, newTargetWithAnchor);								
						}
					}
				}
				Element.extend(obj);
				obj.update(cell.text);
				td.insert(obj);
				tr.insert(td);
			});
			tbody.insert(tr);
		});
		while(table.tBodies && table.tBodies.length) {
			$(table.tBodies[0]).remove();
		};
		table.insert(tbody);
	},

	/**
	 * Erzeugt ein neues Listen-Element.
	 * 
	 * @return Object
	 */
	createList: function() {
		return Builder.node('ul');
	},
	
	/**
	 * Fügt die Liste einem Kategorie-Container hinzu.
	 * 
	 * @param container [Object]
	 * Kategorie-Element, das mit createCategory erzeugt wurde.
	 * 
	 * @param list [Object]
	 * Listen-Element, das mit createList erzeugt wurde.
	 * 
	 * @return void
	 */
	addList: function(container, list) {
		container.container.insert(list);
	},
	
	/**
	 * Versteckt die Liste.
	 * 
	 * @param list [Object]
	 * Listen-Element, das mit createList erzeugt wurde.
	 * 
	 * @return void
	 */
	hideList: function(list) {
		list.hide();
	},
	
	/**
	 * Zeigt die Liste wieder an.
	 * 
	 * @param list [Object]
	 * Listen-Element, das mit createList erzeugt wurde.
	 * 
	 * @return void
	 */
	showList: function(list) {
		list.show();
	},
	
	/**
	 * Aktualisiert die Listen-Daten.
	 * 
	 * @param list [Object]
	 * Listen-Element, das mit createList erzeugt wurde.
	 * 
	 * @param columnDefinition [Array<Object>]
	 * Spalten-Definitionen:
	 * [{ label: <string>, align: <string>, sortable: <boolean>, id: <string> }, ...]
	 * 
	 * @param rows [Array<Object>]
	 * Daten-Definitionen:
	 * [
	 *   {
	 *     cells: [
	 *       { linkTarget: <string>, text: <string>, style: <string>, link: <string> },
	 *       ...
	 *     ]
	 *   },
	 *   ...
	 * ]
	 * 
	 * @param pageBrowser [Object]
	 * PageBrowser-Element, das mit createPageBrowser erzeugt wurde.
	 * 
	 * @param sorting [Object]
	 * Aktuelle Sortierung, bestehend aus 2 Werten. columnId (String) für die Spalten-ID und ascending (Boolean) für die Reihenfolge.
	 * 
	 * @param sortable [Boolean | Array<String>]
	 * Bestimmt, ob Sortierung allgemein (Boolean) oder für bestimmte Spalten (Array<String>) aktiviert werden soll.
	 * 
	 * @param sortFunction [Function]
	 * Funktion, die zum Sortieren aufgerufen wird, die Funktion muss 2 Parameter erwarten.
	 * Der erste setzt die Spalten-ID, der zweite ob das Ergebnis umgekehrt werden soll (=> false := Aufsteigend | true := Absteigend).
	 * 
	 * @return void
	 */
	updateListData: function(list, /*columnDefinition, */rows, pageBrowser, sorting, sortable, sortFunction) {
		list.childElements().each(function(item) { item.remove(); });
		/*
		var colId = columnDefinition[0].id;
		var colSortable = columnDefinition[0].sortable;
		*/
		/*
		 * Hier auskommentiert, weil es scheinbar Probleme gibt, dass Systeme signalisieren, sie könnten in
		 * der Listenansicht sortieren, aber keine Sortierung durchführen.
		 * 
		if (sortable === true && colSortable) {
			if (!pageBrowser.sort) {
				var sortAsc = Builder.node('a', { href: 'javascript:;' }, [ 'Aufsteigend' ]);
				Event.observe(sortAsc, 'click', function(event) {
					Event.stop(event);
					sortFunction(colId, false);
				});

				var sortNone = Builder.node('a', { href: 'javascript:;' }, [ 'Unsortiert' ]);
				Event.observe(sortNone, 'click', function(event) {
					Event.stop(event);
					sortFunction();
				});
				
				var sortDesc = Builder.node('a', { href: 'javascript:;' }, [ 'Absteigend' ]);
				Event.observe(sortDesc, 'click', function(event) {
					Event.stop(event);
					sortFunction(colId, true);
				});
				
				var span = Builder.node('span', { 'class': 'sort' }, [ ', ', sortAsc, ' | ', sortNone, ' | ', sortDesc ]);
				
				pageBrowser.sort = { sortAsc: sortAsc, sortDesc: sortDesc, container: span };
				pageBrowser.div.insert(span);
			}
			
			pageBrowser.sort.container.show();
		} else {
			if (pageBrowser.sort) {
				pageBrowser.sort.container.hide();
			}
		}
		*/
		var that = this;
		rows.each(function(row) {
			Element.extend(row);
			if (row.cells[0]) {
				var cell = row.cells[0];
				Element.extend(cell);
				var li = Builder.node('li');
				Element.extend(li);
				var obj;
				if (cell.link) {
					if(that.linkTarget != null) {
						cell.linkTarget = that.linkTarget;
					}
					if(that.urlOverwrite != null) {
						if (cell.link.startsWith(that.urlOverwrite.oldUrl)) {
							cell.link = cell.link.gsub(that.urlOverwrite.oldUrl, that.urlOverwrite.newUrl);
						}
					}
					obj = Builder.node(
							'a',
							{ href: cell.link, target: cell.linkTarget, style: cell.style });
				} else {
					obj = Builder.node(
							'span',
							{ style: cell.style });
				}
				Element.extend(obj);
				obj.update(cell.text);
				li.insert(obj);
				list.insert(li);
			}
		});
	},
	
	addRefreshFunction: function (container, refreshFunc){
		container.refreshFunc = refreshFunc;
	},
	
	/**
	 * Erzeugt einen PageBrowser
	 * 
	 * @param pageCount [integer]
	 * Anzahl Elemente pro Seite.
	 * 
	 * @param offset [integer]
	 * Offset-Index.
	 * 
	 * @param max [integer]
	 * Maximale-Trefferzahl.
	 * 
	 * @param prevAction [function]
	 * Event-Funktion, für die Aktion beim Klick auf den Previous-Button.
	 * 
	 * @param nextAction [function]
	 * Event-Funktion, für die Aktion beim Klick auf den Next-Button.
	 * 
	 * @return Object
	 */
	createPageBrowser: function(prevAction, nextAction) {
		
		var prev = Builder.node('a', { href: 'javascript:;', title: this.translate('prevPage') }, [ '<<' ]);
		Element.extend(prev);
		Event.observe(prev, 'click', prevAction);
		
		var next = Builder.node('a', { href: 'javascript:;', title: this.translate('nextPage') }, [ '>>' ]);
		Element.extend(next);
		Event.observe(next, 'click', nextAction);
		
		var from = Builder.node('span', { className: 'from' });
		Element.extend(from);
		var to = Builder.node('span', { className: 'to' });
		Element.extend(to);
		var of = Builder.node('span', { className: 'of' });
		Element.extend(of);
		
		var div = Builder.node('div', { className: 'page-browser' }, [
			prev,
			' ' + this.translate('show') +' ',
			from,
			' ' + this.translate('to') +' ',
			to,
			' ' + this.translate('from') +' ',
			of,
			' ' + this.translate('entries') +' ',
			next
		]);
		Element.extend(div);
		div.hide();
		
		return { prev: prev, next: next, from: from, to: to, of: of, div: div };
	},
	
	/**
	 * Aktualisiert einen PageBrowser.
	 * 
	 * @param pageBrowser [Object]
	 * PageBrowser-Element, das mit createPageBrowser erzeugt wurde.
	 * 
	 * @param pageCount [integer]
	 * Anzahl Elemente pro Seite.
	 * 
	 * @param offset [integer]
	 * Offset-Index.
	 * 
	 * @param max [integer]
	 * Maximale-Trefferzahl.
	 * 
	 * @return void
	 */
	updatePageBrowser: function(pageBrowser, pageCount, offset, max) {
		if (offset > 0)
			pageBrowser.prev.show();
		else
			pageBrowser.prev.hide();
		
		if (offset < max - pageCount)
			pageBrowser.next.show();
		else
			pageBrowser.next.hide();
		
		pageBrowser.from.update(offset + 1);
		var end = offset + pageCount;
		if (end > max)
			end = max;
		pageBrowser.to.update(end);
		pageBrowser.of.update(max);
	},
	
	/**
	 * Fügt dem PageBrowser einem Kategorie-Container hinzu.
	 * 
	 * @param container [Object]
	 * Kategorie-Element, das mit createCategory erzeugt wurde.
	 * 
	 * @param pageBrowser [Object]
	 * PageBrowser-Element, das mit createPageBrowser erzeugt wurde.
	 * 
	 * @return void
	 */
	addPageBrowser: function(container, pageBrowser) {
		container.container.insert(pageBrowser.div);
	},

	/**
	 * Versteckt den PageBrowser.
	 * 
	 * @param pageBrowser [Object]
	 * PageBrowser-Element, das mit createPageBrowser erzeugt wurde.
	 * 
	 * @return void
	 */
	hidePageBrowser: function(pageBrowser) {
		pageBrowser.div.hide();
	},

	/**
	 * Zeigt den PageBrowser wieder an.
	 * 
	 * @param pageBrowser [Object]
	 * PageBrowser-Element, das mit createPageBrowser erzeugt wurde.
	 * 
	 * @return void
	 */
	showPageBrowser: function(pageBrowser) {
		pageBrowser.div.show();
	},
	
	/**
	 * (Anforderung aus dem IONAS Context, wo es eine Ableitung dieser Klasse gibt, die
	 * diese Methode implementiert)
	 */
	handleData: function(resultCategories){
		alert("not implemented"); // ask WP / im IONAS Context 
	}
	
});