/** * Registrierte DropDowns */ var dropdowns = new Array(); /** * Registrierte Abhängigkeiten */ var dropdownDependencies = new Array(); /** * Registriert ein DropDown Menü * * @param object element Textfeld * @param string source Funktion für Daten * @param string style Schreibstil ('U' = Grossschreibung, 'L' = Kleinschreibung, '' = Case Sensitive) * @param boolean match Muss der Wert in der Auswahl existieren? * @param int max Maximale Anzahl Datensätze * @param object hidden Hiddenfeld für Status bei S-Feldern * @param array events Zusätzliche Events bei Auswahl * @param boolean showCode Code in Klammern anzeigen * @return void */ function registerDropDown(element, source, style, match, max, hidden, events, showCode) { var index = dropdowns.length; dropdowns[index] = new Array(); dropdowns[index]["element"] = element; dropdowns[index]["source"] = source; dropdowns[index]["style"] = style; dropdowns[index]["match"] = match; dropdowns[index]["max"] = max; dropdowns[index]["hidden"] = hidden; dropdowns[index]["events"] = events; dropdowns[index]["isopen"] = false; dropdowns[index]["showcode"] = showCode; dropdowns[index]["active"] = 0; dropdowns[index]["selected"] = -1; dropdowns[index]["shownValues"] = 0; dropdowns[index]["moreAvailable"] = false; dropdowns[index]["edited"] = false; dropdowns[index]["valueBefore"] = ""; dropdowns[index]["scrollTo"] = -1; } /** * Registriert eine Abhängigkeit zweier DropDowns * * @param string parent ID des übergeordneten Textfelds * @param string child ID desc untergeordneten Textfelds * @param string source Funktion für Daten * @return void */ function registerDropDownDependency(parent, child, source) { var index = dropdownDependencies.length; dropdownDependencies[index] = new Array(); dropdownDependencies[index]["parent"] = parent; dropdownDependencies[index]["child"] = child; dropdownDependencies[index]["source"] = source; } /** * Reagiert auf Mausklicks und öffnet und schliesst registrierte Vorschlagsfenster * * @param event e Ereignis * @return void */ function handleDropDown(e) { if(!e) e = window.event; var src = (e.srcElement ? e.srcElement : e.target); for(var i = 0; i < dropdowns.length; i++) { var div = document.getElementById(dropdowns[i]["element"].id + "_div"); if(src == dropdowns[i]["element"]) makeSuggestion(dropdowns[i]["element"], dropdowns[i]["source"], dropdowns[i]["element"].getAttribute("readonly")); else if(src != div && src.parentNode != div && src != document.getElementById(dropdowns[i]["element"].id + "_btn")) hideSuggestion(dropdowns[i]["element"]); } } /** * Zeigt ein Vorschlagsfenster zu einem Textfeld an * * @param object element Textfeld * @param string source Funktion für Daten * @param boolean showAll Alle Daten anzeigen * @return void */ function makeSuggestion(element, source, showAll) { // Status offen setzen var max = 0; var hidden = null; var additionalEvents = null; var selectedIndex = -1; var moreAvailable = false; var showCode = false; var scrollTo = -1; for(var i = 0; i < dropdowns.length; i++) { if(dropdowns[i]["element"] == element) { additionalEvents = dropdowns[i]["events"]; dropdowns[i]["isopen"] = true; dropdowns[i]["active"] = 0; max = dropdowns[i]["max"]; hidden = dropdowns[i]["hidden"]; moreAvailable = dropdowns[i]["moreAvailable"]; selectedIndex = dropdowns[i]["selected"]; showCode = dropdowns[i]["showcode"]; scrollTo = dropdowns[i]["scrollTo"]; } } if(moreAvailable && selectedIndex >= max) { showAll = true; selectedIndex--; } // Alle angezeigten Vorschläge entfernen var suggestion_div = document.getElementById(element.id + "_div"); while(suggestion_div.childNodes.length > 0) suggestion_div.removeChild(suggestion_div.childNodes[0]); // Tabelle erstellen var suggestion_tbl = document.createElement("table"); suggestion_tbl.cellpadding = "0"; suggestion_tbl.cellspacing = "0"; suggestion_tbl.width = "100%"; suggestion_div.appendChild(suggestion_tbl); // Table-Body erstellen var suggestion_tbody = document.createElement("tbody"); suggestion_tbl.appendChild(suggestion_tbody); // Position ermitteln und Fenster anzeigen var x = -1; var y = element.offsetHeight + 2; var parent = element; do { x += parent.offsetLeft; y += parent.offsetTop; } while(parent = parent.offsetParent); suggestion_div.style.top = y + "px"; suggestion_div.style.left = x + "px"; suggestion_div.style.height = "0px"; suggestion_div.style.display = "inline"; suggestion_div.style.width = "0px"; // Suchkriterien ermitteln var keywords = element.value.split(" "); // Prüfen, ob das DropDown von einem anderen DropDown abhängig ist var parentData = null; var parentValue = null; for(var i = 0; i < dropdownDependencies.length; i++) { if(dropdownDependencies[i]["child"] == element.id) { eval("parentData = " + dropdownDependencies[i]["source"] + "();"); parentValue = document.getElementById(dropdownDependencies[i]["parent"]).value; break; } } // Werte durchlaufen var results = 0; eval("var data = " + source + "();"); for(var key in data) { // Anstelle eines elften gefundenen Datensatzes eine Meldung anzeigen, dass nur 10 angezeigt werden und abbrechen if(!showAll && max > 0 && results == max) { moreAvailable = true; var a = document.createElement("a"); a.href = "javascript:makeSuggestion(document.getElementById('" + element.id + "'), '" + source + "', true);"; a.appendChild(document.createTextNode("Alle Vorschläge anzeigen")); var div = document.createElement("div"); div.className = "AbfSuggestionItem"; div.appendChild(a); var td = document.createElement("td"); td.className = "AbfSuggestion"; td.appendChild(div); var tr = document.createElement("tr"); tr.appendChild(td); suggestion_tbody.appendChild(tr); break; } if(showAll) { combinations = 1; found = 1; } else { // Kombinationen und Funde zählen var combinations = 0; var found = 0; for(var i = 0; i < keywords.length; i++) { combinations++; if(key.substr(0, keywords[i].length).toLowerCase() == keywords[i].toLowerCase()) { found++; } else { for(var k = 0; k < data[key].length; k++) { if(data[key][k].substr(0, keywords[i].length).toLowerCase() == keywords[i].toLowerCase()) { found++; break; } } } } } // Es müssen mehrere Kombinationen vorhanden sein und alle übereinstimmen if(combinations > 0 && combinations == found && (parentData == null || parentValue != null && parentData[parentValue] && parentData[parentValue][key] == true)) { results++; // Sonderzeichen im Key ersetzen var escapedKey = key; escapedKey = escapedKey.replace(/\'/g, "\\'"); escapedKey = escapedKey.replace(/\"/g, "\\\""); escapedKey = escapedKey.replace(/\n/g, "\\n"); // Zelle erstellen var div = document.createElement("div"); div.id = element.id + "_ROW_" + key; div.className = "AbfSuggestionItem"; if(element.getAttribute("readonly")) { // Sonderzeichen ersetzen var escaped = data[key].join(" "); escaped = escaped.replace(/\'/g, "\\'"); escaped = escaped.replace(/\"/g, "\\\""); escaped = escaped.replace(/\n/g, "\\n"); var func = "useSuggestion(document.getElementById('" + element.id + "'), document.getElementById('" + hidden.id + "'), '" + escapedKey + "', '" + escaped + "', true, true);"; if(additionalEvents != null) { for(var i = 0; i < additionalEvents.length; i++) func += additionalEvents[i] + ";"; } // Select div.appendChild(document.createTextNode(data[key].join(" "))); if(div.addEventListener) div.addEventListener("click", new Function("evt", func), false); else if(div.attachEvent) div.attachEvent("onclick", new Function("evt", func)); } else { // Edit + Select var text = data[key].join(" "); if(showCode) text += " (" + key + ")" div.appendChild(document.createTextNode(text)); if(div.addEventListener) div.addEventListener("click", new Function("evt", "useSuggestion(document.getElementById('" + element.id + "'), null, '" + escapedKey + "', null, true, true)"), false); else if(div.attachEvent) div.attachEvent("onclick", new Function("evt", "useSuggestion(document.getElementById('" + element.id + "'), null, '" + escapedKey + "', null, true, true)")); } // Eintrag zum Vorschlagsfenster hinzufügen var td = document.createElement("td"); td.appendChild(div); var tr = document.createElement("tr"); if(results - 1 == selectedIndex) tr.className = "AbfSuggestionSelected"; tr.appendChild(td); suggestion_tbody.appendChild(tr); } } // Anzahl aktuell angezeigter Werte speichern for(var i = 0; i < dropdowns.length; i++) { if(dropdowns[i]["element"] == element) { dropdowns[i]["shownValues"] = results; dropdowns[i]["moreAvailable"] = moreAvailable; break; } } // Höhe berechnen y -= document.body.scrollTop; var browserHeight = (window.innerHeight ? window.innerHeight : document.body.clientHeight); var height = parseInt(suggestion_div.style.height) + suggestion_tbl.offsetHeight; if(height + y > browserHeight) { height = browserHeight - y; } else { height += 2; } suggestion_div.style.height = height + "px"; // Breite korrigieren var browserWidth = (window.innerWidth ? window.innerWidth : document.body.clientWidth); var width = parseInt(suggestion_div.style.width) + suggestion_tbl.offsetWidth; if(width + x > browserWidth) { width = browserWidth - x - 10; } suggestion_div.style.width = width + "px"; // Vorschlagsfenster schliessen, wenn keine Vorschläge vorhanden sind if(suggestion_tbody.childNodes.length == 0) suggestion_div.style.display = "none"; else if(scrollTo >= 0) suggestion_div.scrollTop = scrollTo; } /** * Öffnet das Vorschaufenster, wenn es geschlossen ist oder schliesst es, wenn es geöffnet ist * * @param object element Textfeld * @param string source Funktion für Daten * @return void */ function toggleSuggestion(element, source) { // Status offen setzen for(var i = 0; i < dropdowns.length; i++) { if(dropdowns[i]["element"] == element) { // Dropdown öffnen oder schliessen if(!dropdowns[i]["isopen"]) makeSuggestion(element, source, true); else hideSuggestion(element); break; } } } /** * Übernimmt einen Vorschlag in ein Textfeld * * @param object element Textfeld * @param object hidden Hiddenfeld * @param string value Wert * @param string title Bezeichnung * @param boolean fireEvent onChange Event abfeuern? * @param boolean closeSuggestion Vorschlagsfenster schliessen? * @return void */ function useSuggestion(element, hidden, value, title, fireEvent, closeSuggestion) { // Event-Objekt ermitteln var eventObject = (hidden ? hidden : element); // Wert übernehmen if(hidden == null) { // ES Feld element.value = value; hideSuggestion(element); matchSuggestion(element); } else { // Titel ermitteln if(value.length > 0 && (!title || title.length == 0)) { for(var i = 0; i < dropdowns.length; i++) { if(dropdowns[i]["element"] == element) { var data = new Array(); eval("data = " + dropdowns[i]["source"] + "();"); if(data[value]) title = data[value].join(" "); break; } } } // S Feld hidden.value = value; element.value = title; hideSuggestion(element); } // onChange Event auswerfen, damit bestehende JavaScripts weiterhin funktionieren if(fireEvent) { if(document.createEventObject) { // Change Event für IE auswerfen var evt = document.createEventObject(); eventObject.fireEvent("onchange", evt); } else { // Change Event für Browser auswerfen var evt = document.createEvent("HTMLEvents"); evt.initEvent("change", true, true); eventObject.dispatchEvent(evt); } } // Untergeordnete Objekte aktualisieren for(var i = 0; i < dropdownDependencies.length; i++) { if(dropdownDependencies[i]["parent"] == element.id || hidden != null && dropdownDependencies[i]["parent"] == hidden.id) { // Kind ermitteln var child = document.getElementById(dropdownDependencies[i]["child"]); var childHidden = null; var childValue = ""; var childTitle = ""; if(document.getElementById(child.id.substr(0, child.id.length - 6))) { // Hidden-Feld vorhanden childHidden = document.getElementById(child.id.substr(0, child.id.length - 6)); } // Daten ermitteln var data = new Array(); for(var k = 0; k < dropdowns.length; k++) { if(dropdowns[k]["element"] == child) { eval("data = " + dropdowns[k]["source"] + "();"); if(data[childValue] != null) childTitle = data[childValue].join(" "); break; } } // Aktualisierung durchführen useSuggestion(child, childHidden, childValue, childTitle, fireEvent, closeSuggestion); } } } /** * Versteckt ein Vorschlagsfenster * * @param object element Vorschlagsfenster * @return void */ function hideSuggestion(element) { // Status offen setzen setSuggestionSelection(element, -1); for(var i = 0; i < dropdowns.length; i++) { if(dropdowns[i]["element"] == element) dropdowns[i]["isopen"] = false; } // Vorschläge verstecken var suggestion_div = document.getElementById(element.id + "_div"); if(suggestion_div) suggestion_div.style.display = "none"; } /** * Aktualisiert abhängige DropDowns * * @param object element Übergeordnetes Textfeld * @return void */ function updateSuggestion(element) { for(var i = 0; i < dropdownDependencies.length; i++) { if(dropdownDependencies[i]["parent"] == element.id) { // Abhängigkeiten ermitteln eval("var data = " + dropdownDependencies[i]["source"] + "();"); if(!data || !data[element.value] || !data[element.value][document.getElementById(dropdownDependencies[i]["child"]).value]) { // Abhängiges Feld zurücksetzen, wenn der aktuelle Wert nicht gültig ist document.getElementById(dropdownDependencies[i]["child"]).value = ""; document.getElementById(dropdownDependencies[i]["child"] + "_span").innerText = ""; } } } } /** * Löscht den Eingabewert, wenn er nicht in der Auswahl vorgesehen ist und nicht frei definiert werden darf * * @param object element Textfeld * @return void */ function matchSuggestion(element) { // Sicherstellen, dass der Wert existiert, wenn er dies muss for(var i = 0; i < dropdowns.length; i++) { if(dropdowns[i]["element"] == element && dropdowns[i]["hidden"] == null) { // Prüfen, ob das DropDown abhängig von einem anderen DropDown ist for(var k = 0; k < dropdownDependencies.length; k++) { if(dropdownDependencies[k]["child"] == element.id) { // DropDown ist abhängig. Zurücksetzen, wenn oben nichts ausgewählt ist eval("parentData = " + dropdownDependencies[k]["source"] + "();"); var parentValue = document.getElementById(dropdownDependencies[k]["parent"]).value; if(parentValue == null || parentValue.length == 0) element.value = ""; } } // Style setzen styleSuggestion(element); if(element.value.length == 0) { // Bezeichnung aus dem Span entfernen document.getElementById(element.id + "_span").innerText = ""; updateSuggestion(element); } else { // Sicherstellen, dass der Wert in der Auswahl vorhanden ist, wenn er nicht frei vergeben werden darf eval("var data = " + dropdowns[i]["source"] + "();"); for(var key in data) { // Wert gefunden if((dropdowns[i]["style"] == "U" || dropdowns[i]["style"] == "L") && key.toUpperCase() == element.value.toUpperCase() || key == element.value) { // Abhängige DropDowns aktualisieren document.getElementById(element.id + "_span").innerText = data[element.value].join(" "); updateSuggestion(element); return; } // Wert entfernen, Child-Wert ist im abhängigen DropDown nicht vorhanden for(var k = 0; k < dropdownDependencies.length; k++) { if(dropdownDependencies[k]["child"] == element.id && (!parentData || !parentData[document.getElementById(dropdownDependencies[k]["parent"]).value] || parentData[document.getElementById(dropdownDependencies[k]["parent"]).value][element.value] == null)) { // Wert entfernen element.value = ""; } } } // Wert nicht gefunden, entfernen und abhängige DropDowns aktualisieren updateSuggestion(element); document.getElementById(element.id + "_span").innerText = ""; if(dropdowns[i]["match"]) element.value = ""; } } } } /** * Setzt die Gross-/Kleinschreibung des Feldes * * @param object element Textfeld * @return void */ function styleSuggestion(element) { for(var i = 0; i < dropdowns.length; i++) { if(dropdowns[i]["element"] == element) { // Gross-/Kleinschreibung berücksichtigen if(dropdowns[i]["style"] == "U") element.value = element.value.toUpperCase(); else if(dropdowns[i]["style"] == "L") element.value = element.value.toLowerCase(); } } } /** * Selektiert einen Vorschlag * * @param event e Tastaturereignis * @param object element Element * @return void */ function moveSuggestion(e, element) { // Taste ermitteln e = e || window.event; var key = e.keyCode || e.which; for(var i = 0; i < dropdowns.length; i++) { if(dropdowns[i]["element"] == element) { // Vorschlagsfenster ermitteln var suggestion_div = document.getElementById(element.id + "_div"); // Aktion entsprechend der gedrückten Taste ausführen if(key == 9) { // Tabulator if(dropdowns[i]["edited"]) { if(element.getAttribute("readonly")) { // Select var value = getElementAt(element, getSuggestionSelection(element)); eval("var data = " + dropdowns[i]["source"] + "();"); var title = data[value].join(" "); useSuggestion(element, dropdowns[i]["hidden"], value, title, true, true); } else { // Edit + Select useSuggestion(element, null, getElementAt(element, getSuggestionSelection(element)), null, true, true); } } hideSuggestion(element); } else if(key == 27) { // Escape useSuggestion(element, null, dropdowns[i]["valueBefore"], null, true, true); dropdowns[i]["edited"] = false; hideSuggestion(element); } else if(key == 38) { // Nach oben Taste var pos = getSuggestionSelection(element) - 1; setSuggestionSelection(element, pos); dropdowns[i]["edited"] = true; var scrollTo = pos * 22; if(scrollTo < suggestion_div.scrollTop) dropdowns[i]["scrollTo"] = scrollTo; } else if(key == 40) { // Nach unten Taste var pos = getSuggestionSelection(element) + 1; setSuggestionSelection(element, pos); dropdowns[i]["edited"] = true; var scrollTo = (pos + 1) * 22 + 3; if(scrollTo - suggestion_div.scrollTop >= parseInt(suggestion_div.style.height)) dropdowns[i]["scrollTo"] = scrollTo - parseInt(suggestion_div.style.height); } else if(element.getAttribute("readonly") && (key >= 48 && key <= 57 || key >= 65 && key <= 90 || key >= 97 && key <= 122)) { // Alphanumerisches Zeichen var char = String.fromCharCode(key); var startPos = getSuggestionSelection(element); var pos = getElementBeginningWith(dropdowns[i]["source"], char, startPos); if(startPos >= 0 && pos == -1) pos = getElementBeginningWith(dropdowns[i]["source"], char, 0); if(pos >= 0) { setSuggestionSelection(element, pos); dropdowns[i]["edited"] = true; // Nach oben scrollen? var scrollTo = pos * 22; if(scrollTo < suggestion_div.scrollTop) dropdowns[i]["scrollTo"] = scrollTo; // Nach unten scrollen? var scrollTo = (pos + 1) * 22 + 3; if(scrollTo - suggestion_div.scrollTop >= parseInt(suggestion_div.style.height)) dropdowns[i]["scrollTo"] = scrollTo - parseInt(suggestion_div.style.height); } } else { // Zurücksetzen setSuggestionSelection(element, -1); } break; } } } /** * Setzt die Eigenschaften des DropDowns zurück * * @param object element Element * @return void */ function resetDropDown(element) { for(var i = 0; i < dropdowns.length; i++) { if(dropdowns[i]["element"] == element) { dropdowns[i]["edited"] = false; dropdowns[i]["valueBefore"] = element.value; } } } /** * Liefert das Element an der Position pos zurück * * @param object element Element * @param int pos Position * @return void */ function getElementAt(element, pos) { // Suchkriterien ermitteln var keywords = element.value.split(" "); var escapedKey = ""; for(var i = 0; i < dropdowns.length; i++) { if(dropdowns[i]["element"] == element) { // Prüfen, ob das DropDown von einem anderen DropDown abhängig ist var parentData = null; var parentValue = null; for(var k = 0; k < dropdownDependencies.length; k++) { if(dropdownDependencies[k]["child"] == element.id) { eval("parentData = " + dropdownDependencies[k]["source"] + "();"); parentValue = document.getElementById(dropdownDependencies[k]["parent"]).value; break; } } // Daten ermitteln eval("var data = " + dropdowns[i]["source"] + "();"); var index = -1; for(var key in data) { if(element.getAttribute("readonly")) { if(parentData == null || parentValue != null && parentData[parentValue] && parentData[parentValue][key] == true) index++; } else { // Kombinationen und Funde zählen var combinations = 0; var found = 0; for(var k = 0; k < keywords.length; k++) { combinations++; if(key.substr(0, keywords[k].length).toLowerCase() == keywords[k].toLowerCase()) { found++; } else { for(var m = 0; m < data[key].length; m++) { if(data[key][m].substr(0, keywords[k].length).toLowerCase() == keywords[k].toLowerCase()) { found++; break; } } } } // Es müssen mehrere Kombinationen vorhanden sein und alle übereinstimmen if(combinations > 0 && combinations == found && (parentData == null || parentValue != null && parentData[parentValue] && parentData[parentValue][key] == true)) index++; } if(index == pos) { // Sonderzeichen im Key ersetzen escapedKey = key; escapedKey = escapedKey.replace(/\'/g, "\\'"); escapedKey = escapedKey.replace(/\"/g, "\\\""); escapedKey = escapedKey.replace(/\n/g, "\\n"); break; } } break; } } return escapedKey; } /** * Selektiert einen Vorschlag * * @param object element Element * @param int pos Position * @return void */ function setSuggestionSelection(element, pos) { for(var i = 0; i < dropdowns.length; i++) { if(dropdowns[i]["element"] == element) { if(pos < 0) { pos = -1; dropdowns[i]["moreAvailable"] = false; } else if(dropdowns[i]["moreAvailable"] && pos == dropdowns[i]["shownValues"]) { makeSuggestion(element, dropdowns[i]["source"], true); } else if(pos >= dropdowns[i]["shownValues"]) { pos = dropdowns[i]["shownValues"] - 1; } dropdowns[i]["selected"] = pos; break; } } } /** * Liefert den selektierten Vorschlag zurück * * @param object element Element * @return int Position */ function getSuggestionSelection(element) { var pos = -1; for(var i = 0; i < dropdowns.length; i++) { if(dropdowns[i]["element"] == element) { pos = dropdowns[i]["selected"]; break; } } return pos; } /** * Liefert die Position des nächsten Elementes beginnend mit einem bestimmten Zeichen zurück * * @param string source Datenquelle * @param string char Zeichen * @param int pos Ab Position * @return int Position */ function getElementBeginningWith(source, char, pos) { eval("var data = " + source + "();"); var index = 0; char = char.toUpperCase(); for(var key in data) { if(data[key][0].charAt(0).toUpperCase() == char && index > pos) return index; index++; } return -1; } /** * Mausklicks überwachen */ document.onclick = handleDropDown;