/* URL do strony PHP, która poda podpowiedzi dla danego słowa kluczowego*/ var getFunctionsUrl = "suggest.php"; /* URL który pokaże wyniki wyświetlane dla wybranej podpowiedzi */ var phpHelpUrl="http://www.php.net/manual/en/function."; /* słowo kluczowe, dla którego wywołujemy żądanie HTTP*/ var httpRequestKeyword = ""; /* ostatnie słowo kluczowe, dla którego wysyłano żądanie*/ var userKeyword = ""; /* ilość podpowiedzi pojawiających się dla danego słowa kluczowego*/ var suggestions = 0; /* maksymalna liczba znaków wyświetlanych w podpowiedzi*/ var suggestionMaxLength = 30; /* flaga, która wskazuje, czy użytkownik skorzystał ze strzałek w górę lub w dół podczas ostatniego zdarzenia keyup*/ var isKeyUpDownPressed = false; /* ostatnia podpowiedź użyta do autouzupełniania słowa kluczowego*/ var autocompletedKeyword = ""; /* flaga, która określa, czy są wyniki dla obecnego słowa kluczowego*/ var hasResults = false; /* identyfikator używany do odwołania procesu oceny (metoda Timeout)*/ var timeoutId = -1; /* obecnie wybrana (myszą lub strzałkami) podpowiedź*/ var position = -1; /* obiekt pamięci podręcznej, zawierający podpowiedzi pobrane dla różnych słów kluczowych*/ var oCache = new Object(); /* minimalna i maksymalna pozycja widocznych podpowiedzi*/ var minVisiblePosition = 0; var maxVisiblePosition = 9; // wartośc true owoduje wyświetlenie szczegółowego komunikatu o błędzie var debugMode = true; /* obiekt XMLHttpRequest, stworzony do komunikowania się z serwerem*/ var xmlHttpGetSuggestions = createXmlHttpRequestObject(); /* funkcja init obsługuje zdarzenie onload*/ window.onload = init; //tworzy instancję obiektu XMLHttpRequest function createXmlHttpRequestObject() { // przechowa odowłanie do obiektu XMLHttpRequest var xmlHttp; // powinno zadziałać dla wszystkich przeglądarem z wyjątkiem IE6 i starszych try { // próbuje utworzyć obiekt XMLHttpRequest xmlHttp = new XMLHttpRequest(); } catch(e) { // zakładając, że IE6 lub starsza "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"); // próbuje wszystkie wartości progId, aż jedna zadziała for (var i=0; i<XmlHttpVersions.length && !xmlHttp; i++) { try { // próbuje stworzyć obiekt XMLHttpRequest xmlHttp = new ActiveXObject(XmlHttpVersions[i]); } catch (e) {} } } // zwraca utworzony obiekt lub wyświetla komunikat o błędzie if (!xmlHttp) alert("Błąd podczas tworzenia obiektu XMLHttpRequest."); else return xmlHttp; } /* funkcja inicjująca stronę*/ function init() { // pobiera element kontrolujący słowo kluczowe podane na wejściu var oKeyword = document.getElementById("keyword"); // zapobiega uruchomieniu domyślnego autouzupełniania przeglądarki oKeyword.setAttribute("autocomplete", "off"); // czyści zawartość słowa kluczowego i ustawia kursor w okienku tekstowym oKeyword.value = ""; oKeyword.focus(); // czas, po którym sprawdza się, czy nastąpiły zmiany w polu tekstowym setTimeout("checkForChanges()", 500); } /* funkcja, która dodaje słowo kluczowe do tablicy wartości */ function addToCache(keyword, values) { // twrzy nowy wpis w tablicy pamięci podręcznej // dodaje nowe wartości słów kluczowych do pamięci for(i=0; i<values.length; i++) oCache[keyword][i] = values[i]; } /* funkcja, która sprawdza, czy słowo kluczowe jest już w pamięci lub próbuje znaleźć najdłuższe przedrostki z pamięci do niego pasujące. Następnie dodaje je do pamięci, jako odpowiadające wpisanemu słowu kluczowemu */ function checkCache(keyword) { // sprawdza, czy słowo kluczowe jest już w pamięci if(oCache[keyword]) return true; // próbuje odnaleźć najdłuższy przedrostek for(i=keyword.length-2; i>=0; i--) { // uzupełnia bieżące słowo kluczowe var currentKeyword = keyword.substring(0, i+1); // sprawdza, czy bieżący przedrostek słowa kluczowego jest już w pamięci if(oCache[currentKeyword]) { // jeśli jest w pamięci var cacheResults = oCache[currentKeyword]; // wyniki odpowiadające bieżącemu słowu kluczowemu var keywordResultsSize = 0; // próbuje odnaleźć wszystkie funkcje pasujące do bieżącego przedrostka for(j=0;j<cacheResults.length;j++) { if(cacheResults[j].indexOf(keyword) == 0) keywordResults[keywordResultsSize++] = cacheResults[j]; } // dodaje wszystkie przedrostki słowa kluczowego do pamięci addToCache(keyword, keywordResults); return true; } } // nie znaleziono funkcji return false; } /* początkuje żądanie HTTP, w celu pobrania podpowiedzi dla wpisanego słowa kluczowego */ function getSuggestions(keyword) { /* kontynuuje, jeśli wpisano jakieś znaki i ostatnio wciśniętym klawiszem nie była jedna z pionowych strzałek*/ if(keyword != "" && !isKeyUpDownPressed) { // sprawdza, czy słowo jest w pamięci isInCache = checkCache(keyword); // jeśli jest... if(isInCache == true) { // pobiera wyniki z pamięci httpRequestKeyword=keyword; userKeyword=keyword; // wyświetla wyniki displayResults(keyword, oCache[keyword]); } // jeśli go nie ma w pamięci, wysyła noże żądanie HTTP else { if(xmlHttpGetSuggestions) { try { /* jeśli obiekt XMLHttpRequest nie jest zajęty obsługą poprzedniego żądnia... */ if (xmlHttpGetSuggestions.readyState == 4 || xmlHttpGetSuggestions.readyState == 0) { httpRequestKeyword = keyword; userKeyword = keyword; var httpRequestKeywords = document.getElementById('keyword').value; xmlHttpGetSuggestions.open("POST", getFunctionsUrl, true); xmlHttpGetSuggestions.onreadystatechange = handleGettingSuggestions; xmlHttpGetSuggestions.send("keywords="+encodeURIComponent(httpRequestKeywords)); } // jeśli jest zajęty... else { // pobiera słowo, które wpisał użytkownik userKeyword = keyword; // czyści poprzednio ustawione czasy if(timeoutId != -1) clearTimeout(timeoutId); // ponawia próbę po 0.5 sekundy timeoutId = setTimeout("getSuggestions(userKeyword);", 500); } } catch(e) { displayError("Nie mogę połączyć się z serwerem:\n" + e.toString()); } } } } } /* przekształca wszystkie obiekty potomne węzła XML na tablicę*/ function xmlToArray(resultsXml) { // początkuje zmienną resultsArray // przeszukuje w pętli wszystkie węzły XML, pobierając ich zawartość for(i=0;i<resultsXml.length;i++) resultsArray[i]=resultsXml.item(i).firstChild.data; // zwraca zawartość węzła w postaci tablicy return resultsArray; } /* obsługuje odpowiedź serwera, zawierającą podpowiedzi dla żądanego słowa kluczowego */ function handleGettingSuggestions() { //po ukończeniu procesu, decyduje co zrobić z otrzymanymi danymi if (xmlHttpGetSuggestions.readyState == 4) { // tylko jeśli status żądania HTTP jest "OK" if (xmlHttpGetSuggestions.status == 200) { try { // przetwarza odpowiedź serwera updateSuggestions(); } catch(e) { // wyświetla komunikat o błędzie displayError(e.toString()); } } else { displayError("Wystąpił problem podczas pobierania danych:\n" + xmlHttpGetSuggestions.statusText); } } }
/* funkcja przetwarzająca odpowiedź serwera */ function updateSuggestions() { // pobiera odpowiedź serwera var response = xmlHttpGetSuggestions.responseText; // błąd serwera? if (response.indexOf("ERRNO") >= 0 || response.indexOf("error:") >= 0 || response.length == 0) throw(response.length == 0 ? "Void server response." : response); // pobiera element dokumentu response = xmlHttpGetSuggestions.responseXML.documentElement; // zakłada nową tablicę z nazwami funkcji // sprawdza, czy są wyniki dla szukanego słowa kluczowego if(response.childNodes.length) { /* pobieramy nowe nazwy funkcji z pliku XML w postaci tablicy */ nameArray= xmlToArray(response.getElementsByTagName("name")); } // sprawdza, czy są już poszukiwane inne słowa kluczowe if(httpRequestKeyword == userKeyword) { // wyświetla tablicę wyników displayResults(httpRequestKeyword, nameArray); } else { // dodaje wyniki do pamięci // nie trzeba wyświetlać wyników, ponieważ są już niepotrzebne addToCache(httpRequestKeyword, nameArray); } } /* zapełnia listę podpowiedziami*/ function displayResults(keyword, results_array) { // rozpoczyna tworzenie tabeli HTML, w której umieścimy wyniki var div = "<table>"; // jeśli poszukiwane słowo kluczowe nie znajduje się w pamięci, dodaje je do niej if(!oCache[keyword] && keyword) addToCache(keyword, results_array); // jeśli tablica wyników jest pusta, wyświetla komunikat if(results_array.length == 0) { div += "<tr><td>Nie znaleziono dopasowania dla <strong>" + keyword + "</strong></td></tr>"; // ustawia flagę braku wyników i zeruje licznik hasResults = false; suggestions = 0; } // wyświetla wyniki else { // przestawia indeks wyróżnionej podpowiedzi position = -1; // przestawia flagę określającą, czy wciśnięto którąś ze strzałek isKeyUpDownPressed = false; /* ustawia flagę informującą o dopasowania funkcji do podanego słowa kluczowego */ hasResults = true; // pobiera z pamięci liczbę wyników suggestions = oCache[keyword].length; // przegląda w pętli wyniki i tworzy ich listę w HTML for (var i=0; i<oCache[keyword].length; i++) { // pobiera bieżącą nazwę funkcji crtFunction = oCache[keyword][i]; // ustawia łańcuch odnośnika do dopasowanej funkcji na jej nazwę crtFunctionLink = crtFunction; // zastępuje znak _ w łańcuchu odnośnika znakiem - while(crtFunctionLink.indexOf("_") !=-1) crtFunctionLink = crtFunctionLink.replace("_","-"); // zaczyna tworzyć rząd tabeli HTML, który zawiera odnośnik do oficjalnej strony z pomocą dotyczącą szukanej funkcji div += "<tr id='tr" + i + "' onclick='location.href=document.getElementById(\"a" + i + "\").href;' onmouseover='handleOnMouseOver(this);' " + "onmouseout='handleOnMouseOut(this);'>" + "<td align='left'><a id='a" + i + "' href='" + phpHelpUrl + crtFunctionLink + ".php"; // sprawdza czy długość nazwy wybranej funkcji przekracza dopuszczalną liczbę znaków, które mogą być wyświetlone if(crtFunction.length <= suggestionMaxLength) { // pogrubia dopasowany przedrostek funkcji i słowa kluczowego div += "'><b>" + crtFunction.substring(0, httpRequestKeyword.length) + "</b>" div += crtFunction.substring(httpRequestKeyword.length, crtFunction.length) + "</a></td></tr>"; } else { // sprawdza czy długość nazwy wybranego słowa kluczowego przekracza dopuszczalną liczbę znaków, które mogą być //wyświetlone if(httpRequestKeyword.length < suggestionMaxLength) { /* pogrubia dopasowany przedrostek funkcji i słowa kluczowego */ div += "'><b>" + crtFunction.substring(0, httpRequestKeyword.length) + "</b>" div += crtFunction.substring(httpRequestKeyword.length, suggestionMaxLength) + "</a></td></tr>"; } else { // pogrubia całą nazwę funkcji div += "'><b>" + crtFunction.substring(0,suggestionMaxLength) + "</b></td></tr>" } } } } // koniec tabeli HTML div += "</table>"; // pobiera obiekty oSuggest i oScroll var oSuggest = document.getElementById("suggest"); var oScroll = document.getElementById("scroll"); // przewija na górę listy oScroll.scrollTop = 0; // aktualizuje i wyświetla listę podpowiedzi oSuggest.innerHTML = div; oScroll.style.visibility = "visible"; // jeśli były jakieś wyniki uzupełniamy słowo kluczowe do wartości pierwszej funkcji if(results_array.length > 0) autocompleteKeyword(); } /* funkcja, która okresowo sprawdza, czy wprowadzono zmiany w oknie tekstowym */ function checkForChanges() { // pobiera obiekt słowa kluczowego var keyword = document.getElementById("keyword").value; // sprawdza, czy słowo kluczowe jest puste if(keyword == "") { // ukrywa podpowiedzi hideSuggestions(); // czyści słowo kluczowe userKeyword=""; httpRequestKeyword=""; } // ustawia licznik czasu dla nowego sprawdzenia setTimeout("checkForChanges()", 500); // sprawdza, czy wprowadzono zmiany if((userKeyword != keyword) && (autocompletedKeyword != keyword) && (!isKeyUpDownPressed)) // aktualizuje podpowiedź getSuggestions(keyword); }
/* funkcja obsługująca wciśnięcie klawisza*/ function handleKeyUp(e) { // pobiera zdarzenie e = (!e) ? window.event : e; // pobiera adresata zdarzenia target = (!e.target) ? e.srcElement : e.target; if (target.nodeType == 3) target = target.parentNode; // pobiera kod znaku naciśniętego klawisza code = (e.charCode) ? e.charCode : ((e.keyCode) ? e.keyCode : ((e.which) ? e.which : 0)); // sprawdza, czy zdarzenie było typu keyup if (e.type == "keyup") { isKeyUpDownPressed =false; // sprawdza, czy pojawił się któryś z ważnych dla aplikacji znaków if ((code < 13 && code != 8) || (code >=14 && code < 32) || (code >= 33 && code <= 46 && code != 38 && code != 40) || (code >= 112 && code <= 123)) { // ignoruje pozostałe znaki } else /* po wciśnięciu klawisza Enter, przechodzi do strony pomocy PHP dla danej funkcji */ if(code == 13) { // sprawdza, czy jakaś funkcja jest właśnie wybrana if(position>=0) { location.href = document.getElementById("a" + position).href; } } else // po naciśnięciu strzałki w dół przechodzi do kolejnej podpowiedzi if(code == 40) { newTR=document.getElementById("tr"+(++position)); oldTR=document.getElementById("tr"+(--position)); // usuwa stary wybór if(position>=0 && position<suggestions-1) oldTR.className = ""; // wybiera nową podpowiedź i aktualizuje słowo kluczowe if(position < suggestions - 1) { newTR.className = "highlightrow"; updateKeywordValue(newTR); position++; } e.cancelBubble = true; e.returnValue = false; isKeyUpDownPressed = true; // przewija podpowiedzi w dół, jeśli widoczne okno nie jest już aktualne if(position > maxVisiblePosition) { oScroll = document.getElementById("scroll"); oScroll.scrollTop += 18; maxVisiblePosition += 1; minVisiblePosition += 1; } } else // naciśnięcie strzałki w góę przenosi nas do poprzedniej podpowiedzi if(code == 38) { newTR=document.getElementById("tr"+(--position)); oldTR=document.getElementById("tr"+(++position)); // usuwa stare zaznaczenie if(position>=0 && position <= suggestions - 1) { oldTR.className = ""; } // zaznacza nową podpowiedź i aktualizuje słowo kluczowe if(position > 0) { newTR.className = "highlightrow"; updateKeywordValue(newTR); position--; // przewija okno w górę, jeśli dane w nim są już nieaktualne if(position<minVisiblePosition) { oScroll = document.getElementById("scroll"); oScroll.scrollTop -= 18; maxVisiblePosition -= 1; minVisiblePosition -= 1; } } else if(position == 0) position--; e.cancelBubble = true; e.returnValue = false; isKeyUpDownPressed = true; } } } /* funkcja uzupełniająca słowo kluczowe do warości wybranej podpowiedzi */ function updateKeywordValue(oTr) { // pobiera obiekt słowa kluczowego var oKeyword = document.getElementById("keyword"); // pobiera odnośnik do wybranej funkcji var crtLink = document.getElementById("a" + oTr.id.substring(2,oTr.id.length)).toString(); // zastępuje znaki - znakami _ i pomija rozszerzenie .php crtLink = crtLink.replace("-", "_"); crtLink = crtLink.substring(0, crtLink.length - 4); // aktualizuje słowo kluczowe oKeyword.value = unescape(crtLink.substring(phpHelpUrl.length, crtLink.length)); } /* funkcja usuwająca styl podpowiedzi */ function deselectAll() { for(i=0; i<suggestions; i++) { var oCrtTr = document.getElementById("tr" + i); oCrtTr.className = ""; } } /* funkcja obsługująca pojawienie się kursora myszy w obszarze podpowiedzi */ function handleOnMouseOver(oTr) { deselectAll(); oTr.className = "highlightrow"; position = oTr.id.substring(2, oTr.id.length); } /* funkcja obsługująca wyjście kursora myszy z obszaru podpowiedzi */ function handleOnMouseOut(oTr) { oTr.className = ""; position = -1; } /* funkcja sterująca łańcuchem */ function encode(uri) { if (encodeURIComponent) { return encodeURIComponent(uri); } if (escape) { return escape(uri); } } /* funkcja ukrywająca warstwę z popdpowiedziami */ function hideSuggestions() { var oScroll = document.getElementById("scroll"); oScroll.style.visibility = "hidden"; } /* funkcja, która wybiera obszar w obiekcie tekstowym, przekazywanym jako jej parametr */ function selectRange(oText, start, length) { // sprawdz rodzaj przeglądarki - IE czy FF if (oText.createTextRange) { //IE var oRange = oText.createTextRange(); oRange.moveStart("character", start); oRange.moveEnd("character", length - oText.value.length); oRange.select(); } else // FF if (oText.setSelectionRange) { oText.setSelectionRange(start, length); } oText.focus(); } /* funkcja uzupełniająca wpisane słowo kluczowe */ function autocompleteKeyword() { //pobiera obikt słowa kluczowego var oKeyword = document.getElementById("keyword"); // zeruje pozycję zaznaczonej podpowiedzi position=0; // usuwa zaznacznie ze wszystkich odpowiedzi deselectAll(); // podświetla wybraną podpowiedź document.getElementById("tr0").className="highlightrow"; // uzupełnia słowo kluczowe do podpowiedzi updateKeywordValue(document.getElementById("tr0")); // stosuje odpowiedni styl selectRange(oKeyword,httpRequestKeyword.length,oKeyword.value.length); // ustawia uzupełnione słowo jako wartość słowa kluczowego autocompletedKeyword=oKeyword.value; } /* funkcja wyświetlająca komunikat o błędzie */ function displayError(message) { // wyświetla komunikat błędu - bardziej szczegółowy jeśli debugMode ma wartość true alert("Błąd podczas dostępu do serwera! " + (debugMode ? "\n" + message : "")); }