Dinamično sodelovanje
S sodelovanjem odjemalcev in strežnikov so spletne strani lahko precej uporabnejše in bolj zanimive.
Da so spletni brskalniki vse bolj zmogljivi, smo na teh straneh ugotavljali že pred nekaj meseci. Izboljšave, ki jih je prinesel razvoj tehnologij, kot so HTML, CSS, javascript in DOM, so v brskalnikih omogočile izdelavo tudi bolj zapletenih, dinamičnih uporabniških vmesnikov. Eno vodilnih podjetij, ki postavlja nove standarde v prijaznosti do uporabnika, je prav gotovo Google, ki je na spletnih straneh predstavil že kup inovativnih vmesnikov. Od navdušujoče preproste vstopne strani od silno zmogljivega vmesnika za spletno e-pošto Gmail. Pravo moč in bodočo usmeritev razvoja spletnih uporabniških vmesnikov pa je nakazala storitev Google Suggests, ki je med razvijalci znova vzbudila vroče zanimanje za javascript in njegovo možnost povezovanja s spletnim strežnikom, brez potrebe po vnovičnem nalaganju strani.
Z novo storitvijo, ki jo med nastajanjem tega članka še preizkušajo, je Google omogočil, da med vnašanjem niza za preiskovanje že prikazuje možne izraze, skupaj s številom zadetkov, ki bi jih prikazali. Storitev zelo spominja na priljubljeno samodopolnjevanje (auto-completion), ki ga uporabljajo številni namenski programi, tudi spletni brskalniki, da uporabniku olajšajo vnos. Delovanje pripomočka Google Suggests (zaenkrat za preiskovanje v angleščini) lahko preverimo na spletnem naslovu [1].
Zanimiva nova storitev Googla je povečala zanimanje za zmogljivo skriptno kodo na strani odjemalca.
Slovarček
Podobne dinamične prijeme lahko uporabimo tudi na lastnih spletnih straneh. Treba je le poznati sodobne spletne tehnologije, predvsem skriptni jezik javascript, ki vse skupaj pravzaprav omogoča. A pojdimo po vrsti. Najprej si bomo ogledali možnost vzpostavitve slovarčka, ki prikazuje razlage posameznih besed na spletni strani. Z njegovo pomočjo bomo utrdili poznavanje dinamičnega HTML (spomnimo se enačbe iz prejšnjega članka: dinamični HTML = HTML + CSS + DOM + javascript) in pridobili potrebne osnove, da si podrobneje ogledamo tudi delovanje zanimive Googlove novosti, ki jo bomo lahko pozneje prilagodili lastnim potrebam.
Slovarček bo storitev spletne strani, ki lahko ponudi razlago določenih besed. Lahko bi se odločili, da se požene samodejno, ob prikazu spletne strani, vendar je bolj običajno, da jo po potrebi pokliče sam uporabnik. Po aktivaciji storitve slovarčka se prepoznane besede na spletni strani označijo na poseben način, ponavadi postanejo povezave, klik nanje pa ponudi daljšo razlago.
Mi bomo slovarček še nekoliko izboljšali. Že v prejšnjem prispevku o dinamičnih spletnih straneh smo spoznali možnost prikaza namigov, posebnih "plavajočih" okenc, ki se prikažejo na želenem mestu nad vsebino spletne strani. Tudi tu bomo izkoristili namige, in sicer bomo kar v njih pokazali krajšo razlago pojma iz slovarčka. Še vedno bomo ohranili možnost klika povezave, ki bi lahko prikazala podrobnejšo razlago, a na drugi spletni strani.
Preden se lotimo programiranja, razmislimo še malo o uporabnosti take storitve. Spletne strani so pogosto sestavljene iz številnih elementov. Gotovo si ne želimo, da bi označevali tudi besede v morebitnih oglasih, na menujih, v orodnih vrsticah in podobnem. Zato bomo izbrali možnost, da kos spletne strani, kjer želimo storitev slovarčka, posebej označimo. Za označevanje blokov na stani je najprimernejša oznaka <DIV>, ki ji pripišemo ustrezno enolično oznako (id).
Spletna stran
Torej bo v osnovi naša spletna stran videti taka:
<html>
<head>
<title>Slovar</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1250">
<!-- slogi za slovar -->
<link rel="stylesheet" href="slovar.css" type="text/css">
<!-- koda slovarja -->
<script src="slovar.js"></script>
</head>
<body>
<h1>Slovar</h1>
<p>Besedilo, ki ga ne pregledujemo...</p>
<div id="slovar_txt">
<p>Besedilo, ki ga <i>pregledujemo</i>.</p>
<p><b>Razlaga</b> z razlogom.</p>
</div>
<p>Besedilo, ki ga ne pregledujemo...</p>
<div id="slovar_pop"><!-- bodoči plavajoči namig --></div>
</body>
</html>
V glavi smo vključili datoteki s slogi in kodo, v telesu strani pa lahko opazimo dva bloka (<DIV>). Prvi je označen s "slovar_txt", v njem pa so tisti deli spletne strani, ki bi jih radi pregledali ob pomoči slovarja. Drugi del, označen je s "slovar_pop", pa bo s slogom določen za izhodiščno nevidnega in bo služil prikazu namiga.
Programsko kodo bi lahko razvili v obliki globalno dostopnih podatkov in funkcij, vendar je tak pristop pogosto neprimeren, saj hitro povzroči težave zaradi enotnega imenskega prostora, v katerem nastopajo vsi označevalci (identifiers). Javascript sicer ni popolnoma predmeten programski jezik, vendar pozna predmete kot podatkovno-kodno strukturo. Pozna celo posebno obliko zapisa, s katerim lahko nekakšen razred na hitro zapišemo in s tem omejimo pomensko sorodno kodo v nov imenski prostor.
Denimo, da bomo vse potrebne podatke in kodo za delovanje slovarja shranili v imenski prostor oz. razred z imenom _slovar. Uvodni podčrtaj smo izbrali, da ne bi prišlo do konflikta imen s kakšno globalno spremenljivko, ki bi bila zlahka lahko poimenovana "slovar". V praksi se izkaže, da je za vso kodo koristno izbrati določeno predpono, ki jo dodamo vsem lastnim razredom, naš slovar bi se lahko imenoval tudi "podjetje_slovar", pri čimer seveda "podjetje" nadomestimo z imenom podjetja ali organizacije, v kateri delamo. Hiter zapis razreda v javascriptu je mogoč v naslednji obliki:
<ime razreda> = {
<spremenljivka1> : <vrednost1>,
[ <spremenljivka2> : <vrednost2>, ... ]
<funkcija1> : function ( <parametri1> ) { <stavki funkcije1> },
[ <funkcija2> : function ( <parametri2> ) { <stavki funkcije2> }, ... ]
}
Da bo učinek slovarja viden, moramo najti razlage za nekaj besed iz bloka, ki ga bomo pregledovali. Najbolje, da uporabimo kar Slovar slovenskega knjižnega jezika (SSKJ), ki je na srečo na voljo v spletu [2]. Izbrali bomo le nekaj besed in jih shranili v posebno podatkovno strukturo znotraj našega razreda:
_slovar = {
besede : [ {
k : "besedil",
c : "besedilo",
r : "1. z določenimi besedami izražene misli<br />2. ..."
}, {
k : "razlag",
c : "razlaga",
r : "1. kar kaj razlaga, tolmači<br />2. glagolnik od razlagati"
}, {
k : "razlog",
c : "razlog",
r : "kar utemeljuje, podpira kako ravnanje, odločitev"
}
],
// sledi preostanek razreda...
}
Javascript je praktičen tudi pri zapisu podatkovnih struktur. Polja je mogoče vzpostaviti tako, da začetne vrednosti naštejemo znotraj oglatega oklepaja, ločene z vejico. No, elementi polja pa so lahko poljubne podatkovne strukture, tudi novi (podatkovni) razredi. Zato smo vsak element polja določili znotraj zavitega oklepaja, kjer smo navedli imena spremenljivk in njihove vrednosti. Vsak element v polju besede je pravzaprav polje z elementi k (koren besede), c (celotna beseda) in r (razlaga). Zaradi pregibnosti slovenskega jezika smo izbrali rešitev s korenom, ki sicer ni popolnoma zanesljiva, vendar deluje zadosti dobro. V besedilu, ki ga bomo pregledovali, bomo torej iskali navedene korene besed. Za prikaz namiga pa potrebujemo celotno obliko besede in krajšo razlago. Ker se bo razlaga prikazala znotraj spletne strani, lahko v njej uporabimo oznake HTML, če želimo, in tako oblikujemo izpis.
Preklop slovarja
Storitev slovarja bo delovala na preklop. Imeli bomo spremenljivko stanja aktiven, ki bo hranila dejavnost slovarja ter dve funkciji, vklop() in izklop(). Vse skupaj bo nadzirala funkcija init(), ki jo bomo tudi klicali s spletne strani, kadar bo treba slovar vklopiti ali izklopiti:
_slovar = {
besede : [ ... ],
aktiven : false,
init : function() {
if ( this.aktiven )
this.izklop();
else
this.vklop();
this.aktiven = !this.aktiven;
},
// sledi preostanek razreda...
}
Zaradi lastnega imenskega prostora se moramo na krajevne funkcije in spremenljivke sklicevati z rezervirano besedo this. Delo nadaljujemo z razvojem funkcije za vklop slovarja. Dela bo kar nekaj, zato si najprej oglejmo simbolični zapis:
vklop : function() {
// preverimo sposobnosti brskalnika
// shranimo HTML označenega bloka
// izločimo in si shranimo oznake HTML iz besedila
// označimo besede iz slovarja
// povrnemo oznake HTML na stara mesta
// zamenjamo besedilo označenega bloka
},
Opravila so po vrsti kodirana takole:
// preverimo sposobnosti brskalnika
if (
!document.getElementById ||
!document.getElementById( "slovar_txt" ) ||
!document.getElementById( "slovar_txt" ).innerHTML
) {
alert( "Brskalnik ne omogoča rabe slovarja!" );
return;
}
Z znanim prijemom preverimo, ali so podprte nujno potrebne funkcije DOM. Če katera izmed njih ni, prikažemo opozorilo in se predčasno vrnemo iz funkcije.
// shranimo HTML označenega bloka
var slovarDiv = document.getElementById( "slovar_txt" );
var zaPregled = slovarDiv.innerHTML;
S funkcijami DOM pridemo do celotne kode HTML za označeni blok na spletni strani. Shranjena je v spremenljivki zaPregled.
// izločimo in si shranimo oznake HTML
var re = /<[^<>]*>/gi;
var oznakeHTML = zaPregled.match( re );
var oznaciTxt = zaPregled.replace( re, '$!$' );
Ker nas oznake HTML ne zanimajo in se vanje pravzaprav ne smemo niti vtikati, jih izločimo. Seveda pa jih moramo shraniti za poznejšo rekonstrukcijo. Pomaga nam ustrezen regularni izraz in metoda match(), ki vse zadetke regularnega izraza vrne v obliki polja. Na koncu smo v kodo HTML namesto vseh oznak zapisali neobičajno zaporedje znakov, $!$. Pomembno je, da se tako zaporedje v običajnem besedilu ne more pojaviti.
Obravnava regularnih izrazov nekoliko presega namen tega članka. Vsekakor pa gre za eno najkoristnejših orodij javascripta za delo z nizi. S pomočjo poševnic (/) je mogoče v javascriptu zapisati konstanten regularen izraz, ki ga lahko uporabimo v različnih funkcijah, kot sta npr. match() in replace() iz zgornjega zgleda. Spodaj bomo videli tudi zgled izdelave spremenljivega regularnega izraza. Več o tej tematiki najdemo na spletnem naslovu [3].
Zdaj se lahko lotimo označevanja znanih besed v "prečiščenem" besedilu:
// označimo besede iz slovarja
for ( var slv = 0; slv < this.besede.length; slv++ ) {
// predelamo koren v regularni izraz
var txt = this.besede[slv].k.replace( /\s+/g, "[a-zčšž]*\\s+" );
txt = "\\b(" + txt.substring( 0, txt.length ) + "[a-zčšž]*)";
// ustvarimo ustrezen predmet v javascriptu
var re2 = new RegExp( txt, 'gi' );
// najdemo in zamenjamo vse pojavitve
oznaciTxt = oznaciTxt.replace( re2,
'<a class="slovar" ' +
'href="http://bos.zrc-sazu.si/cgi/neva.php?' +
'name=sskj&expression=ge%3D' + this.besede[slv].k +
'*" target="_blank" ' +
'onmouseover="_slovar.namig(this,' + slv + ')" ' +
'onmouseout="_slovar.namig(this,null)">$1</a>'
);
}
V zanki se sprehodimo čez celotno polje besede. Označevanje je pripravljeno tudi za besedne zveze, zato koren nekoliko prilagodimo. Vse morebitne presledke zamenjamo z regularnim izrazom, ki dovoli poljubno število ponovitev črke (angleški abecedi a-z smo dodali še slovenske črke č, š in ž). Če bi s slovarjem npr. želeli razložiti besedno zvezo, kot je "stalno reklo", bi v koren zapisali "staln rekl", iz česar bi nastal regularni izraz: \b(staln[a-zčšž]*\s+rekl[a-zčšž]*).
V nadaljevanju kode vidimo, kako lahko iz niza izdelamo regularni izraz, kajti zapis konstantnega izraza, kot smo ga uporabili prej, tokrat ni mogoč. Sledi iskanje vseh zadetkov, ki jih s pomočjo vrivanja nizov spremenimo v povezave po naši meri. Če imamo npr. koren besedil, bomo z regularnim izrazom \b(besedil[a-zčšž]*)našli vse besede, ki se začno s tem korenom, in jih zamenjali v naslednjo obliko:
Besedilo
Besedilo
<a class="slovar" href="..." target="_blank"
onmouseover="_slovar.namig(this,6)"
onmouseout="_slovar.namig(this,null)"
>Besedilo</a>
Podrobnosti povezave (href) smo izpustili. Iz dejanske kode pa je razvidno, da se pravzaprav povežemo kar s SSKJ v spletu, kjer lahko uporabnik najde podrobnejšo razlago. Za nas je trenutno bolj zanimivo, kaj se zgodi, ko z miško pokažemo na tako povezavo. Kot lahko vidimo, bomo morali pripraviti še eno razredno funkcijo, namig(), ki bo znala poskrbeti za prikaz krajše razlage na pravem mestu glede na posredovani indeks polja (v zgornjem zgledu je izmišljen, 6), oziroma za skrivanje namiga, če je indeks nedoločen (null). Preden pa to funkcijo spoznamo, preglejmo označevanje in odznačevanje do konca.
// povrnemo oznake HTML
for( var j = 0; oznaciTxt.indexOf("$!$") > -1; j++ ) {
oznaciTxt = oznaciTxt.replace( "$!$", oznakeHTML[j] );
}
slovarDiv.innerHTML = oznaciTxt;
Ko smo s pomočjo korenov označili vse znane besede, povrnemo prej shranjene oznake HTML na ustrezna mesta. Preprosto zamenjujemo nadomestna zaporedja $!$ z elementi polja, dokler še najdemo kakega.
Na koncu z našim izdelkom nadomestimo obstoječo kodo v označenem bloku spletne strani in zaključimo izvajanje funkcije.
Označevanje besed iz slovarja že deluje, potrebujemo pa še nekaj slogov, da bo vse skupaj videti lepše.
Funkcija izklop() je precej preprostejša, potrebujemo le en regularen izraz, ki odstrani vse vstavljene oznake:
izklop : function() {
var slovarDiv = document.getElementById("slovar_txt");
// shranimo HTML
var zaPregled = slovarDiv.innerHTML;
// odstranimo oznake slovarja
var re = /<a class="?slovar[^>]*>(.*?)<\/a>/gi; //"
slovarDiv.innerHTML = zaPregled.replace(re, "$1");
},
Namig
Zdaj je čas, da si ogledamo še funkcijo namig(). V resnici je zelo podobna zgledu iz prejšnjega članka, ko smo opozarjali na posebne vrste povezav:
namig : function( a, slv ) {
var popup = document.getElementById("slovar_pop");
if ( popup )
if ( slv == null )
popup.style.visibility = "hidden";
else {
var p = this.getpos(a);
popup.style.left = p.x + 12 + "px";
popup.style.top = p.y + 24 + "px";
var sld = this.besede[slv];
popup.innerHTML = "<b>" + sld.c + "</b><br />" + sld.r;
popup.style.visibility = "visible";
}
},
Sposobnosti brskalnika ne preverjamo več, saj se funkcija sploh ne bo klicala, če že označevanje ugotovi, da je brskalnik preslaboten. Če je drugi parameter nedoločen (null), spremenimo vidnost namiga, v nasprotnem primeru pa izračunamo položaj povezave. Tja prestavimo še neviden namig, mu vstavimo oblikovano celotno besedo in razlago ter ga na koncu še prikažemo.
Za izračun položaja elementa znova uporabljamo funkcijo getpos(), le da smo jo tokrat izdelali v iterativni namesto v rekurzivni obliki, ki jo poznamo iz prejšnjega članka:
getpos : function( obj ) {
var coordinates = {x: 0, y:0};
if ( obj ) {
coordinates.x = this.getposX(obj);
coordinates.y = this.getposY(obj);
}
return coordinates;
},
getposX : function( obj ) {
var curleft = 0;
if ( obj.offsetParent ) {
while ( obj.offsetParent ) {
curleft += obj.offsetLeft;
obj = obj.offsetParent;
}
} else if ( obj.x )
curleft += obj.x;
return curleft;
},
getposY : function( obj ) {
var curtop = 0;
if ( obj.offsetParent ) {
while ( obj.offsetParent ) {
curtop += obj.offsetTop;
obj = obj.offsetParent;
}
} else if ( obj.y )
curtop += obj.y;
return curtop;
}
S tem je naš razred v celoti izdelan.
Za aktiviranje/skrivanje označevanja moramo na ustrezno mesto na spletni strani dodati klic funkcije. To je lahko običajna povezava, ki ji dodamo klic v javascriptu:
<p><b><a href="javascript:void(_slovar.init())">{{{ Razloži! }}}</a></b></p>
Slogi
Poigrajmo se še s slogi. Učinkovita rešitev bi lahko bila taka:
DIV#slovar_pop {
position: absolute;
visibility: hidden;
width: 220px;
z-order: 2;
border: solid 1px #f00;
background-color: #fee;
color: #336;
padding: 2px;
font-family: helvetica,sans-serif;
font-size: 11px;
-moz-opacity:0.9;
filter:alpha(opacity=90);
}
A.slovar {
color: #fc0;
padding-left: 10px;
padding-top: 2px;
padding-right: 2px;
padding-bottom: 2px;
background: #c00 url(slovar.gif) no-repeat;
text-decoration: none;
}
Elementu z oznako (id) slovar_pop določimo absolutni položaj in začetno nevidnost. To je seveda naš bodoči namig. Druga določila opredeljujejo njegov videz, za kozmetično podrobnost smo na koncu dodali še dve nestandardni določili za delno prosojnost (za Mozillo in Internet Explorer/Opero).
Nekoliko smo se pozabavali tudi pri slogu, ki ga dobijo vstavljene oznake. Da je vse skupaj videti še bolj nazorno, smo dodali nekaj praznega prostora na levi in vanj vstavili sliko v ozadju. Videz povezav in namiga je viden na sliki.
Oblikovan prikaz namiga in povezav s pomočjo slogov CSS.
S klikom dinamično ustvarjenih povezav dobimo podrobnejšo razlago iz spletne različice SSKJ.
Dinamična povezava
Zgled deluje po pričakovanjih, vendar je zgolj demonstracija. Praktično uporaben slovar bi moral vsebovati precej več kot le tri besedne korene z razlagami. Tu naletimo na podoben problem, kot ga je reševal Google pri svoji storitvi Suggest. V določenih primerih je slovar zadosti majhen in nespremenljiv, da celotna skriptna koda ne preseže nekaj 10kB, kar je znosna velikost za praktično vse internetne povezave. Povsem drugače pa je pri zelo obsežnih slovarjih, še posebej, če so dinamični, torej odvisni od spreminjajoče se vsebine, kot je npr. vnos iskanega izraza. Kako so se torej problema lotili pri Googlu?
Kot vemo, na spletnih straneh skrivanje kode ni nekaj običajnega, zato lahko obiščemo stran [1] in preverimo izvirno kodo. Opazimo lahko, da je večina skriptne kode v posebni datoteki, ac.js. Če jo naložimo in pregledamo, ni videti nič kaj obetavna:
// Copyright 2004 and onwards Google Inc.
var w=""; var pa=false; var ta=""; var da=false; var g=""; var G=""; var m=""; var j=-1; var h=null; var Z=-1; var za=null; var Ca=5; var q=""; var Lb="div"; var Bb="span"; var la=null; var a=null; var b=null; var Xa=null; var mb=null; var X=null; var ha=null; var ra=false; var kc=null; var hc=null; var Ua=new Object(); var ca=1; var Aa=1; var Y=false; var na=-1; var Va=(new Date()).getTime(); var Q=false; var k=null; var sa=null; var E=null; var B=null; var aa=null; var Ba=false; var Ka=false; var p=60; var ia=null; var ya=null; var W=0; InstallAC=function(frm,fld,sb,pn,rl,hd,sm,ufn) { la=frm; a=fld; Xa=sb; if(!pn) pn="search"; ia=pn; var Kb="en|"; var Jb="zh-CN|zh-TW|ja|ko|vi|"; if(!rl||Kb.indexOf(rl+"|")==-1) rl="en"; ha=nb(rl); if(Jb.indexOf(ha+"|")==-1) { X=true; Y=false; Ba=false } else { X=false; if(ha.indexOf("zh")==0) Y=false; else Y=true; Ba=true } if(!hd) hd=false; ya=hd; if(!sm) sm="query"; w=sm; mb=ufn; ac() };
function Yb() { ra=true; a.blur(); setTimeout("sfi();",10); return }
function Fb() { if(document.createEventObject) { var y=document.createEventObject(); y.ctrlKey=true; y.keyCode=70; document.fireEvent("onkeydown",y) } }
// se nadaljuje...
Če je na prvi pogled koda morda videti šifrirana (encrypted), je v resnici le stisnjena (compressed). Če vzamemo v račun dnevni obisk spletišča, kot je Google, potem nam postane jasno, da vsak prihranjeni kB pomeni znatno manjšo obremenitev povezav. Torej imamo izhodišče, ki ga lahko preučujemo.
Na srečo nam vsega dela ni treba opraviti, ker se je že našla dobra duša in skriptno kodo predelala v razumljivejšo obliko. Kanadčan Chris Justus si je vzel nekaj časa, saj ga je reč zelo zanimala. Pripravil je predelano obliko skriptne kode, ki jo velja preučiti, saj je zelo poučna [5].
Zanimiv predmet
Stvar, ki omogoča "čarovnijo" Google Suggests in je tudi odgovor na naš zgoraj opisani problem, je gradnik XMLXTTP (za Okna) oz. XMLHttpRequest (za druge podlage, na katerih je na voljo brskalnik Netscape/Mozilla/Firefox). Z njegovo pomočjo lahko skriptna koda opravi pogovor s poljubnim spletnim strežnikom, ne da bi se morala glavna spletna stran osvežiti, torej znova naložiti iz strežnika. Uporaba omenjenega gradnika bo zagotovo postala pomemben element spletnih uporabniških vmesnikov prihodnosti, saj skupaj z eleganco, ki jo omogoča dinamični HTML, ponuja povsem nove razsežnosti uporabniške izkušnje, katere droben delček nam ponazarja Googlova novotarija.
Mimogrede omenimo, da je Google Suggests na voljo tudi za brskalnike, ki omenjenih gradnikov ne podpirajo. V tem primeru nalaga predloge v poseben okvir, to pa da podoben učinek, le da je postopek precej manj eleganten. Glede na aktualno stanje pri spletnih brskalnikih se lahko brez večje škode ukvarjamo le s sodobnejšo rešitvijo, saj je prihodnost na njeni strani.
Ključna funkcija, ki poskrbi za varno stvaritev potrebnega predmeta, je v nestisnjeni različici Googlove kode, ki jo je pripravil Justus, videti taka:
// izvirno jb()
// vrne predmet XMLHttp object
function getXMLHTTP() {
var A = null;
try {
A = new ActiveXObject("Msxml2.XMLHTTP")
} catch(e) {
try {
A = new ActiveXObject("Microsoft.XMLHTTP")
} catch( oc ) {
A = null;
}
}
if( !A && typeof XMLHttpRequest != "undefined") {
A = new XMLHttpRequest()
}
return A;
}
funkcija poizkusi najprej ustvariti enega od predmetov COM/ActiveX, kar ji bo uspelo na dovolj sodobnih Oknih v brskalniku IE, če pa to spodleti, poizkusi ustvariti še Mozillin predmet XMLHttpRequest.
Predlogi
Googlova koda glavnino časa preživi v funkciji, ki kliče samo sebe v izračunanem časovnem intervalu, ki se prilagaja hitrosti naše povezave. Na počasnejših povezavah je tako povsem mogoče, da bomo vnesli več znakov in šele po tem dobili osvežen seznam predlogov. Justus je kodo predelal v naslednjo obliko:
// izvirno idfn()
// glavna funkcija, ki v izračunanem
// časovnem intervalu kliče samo sebe
mainLoop=function(){
if(_oldInputFieldValue!=_currentInputFieldValue){
if(!da){
var Za=escapeURI(_currentInputFieldValue);
var ma=_resultCache[_currentInputFieldValue];
if (ma) {
// najdeno v predpomnilniku
Va=-1;
sendRPCDone(
B, _currentInputFieldValue,
ma[0], ma[1],
B.completeDiv.prefixStrings
)
}else{
_timeoutAdjustment++;
Va=(new Date()).getTime();
callGoogle(Za)
}
_inputField.focus()
}
da=false
}
_oldInputFieldValue=_currentInputFieldValue;
setTimeout("mainLoop()",recalculateTimeout(_timeoutAdjustment));
return true
};
// pokliči mainLoop() po 10 ms
setTimeout("mainLoop()",10);
Funkcija mainLoop() se samodejno aktivira po 10 ms. Sama najprej preveri, ali se je vsebina vnosnega polja vmes spremenila, in se odzove le ob spremembi. Najprej preveri, ali ni zahtevan izid predlogov morda že shranjen v predpomnilniku. V tem primeru bi si prihranili zamudno poizvedovanje v strežniku. Če želenega izida ne najdemo, sledi klic strežnika, ki ga je Justus predelal v funkciji callGoogle():
// izvirno fc()
// izvede klic strežnika preko URL:
// http://www.google.com/complete/search?hl=en&js=true&qu=<vnos>
// ki vrne besedilo/kodo:
// sendRPCDone(frameElement, "<vnos>", new Array(
// "<predlog1>", "<predlog2>", "<predlog3>"...),
// new Array("<izidov1>", "<izidov2>", "<izidov3>"...),
// new Array("")
// );
function callGoogle(Rb){
if ( _xmlHttp.readyState != 0 ) {
_xmlHttp.abort()
}
_xmlHttp=getXMLHTTP();
_xmlHttp.open("GET",_completeSearchEnString+"&js=true&qu="+Rb,true);
// brezimna funkcija, ki sledi, se bo izvedla le, ko prejmemo izid
_xmlHttp.onreadystatechange = function() {
if (_xmlHttp.readyState==4 && _xmlHttp.responseText ) {
if(_xmlHttp.responseText.charAt(0)=="<"){
_timeoutAdjustment--
} else {
// vrnjeno besedilo izvedemo
// kot kodo javascripta
eval(_xmlHttp.responseText)
}
}
};
// naslednja vrstica se ne sme izvajati,
// če preizkušamo datoteko krajevno
_xmlHttp.send(null);
}
Skriptna koda ustvari poseben naslov URL, ki ga posreduje strežniku. Za tem nastavi ključni gradnik tako, da bo v primeru pravega odgovora izvedel posebno, brezimno funkcijo. Njena ključna naloga je, da izvede "besedilo", ki ga prejmemo kot odgovor. Zelo pomembno je torej, da nam strežnik vrne niz, ki je izvedljivi stavek v javascriptu. Ta pozna posebno funkcijo, eval(), ki lahko vsak niz izvede kot kodo.
Odgovore strežnika lahko preverjamo tudi ročno, z vnosom ustreznega naslova URL v brskalnik.
Iz komentarja vidimo, da se v vrnjenem besedilu/kodi kliče funkcija sendRPCDone(), ki ji pošljemo nekaj parametrov:
// funkcija, ki se kliče iz vrnjenega besedila/kode
sendRPCDone = function(fr, is, cs, ds, pr) {
if(_timeoutAdjustment>0) {
_timeoutAdjustment--;
}
var lc = (new Date()).getTime();
if(!fr) {
fr=B;
}
cacheResults(is,cs,ds);
var b = fr.completeDiv;
b.completeStrings = cs;
b.displayStrings = ds;
b.prefixStrings = pr;
displaySuggestedList(b, b.completeStrings, b.displayStrings);
Pa(b, valueOfCAutoComplete);
if(_completeDivRows2 > 0) {
b.height = 16*_completeDivRows2 + 4;
} else {
hideCompleteDiv();
}
}
Vidimo, da funkcija shrani izid v medpomnilnik in pokliče pomožno funkcijo za prikaz predlogov, displaySuggestedList(). Ta s pomočjo metod DOM ustvari vsebino lebdečega bloka, podobnega namigu iz našega slovarja. V njem se vsak predlog iz polja, ki ga je vrnil strežnik, predstavi kot blok <DIV> posebnega sloga.
Googlova koda, ki jo je Justus tako prijazno predelal v preglednejšo obliko, skriva še nekaj zanimivih podrobnosti, predvsem upravljanje vmesnika s tipkovnico in dinamično označevanje besedila v vnosnem polju, vendar nas te podrobnosti ta trenutek ne zanimajo. Ključne informacije smo dobili. Za povsem drugačno uporabniško izkušnjo pri delu s spletnimi stranmi torej potrebujemo ustrezen gradnik, programsko rešeno klicanje glavne funkcije v časovni zanki, ki se prilagaja hitrosti povezave, vzpostavljen predpomnilnik, kajti količina vrnjenih podatkov pri dinamičnem delu ne bo velika, hitrostni prihranki pa bodo precejšnji (menjamo prostor za čas), ter program v strežniku, ki ga bomo klicali z ustreznim naslovom URL, ta pa nam bo kot odgovor vrnil niz, ki je že pripravljena skriptna koda, nared za posredovanje funkciji eval(), ki jo bo izvedla.
Kako bomo naš slovar prilagodili v tako obliko, pa prihranimo za nadaljevanje prihodnjič. V vmesnem času toplo priporočam ogled in podrobnejšo analizo kode, ki jo najdemo na [5]. Kodo in datoteke zgleda iz tega članka pa najdete na [6].
Viri in dodatna literatura:
[1] Google Suggests (http://www.google.com/webhp?complete=1&hl=en)
[2] SSKJ v spletu (http://bos.zrc-sazu.si/sskj.html)
[3] Pattern Matching and Regular Expressions (http://www.webreference.com/js/column5/)
[4] Gecko DOM Reference (http://www.mozilla.org/docs/dom/domref/)
[5] Google Suggest Dissected, Chris Justus (http://serversideguy.blogspot.com/2004/12/google-suggest-dissected.html)
[6] Koda in dodatne datoteke zgleda "Slovar" (http://www.monitor.si/datoteke/slovar.zip)