Przydatną funkcją na każdej stronie jest jakaś forma wyszukiwarki. Jej rola jest nie do przecenienia. Dzięki wyszukiwarce użytkownicy, którzy nie znają dobrze serwisu, mogą w mig odnaleźć interesujące ich informacje. Przedzieranie się przez spis treści nie każdemu odpowiada, a poza tym nie wszystko można tam odnaleźć.
Specjalną formą wyszukiwarki jest indeks lub inaczej skorowidz. Można go znaleźć w niemal każdym podręczniku. "Papierowa" wersja skorowidza jednak nie do końca jest wygodna, ponieważ nadal zmusza do wertowania stron i szukania haseł według kolejności liter w alfabecie. Na szczęście komputer może to zrobić za nas. Wystarczy, że użytkownik zacznie wpisywać żądane hasło, a w miarę jak będzie to robił, odszukiwane będą pozycje coraz bardziej pasujące do zapytania. Dodatkowo skoro będziemy już mieli gotowy indeks, nic nie stoi na przeszkodzie, aby zbudować z niego normalną wyszukiwarkę. Dzięki temu będzie można odszukać hasła, które nie tylko rozpoczynają się od wpisywanych liter, ale również mogą figurować wewnątrz haseł zapisanych w skorowidzu. Taka funkcjonalność znacznie zwiększa możliwości.
Warto dodać, że taki indeks wcale nie musi być alternatywą dla tradycyjnych wyszukiwarek. Może bardzo dobrze je uzupełniać, ponieważ oferuje inną funkcjonalność, dzięki której wyniki zwykle będą bardziej trafne, ponieważ hasła do indeksu dodaje człowiek, a nie komputer.
Wymagana wiedza:
Aby wstawić na swoją stronę indeks z dodatkową funkcją wyszukiwarki, postępują według poniższych instrukcji:
Utwórz w swoim edytorze (X)HTML nowy dokument. Wklej do niego poniższy kod i zapisz w pliku indeks.js (koniecznie użyj kodowania iso-8859-2):
/** * @author Sławomir Kokłowski {@link http://www.kurshtml.boo.pl} * @copyright NIE usuwaj tego komentarza! (Do NOT remove this comment!) */ function Indeks(id, ramka) { this.id = id; if (typeof ramka == 'undefined') this.ramka = self; else if (typeof ramka == 'string') { if (ramka == '_blank') this.ramka = ''; else if (ramka == '_self') this.ramka = self; else if (ramka == '_parent') this.ramka = parent; else if (ramka == '_top') this.ramka = top; else this.ramka = ramka; } else if (!ramka) this.ramka = self; else this.ramka = ramka; this._szukaj = { html: '', haslo: '', i: 0 }; } Indeks.prototype.wstaw = function(hasla, adres_bazowy, rozmiar, sortuj) { if (typeof sortuj != 'undefined' && sortuj) { hasla.sort( function(a, b) { if (a[0] == b[0]) return 0; return a[0].compare() < b[0].compare() ? -1 : 1; } ); } if (typeof sortuj != 'undefined' && sortuj < 0) { document.write("<pre>"); for (var i = 0; i < hasla.length; i++) { document.write("['" + hasla[i][0].addSlashes() + "','" + hasla[i][1].addSlashes() + "']" + (i < hasla.length - 1 ? ",\n" : "")); } document.write("</pre>"); } else { document.write( '<form id="' + this.id + '" action="javascript:void(0)" onsubmit="' + this.id + '.wyswietl(); return false">' + '<input type="text" name="haslo" size="30" onkeyup="' + this.id + '.zaznacz()" class="text" />' + '<div><select name="hasla" size="' + (typeof rozmiar != 'undefined' && rozmiar ? rozmiar : 15) + '" ondblclick="' + this.id + '.wyswietl()">' ); for (var i = 0; i < hasla.length; i++) { document.write('<option value="' + ((typeof adres_bazowy == 'undefined' || !adres_bazowy ? '' : adres_bazowy) + hasla[i][1]).htmlSpecialChars(true) + '"' + (i == 0 ? ' selected="selected"' : '') + '>' + hasla[i][0].htmlSpecialChars() + '</option>'); } document.write( '</select></div>' + '<input type="submit" value="Wyświetl" class="button" onclick="this.form.submit()" /> <input type="button" value="Szukaj" onclick="' + this.id + '.szukaj()" class="button" />' + '</form>' + '<div id="' + this.id + '_szukaj"></div>' ); var matches = window.location.search.match(new RegExp('[?&]' + this.id + '=([^&]+)')); if (matches && typeof matches[1] != 'undefined') { document.forms[this.id].elements['haslo'].value = unescape(matches[1]); this.zaznacz(); this.szukaj(); } } } Indeks.prototype.wstawWyszukiwarke = function(adres) { document.write( '<form action="' + adres.htmlSpecialChars(true) + '" method="get" onsubmit="' + this.id + '.wyszukaj(this.action, this.elements[0].value); return false">' + '<input type="text" class="text" />' + '<input type="submit" value="Szukaj" class="button" />' + '</form>' ); } Indeks.prototype.wyszukaj = function(adres, haslo) { var search = adres.match(/#.*/); adres = adres.replace(/#.*/, '').replace(new RegExp('[&?]+' + this.id + '=[^&]*', 'g'), ''); if (adres.indexOf('&') >= 0 && adres.indexOf('?') < 0) adres = adres.replace(/&/, '?'); this.wyswietl(adres + (adres.indexOf('?') < 0 ? '?' : '&') + this.id + '=' + escape(haslo) + (search ? search : '')); } Indeks.prototype.wyswietl = function(adres) { if (typeof adres == 'undefined') adres = document.forms[this.id].elements['hasla'].value; if (typeof this.ramka == 'string') { var okno = window.open(adres, this.ramka, 'menubar=yes,toolbar=yes,location=yes,directories=no,status=yes,scrollbars=yes,resizable=yes'); if (okno) okno.focus(); else window.location.href = adres; } else this.ramka.location.href = adres; } Indeks.prototype.zaznacz = function() { var haslo = document.forms[this.id].elements['haslo'].value.toLowerCase(); for (var i = 0; i < document.forms[this.id].elements['hasla'].options.length; i++) { if (document.forms[this.id].elements['hasla'].options[i].text.toLowerCase().indexOf(haslo) == 0) { document.forms[this.id].elements['hasla'].options[i].selected = true; break; } } } Indeks.prototype.szukaj = function() { if (!this._szukaj.i) { if (document.forms[this.id].elements['haslo'].value == '') return; var haslo = document.forms[this.id].elements['haslo'].value.toLowerCase().replace(/[^a-ząćęłńóśźż_"]+/gi, ' '); this._szukaj.haslo = haslo.match(/"[^"]+"/g); if (!this._szukaj.haslo) this._szukaj.haslo = new Array(); else { for (var i = 0; i < this._szukaj.haslo.length; i++) { var pos = -1; var len = this._szukaj.haslo[i].length; while ((pos = haslo.indexOf(this._szukaj.haslo[i], pos + 1)) >= 0) { haslo = haslo.substring(0, pos) + haslo.substring(pos + len); } this._szukaj.haslo[i] = this._szukaj.haslo[i].replace(/(^[" ]+|[" ]+$)/g, ''); } } haslo = haslo.replace(/[" ]+/g, ' ').replace(/(^ | $)/g, ''); if (haslo != '' && haslo != ' ') { haslo = haslo.split(' '); for (var i = 0; i < haslo.length; i++) { this._szukaj.haslo[this._szukaj.haslo.length] = haslo[i]; } } var oHaslo = new Object(); for (var i = 0; i < this._szukaj.haslo.length; i++) { oHaslo[this._szukaj.haslo[i]] = this._szukaj.haslo[i]; } this._szukaj.haslo = new Array(); for (var haslo in oHaslo) { if (haslo.length > 2) this._szukaj.haslo[this._szukaj.haslo.length] = new RegExp('(^|[^a-ząćęłńóśźż_])' + haslo + '([^a-ząćęłńóśźż_]|$)'); } this._szukaj.html = ''; if (this._szukaj.haslo.length == 0) return; } for (; this._szukaj.i < document.forms[this.id].elements['hasla'].options.length; this._szukaj.i++) { for (var j = 0; j < this._szukaj.haslo.length; j++) { if (document.forms[this.id].elements['hasla'].options[this._szukaj.i].text.toLowerCase().search(this._szukaj.haslo[j]) >= 0) { var tag = new Array('<a href="' + document.forms[this.id].elements['hasla'].options[this._szukaj.i].value.htmlSpecialChars(true) + '"' + (this.ramka ? ' onclick="' + this.id + '.wyswietl(this.href); return false"' : '') + '>', '</a>'); var text = document.forms[this.id].elements['hasla'].options[this._szukaj.i].text.htmlSpecialChars(); var pos = this._szukaj.html.indexOf(tag[0]); if (pos >= 0) { var pos = this._szukaj.html.indexOf(tag[1], pos); this._szukaj.html = this._szukaj.html.substring(0, pos) + ',<br />' + text + this._szukaj.html.substring(pos); } else this._szukaj.html += '<li>' + tag[0] + text + tag[1] + '</li>'; break; } } if (this._szukaj.i && this._szukaj.i < document.forms[this.id].elements['hasla'].options.length - 1 && !(this._szukaj.i % 100)) { document.getElementById(this.id + '_szukaj').innerHTML = Math.floor(this._szukaj.i / document.forms[this.id].elements['hasla'].options.length * 100) + '%'; this._szukaj.i++; setTimeout(this.id + '.szukaj()', 1); return; } } document.getElementById(this.id + '_szukaj').innerHTML = '<p>Wyniki wyszukiwania: <em>' + document.forms[this.id].elements['haslo'].value.htmlSpecialChars() + '</em></p>' + (this._szukaj.html == '' ? '' : '<ol>' + this._szukaj.html + '</ol>'); this._szukaj.i = 0; } String.prototype.htmlSpecialChars = function(attribute) { var str = this.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); if (typeof attribute != 'undefined' && !attribute) str = str.replace(/"/g, '"'); return str; } String.prototype.addSlashes = function(limiter) { if (typeof limiter == 'undefined') limiter = "'"; var regExp = new RegExp(limiter, 'g'); return this.replace(/\\/g, '\\\\').replace(regExp, '\\' + limiter); } if (!String.prototype._toLowerCase) { String.prototype._toLowerCase = String.prototype.toLowerCase; String.prototype.toLowerCase = function() { return this._toLowerCase().replace(/[ĄĆĘŁŃÓŚŹŻ]/g, function(str) { if (str == 'Ą') return 'ą'; if (str == 'Ć') return 'ć'; if (str == 'Ę') return 'ę'; if (str == 'Ł') return 'ł'; if (str == 'Ń') return 'ń'; if (str == 'Ó') return 'ó'; if (str == 'Ś') return 'ś'; if (str == 'Ź') return 'ź'; if (str == 'Ż') return 'ż'; return str; } ); } } String.prototype.compare = function() { return this.toLowerCase().replace(/[ąćęłńóśźż]/g, function(str) { if (str == 'ą') return 'aż'; if (str == 'ć') return 'cż'; if (str == 'ę') return 'eż'; if (str == 'ł') return 'lż'; if (str == 'ń') return 'nż'; if (str == 'ó') return 'oż'; if (str == 'ś') return 'sż'; if (str == 'ź') return 'zż'; if (str == 'ż') return 'zżż'; return str; } ); }
Następnie w taki sam sposób utwórz drugi plik - tym razem pod nazwą indeks_hasla.js - i wklej w nim (znowu należy koniecznie pamiętać o ustawieniu w edytorze kodowania znaków iso-8859-2):
var indeks = new Indeks('indeks'); indeks.wstaw(new Array( ['Hasło 1','adres1'], ['Hasło 2','adres2'], ['Hasło 3','adres3'] ));
'
ani \
. Jeżeli takie znaki muszą się w nim znaleźć, należy je poprzedzić odwróconym ukośnikiem: \'
i \\
.Hasła powinny być podane w kolejności alfabetycznej.
Zauważ, że po każdym kolejnym wierszu z hasłami indeksu (oprócz ostatniego!) znajduje się przecinek.
Na koniec wystarczy otworzyć dokument (X)HTML, w których chcemy wstawić indeks i w jego nagłówku (<head>...</head>) wkleić jeden raz następujący kod:
<script type="text/javascript" charset="iso-8859-2" src="indeks.js"></script>
W tym samym dokumencie (X)HTML, ale już w wybranym miejscu strony, trzeba jeszcze wstawić:
<script type="text/javascript" charset="iso-8859-2" src="indeks_hasla.js"></script>
...i już można cieszyć się indeksem/wyszukiwarką na swojej stronie :-)
Czasami może zdarzyć się tak, że wszystkie adresy do haseł z indeksu rozpoczynają się tak samo. Oczywiście w takim przypadku ten sam fragment ścieżki można dodawać w każdym kolejnym haśle, ale można również zrobić to tylko raz. W tym celu należy zmodyfikować wpis w pliku indeks_hasla.js:
var indeks = new Indeks('indeks'); indeks.wstaw(new Array( ['Hasło 1','adres1'], ['Hasło 2','adres2'], ['Hasło 3','adres3'] ), 'adres_bazowy');
Teraz nie trzeba już wpisywać tego prefiksu w adresach na liście haseł.
Jeżeli zachodzi potrzeba zmiany wysokości okienka z hasłami, należy zmodyfikować wpis w pliku indeks_hasla.js:
var indeks = new Indeks('indeks'); indeks.wstaw(new Array( ['Hasło 1','adres1'], ['Hasło 2','adres2'], ['Hasło 3','adres3'] ), null, rozmiar);
Jak już wspomniono, dla poprawnego działania indeksu konieczne jest, aby hasła na liście były ułożone w kolejności alfabetycznej. Można ustawić specjalną opcję, tak aby sortowanie alfabetyczne odbywało się automatycznie:
var indeks = new Indeks('indeks'); indeks.wstaw(new Array( ['Hasło 1','adres1'], ['Hasło 2','adres2'], ['Hasło 3','adres3'] ), null, null, true);
Ustawienie tej opcji może znacznie wydłużyć generowanie indeksu!
Aby wyeliminować tę niedogodność, a jednocześnie nie musieć przy każdym dodawaniu haseł do indeksu żmudnie układać je według alfabetu, można testowo użyć takiego kodu:
var indeks = new Indeks('indeks'); indeks.wstaw(new Array( ['Hasło 1','adres1'], ['Hasło 2','adres2'], ['Hasło 3','adres3'] ), null, null, -1);
Po jego wpisaniu, na stronie nie zostanie wyświetlony normalny indeks, ale fragment kodu skryptu z hasłami ułożonymi alfabetycznie. Wystarczy ten kod skopiować, wkleić w odpowiednie miejsce do pliku indeks_hasla.js i na koniec usunąć wartość opcji (wraz z końcowym przecinkiem przed zamknięciem nawiasu).
Wszystkie z przedstawionych powyżej opcji można łączyć, przy czym skrajane argumenty z prawej strony, których nie chcemy podawać, można po prostu pominąć. Dla pozostałych, które chcemy pominąć, wystarczy wpisać null
(ale nie 'null'!), np.:
var indeks = new Indeks('indeks'); indeks.wstaw(new Array( ['Hasło 1','adres1'], ['Hasło 2','adres2'], ['Hasło 3','adres3'] ), null, 10);
Jeżeli Twój serwis wykorzystuje ramki, a jednocześnie dokumenty, do których odsyłają poszczególne hasła, mają się wyświetlać w innej ramce (np. indeks znajduje się w ramce spisu treści, a wyniki mają być wczytywane w ramce głównej - z treścią), trzeba będzie podać ramkę, do której mają być wczytywane dokumenty z indeksu. Należy to zrobić w pliku indeks_hasla.js:
var indeks = new Indeks('indeks', ramka); indeks.wstaw(new Array( ['Hasło 1','adres1'], ['Hasło 2','adres2'], ['Hasło 3','adres3'] ));
self
self.nazwaramki
parent.nazwaramki
top.nazwaramki
Aby otworzyć stronę w nowym oknie, należy zmienić plik indeks_hasla.js następująco:
var indeks = new Indeks('indeks', 'nazwaokna'); indeks.wstaw(new Array( ['Hasło 1','adres1'], ['Hasło 2','adres2'], ['Hasło 3','adres3'] ));
Jeżeli nie odpowiada nam podstawowy wygląd indeksu, można go zmienić wykorzystując polecenia CSS. W tym celu w pliku indeks.css należy wkleić np.:
#indeks { width: 300px; padding: 10px 0; background: #ccc; border: 2px outset #aaa; text-align: center; margin: 1em auto; } #indeks input.text { width: 290px; } #indeks select { width: 100%; margin: 2px 0 10px 0; } #indeks input.button { width: 100px; margin-left: 5px; margin-right: 5px; }
W wyróżnionych miejscach wpisano identyfikator indeksu, zdefiniowany uprzednio w pliku indeks_hasla.js. Teraz w nagłówku dokumentu (X)HTML (<head>...</head>), na którym wstawiony jest indeks haseł, wystarczy wstawić odwołanie do utworzonego właśnie zewnętrznego arkusza stylów:
<link rel="Stylesheet" type="text/css" href="indeks.css" />
Bardzo pomocne dla użytkowników może okazać się wstawienie formularza wyszukiwania na wszystkich stronach serwisu. Aby to zrobić, należy wstawić w nagłówku strony (<head>...</head>) odwołanie do pliku indeks.js:
<script type="text/javascript" charset="iso-8859-2" src="indeks.js"></script>
Nie trzeba natomiast wstawiać odwołania do skryptu indeks_hasla.js, w którym znajduje się lista haseł. Następnie w wybranym miejscu strony wystarczy wkleić:
<script type="text/javascript"> // <![CDATA[ var indeks = new Indeks('indeks'); indeks.wstawWyszukiwarke('adres'); // ]]> </script>
Można również podać nazwę ramki lub wymusić otwieranie wyników z wyszukiwarki w nowym oknie - dokładnie jak w pliku indeks_hasla.js:
<script type="text/javascript"> // <![CDATA[ var indeks = new Indeks('indeks', ramka); indeks.wstawWyszukiwarke('adres'); // ]]> </script>lub
<script type="text/javascript"> // <![CDATA[ var indeks = new Indeks('indeks', 'nazwaokna'); indeks.wstawWyszukiwarke('adres'); // ]]> </script>
Przykład:
Demonstrację działania wyszukiwarki można obejrzeć w części Przykład tego rozdziału. Zwróć uwagę, że po wpisaniu hasła i kliknięciu przycisku "Szukaj", nastąpi wczytanie strony z indeksem, w którym automatycznie uruchomi się proces wyszukiwania wpisanych wyrazów.