/* JavaScript für den Navigationsrumpf. */

//*** Hilfsfunktionen ***

// feststellen ob der Browser ein Internet Explorer ist
var isIE = (document.all ? true : false);

// aktueller Modus
//  0 = Inhalt, 1 = Index, 2 = Suche
var curMode = 0;
// gespeicherte Scroll-Positionen je Modus
var lastScrollPos = new Array();

// Setzt den aktuellen Modus und schaltet die entsprechenden html Elemente
function setMode(mode) {
    // Scroll-Position merken
    lastScrollPos[curMode] = getScrollPos();
    // alten Auswahl deaktivieren
    document.getElementById("data" + curMode).style.display = "none";
    
    // neue Auswahl setzen
    curMode = mode;
    document.getElementById("data" + curMode).style.display = "block";
    // Scroll-Position wieder herstellen
    setScrollPos(lastScrollPos[curMode]);
}

// ermittelt die aktuelle Scroll-Position
function getScrollPos() {
    var x;
    var y;
    if (isIE) {
        //x = document.body.scrollLeft;
        x = document.documentElement.scrollLeft;
        //y = document.body.scrollTop;
        y = document.documentElement.scrollTop;
    } else {
        x = window.pageXOffset;
        y = window.pageYOffset;
    }
    return new Array(x, y);
}

// setzt die aktuelle Scroll-Position
function setScrollPos(scrollPos) {
    var x = (scrollPos) ? scrollPos[0] : 0;
    var y = (scrollPos) ? scrollPos[1] : 0;
    window.scrollTo(x, y);
}


//*** Inhalt ***

var navContent;
var navContentInitialized = false;
var navConzentDeferredSelection;

// Aktualisiert die Auswahl im Inhaltsbaum
function setContentSelection(url) {
    if (navContentInitialized) {
        var node = navContent.nodeForURL(url, true);
        navContent.openTo((node != null) ? node.idx : -1, true);
    } else {
        navConzentDeferredSelection = url;
    }
}

function setContentInitialized() {
    this.navContentInitialized = true;
    if (navConzentDeferredSelection) {
        this.setContentSelection(navConzentDeferredSelection);
        navConzentDeferredSelection = null;
    }
}

/**
 * Erzeugt einen Knoten für den Baum.
 *
 * @param idx der Index im Knotenarray
 * @param pidx der Index des übergeordneten Knotens
 * @param name der Anzeigename
 * @param url das Verweissziel,
 *          wenn nicht gesetzt wird kein Verweiss erzeugt
 * @param title der Titel/Toolzip für den Verweiss,
 *          wenn nicht gesetzt wird kein Titel erzeugt
 * @param target das Zielfenster für den Verweiss,
 *          wenn nicht gesetzt wird das globale Zielfenster verwendet
 * @param icon das Symbol für den (geschlossenen) Knoten
 *          wenn nicht gesetzt wird das globale Symbol verwendet
 * @param iconOpen das Symbol für den geschlöffneten Knoten
 *          wenn nicht gesetzt wird das globale Symbol verwendet
 * @param open true wenn der Knoten initial geöffnet sein soll,
 *          wenn nicht gesetzt ist der Knoten initial geschlossen
 * @return das Knotenobjekt
 */
function dgTreeNode(
        idx, pidx, name, url, title, target, icon, iconOpen, open) {
    this.idx = idx;
    this.pidx = pidx;
    this.name = name;
    this.url = url;
    this.title = title;
    this.target = target;
    this.icon = icon;
    this.iconOpen = iconOpen;
    this._isOpen = open || false || (this.pidx == -1);
    this._hasChildren = false;
    this._lastSibling = false;
}

/**
 * Erzeugt eine Instanz der Klasse dgTree mit dem gegebenen Objektnamen.
 * Der Objektname muss dem Namen der globalen Variable entsprechen der
 * das Objekt zugeordnet wird.
 *
 * @param objName der Objektname
 * @return das Objekt
 */
function dgTree(objName) {
    this.config = {
            target          : null,
            useLines        : true,
            useIcons        : true,
            closeSiblings   : false
    }
    
    this.icon = {
            root            : 'images/treeBase.gif',
            folder          : 'images/treeFolder.gif',
            folderOpen      : 'images/treeFolderOpen.gif',
            node            : 'images/treePage.gif',
            empty           : 'images/treeEmpty.gif',
            line            : 'images/treeLine.gif',
            join            : 'images/treeJoin.gif',
            joinBottom      : 'images/treeJoinBottom.gif',
            plus            : 'images/treePlus.gif',
            plusBottom      : 'images/treePlusBottom.gif',
            minus           : 'images/treeMinus.gif',
            minusBottom     : 'images/treeMinusBottom.gif',
            nlPlus          : 'images/treeNoLinesPlus.gif',
            nlMinus         : 'images/treeNoLinesMinus.gif'
    };

    this.obj = objName;
    this.nodes = [];
    this.selectedIdx = -1;
}

/**
 * Setzt die Knoten des Baums. Die gegebenene Knoten müssen in der
 * Reihenfolge in gesetzt sein in der sie bei komplett expandiertem
 * Baum angezeigt werden. Der idx-Wert jedes Knotens muss dem Index
 * in dem Array entsprechen.
 *
 * @param pNodes das Array mit dgTreeNode Objekten
 */
dgTree.prototype.setData = function(pNodes) {
    this.nodes = pNodes;

    // zusätzliche Flags in den Knoten setzen
    for (var idx = 0; idx < this.nodes.length; ++idx) {
        var node = this.nodes[idx];
        
        // Verweissziel setzen
        if (!node.target) {
            node.target = this.config.target;
        }
        
        // im übergeordneten Knoten eintragen das es Kinder gibt
        if (node.pidx != -1) {
            this.nodes[node.pidx]._hasChildren = true;
        }
        
        // prüfen ob dies der letzte Knoten des übergeordneten Knotens ist
        var lastSib = true;
        for (var jdx = idx + 1; jdx < this.nodes.length; ++jdx) {
            var test = this.nodes[jdx];
            if (test.pidx == node.pidx) {
                // Gibt es einen später definierten Knoten mit dem selben
                // übergeordneten Knoten so kann der aktuelle Knoten nicht
                // der letzten Knoten der Ebene sein.
                lastSib = false;
                break;
            }
            if (test.pidx < node.pidx) {
                // Gibt es einen später definierten Knoten dessen
                // übergeordneter Knoten vor dem übergeordneten Knoten
                // des aktuellen Knotens definiert ist so liegt dieser
                // Knoten weiter in Richtung Wurzel und damit ist der
                // aktuelle Knoten der letzte der Ebene.
                break;
            }
        }
        node._lastSibling = lastSib;
    }
}

/**
 * Sucht den ersten Knoten mit der gegebenen URL und gibt den Index des
 * Knotens zurück. Wenn kein Knoten mit genau der URL gefunden wird kann
 * die Suche optional ohne Berücksichtigung von Ankern in URLs ausgeführt
 * werden.
 *
 * @param url die zu suchende URL
 * @param fallbackNoAnchor
 * @return der Knotenindex oder -1 für keinen passenden Knoten gefunden
 */
dgTree.prototype.nodeForURL = function(url, fallbackNoAnchor) {
    // URL für Suche ohne Anker vorbereiten
    var fbUrl = url;
    if (fallbackNoAnchor) {
        var idx = fbUrl.indexOf('#');
        if (idx != -1) {
            fbUrl = fbUrl.substring(0, idx);
        }
    }
    // Knoten suchen
    var res = null;
    var fbRes = null;
    for (var nodeIdx = 0; nodeIdx < this.nodes.length; ++nodeIdx) {
        var node = this.nodes[nodeIdx];
        // komplette URL testen
        var test = node.url;
        if (test == url) {
            res = node;
            break;
        }
        // URL ohne Anker testen wenn dafür noch kein Treffer vorliegt
        if ((fallbackNoAnchor)
                && (fbRes == null)) {
            var idx = test.indexOf('#');
            if (idx != -1) {
                test = test.substring(0, idx);
                if (test == url) {
                    fbRes = node;
                }
            }
        }
    }
    // das bessere Ergebnis zurück geben
    return (res != null) ? res : fbRes;
}

/**
 * Gibt die Zeichenkette mit den html Elementen zurück.
 *
 * @return die Html Zeichenkette
 */
dgTree.prototype.htmlString = function() {
    var str = '<div class="dgTree">\n';
    var nodeRes = this._htmlNodeString(0, '');
    str += nodeRes[0];
    str += '</div>';
    return str;
}

/**
 * Erzeugt die Zeichenkette mit den html Elementen für den gegebenen
 * Knoten und gibt neben der Zeichenkette den Index des nächsten
 * auszugebenden Knotens zurück.
 *
 * @param nextIdx der Index des nächsten auszugebenden Knotens
 * @param indent die Html Zeichenkette für die Ebenen-Einrückung
 * @return ein Array mit der erzeugten Html Zeichenkette (Index 0)
 *      und dem neuen Index des nächsten auszugebenden Knotens (Index 1).
 */
dgTree.prototype._htmlNodeString = function(nextIdx, indent) {
    // Knoten ausgeben
    var node = this.nodes[nextIdx];
    ++nextIdx;
    var str = '<div class="dgTreeNode">';

    // Einrückung
    if (node.pidx != -1) {
        str += indent;
        if (node._hasChildren) {
            // Handle ausgeben
            str += '<a href="javascript: ' + this.obj
                        + '.open(' + node.idx + ');"><img id="j'
                        + this.obj + node.idx + '" src="';
            if (this.config.useLines) {
                str += (node._isOpen)
                        ? (node._lastSibling
                                ? this.icon.minusBottom : this.icon.minus)
                        : (node._lastSibling
                                ? this.icon.plusBottom : this.icon.plus);
            } else {
                str += (node._isOpen) ? this.icon.nlMinus : this.icon.nlPlus;
            }
            str += '" /></a>';
        } else {
            str += '<img src="';
            if (this.config.useLines) {
                str += node._lastSibling
                        ? this.icon.joinBottom : this.icon.join;
            } else {
                str += this.icon.empty;
            }
            str += '" />';
        }
    }

    // Bild für Knoten
    if (this.config.useIcons) {
        if (!node.icon) {
            node.icon = (node.pidx == -1)
                    ? this.icon.root
                    : (node._hasChildren
                            ? this.icon.folder : this.icon.node);
        }
        if (!node.iconOpen) {
            node.iconOpen = (node.pidx == -1)
                    ? this.icon.root
                    : (node._hasChildren
                            ? this.icon.folderOpen : this.icon.node);
        }
        str += '<img id="i' + this.obj + node.idx + '" src="'
                + (node._isOpen ? node.iconOpen : node.icon) + '" />';
    }

    // Anzeigename (inkl. Verweiss)
    if (node.url) {
        str += '<a id="s' + this.obj + node.idx+ '" class="'
                + ((node.idx == this.selectedIdx) ? 'nodeSel' : 'node')
                + '" href="' + node.url + '"';
        if (node.title) {
            str += ' title="' + node.title + '"';
        }
        if (node.target) {
            str += ' target="' + node.target + '"';
        }
        str += ' onclick="' + this.obj + '.sel(' + node.idx + ');">';
        str += node.name;
        str += '</a>';
    } else if (node._hasChildren && (node.pidx != -1)) {
        str += '<a href="javascript:'
                + this.obj + '.open(' + node.idx + ');" class="node">';
        str += node.name;
        str += '</a>';
    } else {
        str += node.name;
    }

    str += '</div>';

    // untergeordnete Knoten ausgeben
    if (node._hasChildren) {
        str += '<div id="d' + this.obj + node.idx + '" style="display:'
                + ((node._isOpen) ? 'block' : 'none')
                + ';">';
        if (node.pidx != -1) {
            indent += '<img src="'
                    + ((this.config.useLines && !node._lastSibling)
                            ? this.icon.line : this.icon.empty)
                    + '" />';
        }
        while (true) {
            var lastSib = this.nodes[nextIdx]._lastSibling;
            var childRes = this._htmlNodeString(nextIdx, indent)
            str += childRes[0];
            nextIdx = childRes[1];
            if (lastSib) {
                break;
            }
        }
        str += '</div>';
    }
    
    return [ str, nextIdx ];
}

/**
 * Wählte den Knoten mit dem gegebenen Index aus.
 *
 * @param nodeIdx der Knotenindex
 */
dgTree.prototype.sel = function(nodeIdx) {
    if (nodeIdx != this.selectedIdx) {
        // bisherige Auswahl aufheben
        if (this.selectedIdx != -1) {
            var eOld = document.getElementById(
                    's' + this.obj + this.selectedIdx);
            eOld.className = 'node';
        }

        // neue Auswahl setzen
        this.selectedIdx = nodeIdx;
        if (this.selectedIdx != -1) {
            var eNew = document.getElementById(
                    's' + this.obj + this.selectedIdx);
            eNew.className = 'nodeSel';
            eNew.focus();
        }
    }
    
    // neue ausgewählten Knoten öffnen
    if (this.selectedIdx != -1) {
        var node = this.nodes[this.selectedIdx];
        if (node._hasChildren && !node._isOpen) {
            this.open(this.selectedIdx);
        }
    }
}

/**
 * Schaltet den IsOpen-Status des Knotens mit dem gegebenen Index um.
 *
 * @param nodeIdx der Knotenindex
 */
dgTree.prototype.open = function(nodeIdx) {
    // Knoten umschalten
    var node = this.nodes[nodeIdx];
    this._nodeOpen(node, !node._isOpen);
    
    // evtl. benachbarte Knoten schliessen
    if (this.config.closeSiblings) {
        this._closeSiblings(node);
    }
}

/**
 * Öffnet den Knoten mit dem gegebenen Index und alle übergeordneten Knoten.
 *
 * @param nodeIdx der Knotenindex
 * @param select true um den Knoten auszuwählen
        false um die aktuelle Auswahl nicht zu verändern
 */
dgTree.prototype.openTo = function(nodeIdx, select) {
    // die Knoten bis zur Wurzel öffnen
    var idx = nodeIdx;
    while (idx != -1) {
        var node = this.nodes[idx];
        if (!node._isOpen) {
            this.open(idx);
        }
        idx = node.pidx;
    }
    
    // optional den Knoten auswählen
    if (select) {
        this.sel(nodeIdx);
    }
}

/**
 * Öffnet alle Knoten.
 */
dgTree.prototype.openAll = function() {
    for (var idx = 0; idx < this.nodes.length; ++idx) {
        this._nodeOpen(this.nodes[idx], true);
    }
}

/**
 * Schliesst alle Knoten.
 */
dgTree.prototype.closeAll = function() {
    for (var idx = 0; idx < this.nodes.length; ++idx) {
        this._nodeOpen(this.nodes[idx], false);
    }
}

/**
 * Setzt den isOpen-Status des Knotens und schaltet die entsprechenden
 * html Elemente.
 *
 * @param node der Knoten
 * @param status der neue IsOpen-Status
 */
dgTree.prototype._nodeOpen = function(node, status) {
    if ((node.pidx == -1)
            || (!node._hasChildren)) {
        // Die Knoten der obersten Ebene und Knoten ohne Kinder können
        // nicht geöffnet oder geschlossen werden.
        return;
    }
    node._isOpen = status;
    
	if (this.config.useIcons) {
		eIcon = document.getElementById('i' + this.obj + node.idx);
		eIcon.src = (status) ? node.iconOpen : node.icon;
	}
    var bottom = node._lastSibling;

	var eJoin = document.getElementById('j' + this.obj + node.idx);
	eJoin.src = (this.config.useLines)
            ? ((status)
                    ? ((bottom) ? this.icon.minusBottom : this.icon.minus)
                    : ((bottom) ? this.icon.plusBottom : this.icon.plus))
            : ((status) ? this.icon.nlMinus : this.icon.nlPlus);

    var eDiv = document.getElementById('d' + this.obj + node.idx);
	eDiv.style.display = (status) ? 'block': 'none';
}

/**
 * Schliesst alle anderen Knoten mit dem selben übergeordneten Knoten
 * wie der gegebene Knoten.
 *
 * @param node der Knoten
 */
dgTree.prototype._closeSiblings = function(node) {
    // da auch alle untergeordeten Knoten zu den anderen Knoten geschlossen
    // werden sollen, kann alles in einer Schleife durchgeführt werden
    var pidx = node.pidx;
    for (var idx = pidx + 1; idx < this.nodes.length; ++idx) {
        var other = this.nodes[idx];
        if ((other.idx != node.idx)
                && (other._hasChildren)) {
            this._nodeOpen(other, false);
        }
        if ((other.pidx == pidx)
                && (other._lastSibling)) {
            // hiermit wird sichergestellt das auch der Baum unter dem
            // letzten Knoten der Ausgangsebene komplett geschlossen wird
            if (other._hasChildren) {
                pidx = other.pidx;
            } else {
                break;
            }
        }
    }
}


//*** Index ***

var navIndex;

// Führt die Indexsuche durch
function searchIndex(text) {
    if (text.length > 0) {
        var entry = navIndex.entryForName(text, true, true, true);
        if (entry != null) {
            navIndex.sel(entry.idx);
        }
    }
}

/**
 * Erzeugt einen Eintrag für den Index.
 *
 * @param level die 0-basierte Ebene des Eintrags
 * @param name der Anzeigename
 * @param urls ein Array mit den Zielangaben, jedes Element des Arrays
 *          ist wieder ein Array mit der URL und dem entsprechenden Titel.
 * @return das Eintragsobjekt
 */
function dgIndexEntry(level, name, urls) {
    this.idx; // wird von setData gesetzt
    this.level = level;
    this.name = name;
    this.urls = ((urls) ? urls : []);
}

/**
 * Erzeugt eine Instanz der Klasse dgIndex mit dem gegebenen Objektnamen.
 * Der Objektname muss dem Namen der globalen Variable entsprechen der
 * das Objekt zugeordnet wird.
 *
 * @param objName der Objektname
 * @return das Objekt
 */
function dgIndex(objName) {
    this.config = {
            target          : null,
            // Einrückung je Ebene in Pixel
            levelIndent     : 20
    }
    
    this.text = {
            selectTarget    : null,
            noTarget        : null
    }

    this.obj = objName;
    this.entries = [];
    this.selectedIdx = -1;
    this.visiblePopupId = null;
}

/**
 * Setzt die Einträge des Index. Die gegebenene Einträge müssen in der
 * Reihenfolge in gesetzt sein in der sie angezeigt werden. Der idx-Wert
 * jedes Eintrags wird automatisch auf den Index in dem Array gesetzt.
 *
 * @param pEntries das Array mit dgIndexEntry Objekten
 */
dgIndex.prototype.setData = function(pEntries) {
    this.entries = pEntries;

    for (var idx = 0; idx < this.entries.length; ++idx) {
        var entry = this.entries[idx];
        
        entry.idx = idx;
    }
}

/**
 * Sucht den ersten Eintrag mit dem gegebenen Anzeigenamen.
 *
 * @param name der zu suchende Anzeigename
 * @param ignoreCase true um die Gross-/Kleinschreibung zu ignorieren
 * @param startsWith true um die Suche als "startsWith" auszuführen,
 *          andernfalls wird die Suche als "equals" ausgeführt
 * @param topLevelOnly true um nur Einträge der Ebene 0 zu durchsichen
 */
dgIndex.prototype.entryForName = function(
        name, ignoreCase, startsWith, topLevelOnly) {
    var fName = name;
    if (ignoreCase) {
        fName = fName.toLowerCase();
    }
    var fNameLen = fName.length;
    var res = null;
    for (var entryIdx = 0; entryIdx < this.entries.length; ++entryIdx) {
        var entry = this.entries[entryIdx];
        if (topLevelOnly && entry.level > 0) {
            continue;
        }
        var nName = entry.name;
        if (ignoreCase) {
            nName = nName.toLowerCase();
        }
        var hit = false;
        if (startsWith) {
            hit = ((nName.length >= fNameLen)
                    && (nName.substring(0, fNameLen) == fName));
        } else {
            hit = (nName == fName);
        }
        if (hit) {
            res = entry;
            break;
        }
    }
    return res;
}

/**
 * Gibt die Zeichenkette mit den html Elementen zurück.
 *
 * @return die Html Zeichenkette
 */
dgIndex.prototype.htmlString = function() {
    var str = '<div class="dgIndex">\n';
    for (var idx = 0; idx < this.entries.length; ++idx) {
        str += this._htmlEntryString(this.entries[idx]);
    }
    str += '</div>';
    return str;
}

/**
 * Erzeugt die Zeichenkette mit den html Elementen für den gegebenen
 * Eintrag.
 *
 * @return die erzeugte Html Zeichenkette
 */
dgIndex.prototype._htmlEntryString = function(entry) {
    var str = '<div class="dgIndexEntry" style="margin-left:'
            + (entry.level * this.config.levelIndent) + 'px">';

    // Link für Eintrag
    str += '<a id="s' + this.obj + entry.idx + '"';
    if (entry.urls.length == 1) {
        // genau ein Ziel
        str += ' href="' + entry.urls[0][0] + '"';
        if (this.config.target) {
            str += ' target="' + this.config.target + '"';
        }
        str += ' title="' + entry.urls[0][1] + '"';
        str += ' onclick="' + this.obj + '.sel(' + entry.idx + ');"';
    } else {
        // kein Ziel oder mehr als ein Ziel
        str += ' href="javascript: ' + this.obj + '.popup(' + entry.idx + ');"';
    }
    str += ' class="entry">' + entry.name + '</a>';
    str += '</div>\n';

    // extra div für Popup zu dem Eintrag
    if (entry.urls.length != 1) {
        str += '<div id="p' + this.obj + entry.idx + '" class="popup">';
        if (entry.urls.length == 0) {
            str += this.text.noTarget;
        } else {
            str += this.text.selectTarget;
            for (var n = 0; n < entry.urls.length; ++n) {
                str += '<br />';
                str += '<a href="' + entry.urls[n][0] + '"';
                if (this.config.target) {
                    str += ' target="' + this.config.target + '"';
                }
                str += ' onclick="' + this.obj + '.closePopup();"';
                str += ' class="entry">' + entry.urls[n][1] + '</a>';
            }
        }
        str += '</div>';
    }

    return str;
}

/**
 * Schliesst das aktuell sichtbare Popup.
 */
dgIndex.prototype.closePopup = function() {
    if (this.visiblePopupId) {
        ePop = document.getElementById(this.visiblePopupId);
        ePop.style.display = 'none';
        this.visiblePopupId = null;
    }
}

/**
 * Wählte den Eintrag mit dem gegebenen Index aus.
 *
 * @param entryIdx der Eintragindex
 */
dgIndex.prototype.sel = function(entryIdx) {
    // aktuelles Popup ausblenden
    this.closePopup();

	// bisherige Auswahl aufheben
	if (this.selectedIdx != -1) {
		var eOld = document.getElementById(
				's' + this.obj + this.selectedIdx);
		eOld.className = 'entry';
	}
	
	// neue Auswahl setzen
	this.selectedIdx = entryIdx;
	if (this.selectedIdx != -1) {
		var eNew = document.getElementById(
				's' + this.obj + this.selectedIdx);
		eNew.className = 'entrySel';
		eNew.focus();
	}
}

/**
 * Wählte den Eintrag mit dem gegebenen Index aus und zeigt das zu dem
 * Eintrag gehörende Popup an.
 *
 * @param entryIdx der Eintragindex
 */
dgIndex.prototype.popup = function(entryIdx) {
    var oldPId = this.visiblePopupId;

    // Eintrag auswählen
    this.sel(entryIdx);

    // Popup anzeigen wenn es nicht zuvor sichtbar war
    var pId = 'p' + this.obj + entryIdx;
    if (pId != oldPId) {
        var ePop = document.getElementById(pId);
        if (ePop) {
            ePop.style.display = 'block';
            ePop.focus();
            this.visiblePopupId = pId;
        }
    }
}


//*** Suche ***

var navSearch;

// Führt die Volltextsuche durch
function searchText(text) {
    if (text.length > 0) {
        navSearch.find(text, false); // true = ODER-Suche, sonst UND-Suche
    }
}

/**
 * Erzeugt eine Instanz der Klasse dgSearch mit dem gegebenen Objektnamen.
 * Der Objektname muss dem Namen der globalen Variable entsprechen der
 * das Objekt zugeordnet wird.
 *
 * @param objName der Objektname
 * @return das Objekt
 */
function dgSearch(objName) {
    this.config = {
            target          : null
    }
    
    this.text = {
            hitCount        : null
    }
    
    this.obj = objName;
    this.pages = [];
    this.words = [];
    this.selectedPageIdx = -1;
}

/**
 * Setzt die Daten für die Volltextsuche.
 *
 * @param pPages ein Array mit allen Überschriften, jedes Element ist wieder
 *              ein Array mit dem Titel und dem Verweissziel der Überschrift
 * @param pWords ein Array mit allen Suchwörtern, jedes Element ist wieder
 *              ein Array mit dem Wort (ohne html Zeichenreferenzen) und
 *              einem Array mit den Seitenindexen.
 */
dgSearch.prototype.setData = function(pPages, pWords) {
    this.pages = pPages;
    this.words = pWords;
}

/**
 * Gibt die Zeichenkette mit den html Elementen zurück.
 *
 * @return die Html Zeichenkette
 */
dgSearch.prototype.htmlString = function() {
    var str = '<div class="dgSearch">\n';
    // div für Trefferanzahl
    str += '<div class="count">' + this.text.hitCount
            + '<span id="cnt' + this.obj + '">0</span></div>';
    // div für Überschriftenliste
    str += '<div id="res' + this.obj + '" class="result"></div>';
    str += '</div>';
    return str;
}

/**
 * Führt die Suche nach dem gegebenen Text aus und zeigt die gefundenen
 * Seiten an.
 *
 * @param text die Suchwörter durch Leerzeichen getrennt
 * @param orSearch true um Wortergebnisse mit ODER zu verknüpfen, bei allen
 *          anderen Werten werden Wortergebnisse mit UND verknüpft
 */
dgSearch.prototype.find = function(text, orSearch) {
    // bisheriges Ergebnis verwerfen
    this.clear();

    // reguläre Ausdrücke erzeugen
    var exps = this._exps(text);
    
    // Suche ausführen
    var res = this._exec(exps, orSearch);
    return res;
}

/**
 * Verwirft das aktuelle Suchergebnis.
 */
dgSearch.prototype.clear = function() {

}

/**
 * Erzeugt zu den Suchwörtern die regulären Ausdrücke für die Suche.
 *
 * @param text die Suchwörter durch Leerzeichen getrennt
 * @return die regulären Suchausdrücke
 */
dgSearch.prototype._exps = function(text) {
    var toks = text.split(' ');
    var exps = [];
    for (var idx = 0; idx < toks.length; ++idx) {
        var exp = toks[idx].toLowerCase();
        if (exp.length == 0) {
            continue;
        }
        
        // Sonderzeichen maskieren
        exp = exp.replace(/\./g, '\\.')
                 .replace(/\\/g, '\\\\');

        // Pattern (?, *) ersetzen
        exp = exp.replace(/\?/g, '.')
                 .replace(/\*/g, '.*?');

        exps[exps.length] = exp;
    }
    return exps;
}

/**
 * Führt die Suche mit den gegebenen regulären Ausdrücken aus.
 *
 * @param exps die regulären Suchausdrücke
 * @param orSearch true um Wortergebnisse mit ODER zu verknüpfen, bei allen
 *          anderen Werten werden Wortergebnisse mit UND verknüpft
 * @return ein Array mit Indexen der Trefferseiten
 */
dgSearch.prototype._exec = function(exps, orSearch) {
    var hits = null;

    // Suche je Wort durchführen
    for (var expIdx = 0; expIdx < exps.length; ++expIdx) {
        // Such-RegExp erzeugen
        var reg = new RegExp('^' + exps[expIdx] + '$', '');
        
        // Wörter prüfen
        var tokHits = new Array(this.pages.length);
        for (var idx = 0; idx < tokHits.length; ++idx) {
            tokHits[idx] = false;
        }
        for (var wordIdx = 0; wordIdx < this.words.length; ++wordIdx) {
            if (reg.test(this.words[wordIdx][0])) {
                // Treffer, Seiten merken
                var indexes = this.words[wordIdx][1];
                for (var idx = 0; idx < indexes.length; ++idx) {
                    tokHits[indexes[idx]] = true;
                }
            }
        }

        // Treffer verknüpfen
        if (hits == null) {
            // erste Treffer direkt übernehmen
            hits = tokHits;
        } else {
            // weitere Treffer verknüpfen
            for (var idx = 0; idx < hits.length; ++idx) {
                if (orSearch) {
                    hits[idx] = (hits[idx] || tokHits[idx]);
                } else {
                    hits[idx] = (hits[idx] && tokHits[idx]);
                }
            }
        }
    }
    
    // Ergebnis bilden
    var count = 0;
    if (hits != null) {
        for (var idx = 0; idx < hits.length; ++idx) {
            if (hits[idx] == true) {
                ++count;
            }
        }
    }
    var res = new Array(count);
    if (count > 0) {
        for (var idx = 0, jdx = 0; idx < hits.length; ++idx) {
            if (hits[idx] == true) {
                res[jdx] = idx;
                ++jdx;
            }
        }
    }
    return res;
}

/**
 * Zeigt das Suchergebnis an.
 *
 * @param hits ein Array mit Indexen der Trefferseiten
 * @param exps die regulären Suchausdrücke
 */
dgSearch.prototype._show = function(hits, exps) {
    // Seiten anzeigen
    var eCont = document.createElement('div');
    for (var idx = 0; idx < hits.length; ++idx) {
        var pageIdx = hits[idx];
        var page = this.pages[pageIdx];
    
        // div für Eintrag
        var eDiv = document.createElement('div');
        eDiv.setAttribute('class', 'dgSearchEntry');
        
        // Link für Eintrag
        var eLink = document.createElement("a");
        eLink.setAttribute('id', 's' + this.obj + pageIdx);
        var href = page[1];
        if (exps != null) {
            for (var jdx = 0; jdx < exps.length; ++jdx) {
                if (jdx == 0) {
                    href += '?';
                } else {
                    href += '&';
                }
                href += escape(exps[jdx]);
            }
        }
        eLink.setAttribute('href', href);
        if (this.config.target) {
            eLink.setAttribute('target', this.config.target);
        }
        eLink.setAttribute('onclick', this.obj + '.sel(' + pageIdx + ');');
        eLink.setAttribute('class', 'entry');
        eDiv.appendChild(eLink);

        // Text
        eLink.appendChild(document.createTextNode(page[0]));
        
        // Elemente anfügen
        eCont.appendChild(eDiv);
    }
    var eRes = document.getElementById('res' + this.obj);
    eRes.appendChild(eCont);
    
    // Anzahl anzeigen
    var eCnt = document.getElementById('cnt' + this.obj);
    eCnt.firstChild.nodeValue = hits.length;
}

/**
 * Wählt die Ergebnisseite mit dem gegebenen Index aus.
 *
 * @param pageIdx der Seitenindex
 */
dgSearch.prototype.sel = function(pageIdx) {
    if (this.selectedPageIdx != pageIdx) {
        // bisherige Auswahl aufheben
        if (this.selectedPageIdx != -1) {
            var eOld = document.getElementById(
                    's' + this.obj + this.selectedPageIdx);
            eOld.className = 'entry';
        }
        
        // neue Auswahl setzen
        this.selectedPageIdx = pageIdx;
        if (this.selectedPageIdx != -1) {
            var eNew = document.getElementById(
                    's' + this.obj + this.selectedPageIdx);
            eNew.className = 'entrySel';
            eNew.focus();
        }
    }
}
