kolmapäev, 29. september 2010

Progressist JavaScript põhise e-posti serveri kirjutamisel

Mõnda aega tagasi kirjutasin kuidas sain valmis POP3 protokolli implementatsiooni Javascriptis. Sain sellest katsetusest pisiku külge ja otsustasin proovida luua samas stiilis täiemahulist e-posti serveri tarkvara. Väga kaugele pole jõudnud, alustalade seadmine on palju aega ja mõtlemist nõudnud, aga tundub, et kõik on ületatav. Hetke progressi saab näha siit.

Kuna eri serverid (SMTP, POP3, IMAP) on üles ehitatud käsklus-vastus stiilis tekstipõhiste serveritena, lõin sarnaste situatsioonide jaoks serveri baasobjekti, mis sisaldab kõike toimimiseks vajalikku ning mida tuleb lihtsalt konkreetse serveri vajadustest lähtuvalt uute käsklustega täiendada. Näiteks POP3 server saab defineerida käskluse "STLS", aga SMTP hoopis "STARTTLS". Nimetasin selle serveri baasobjekti Request-Answer-Interface'ks ja selle source on nähtaval siin (faili lõpus on kommentaarina ka minimaalne implementatsioon). Täpsemat näiterakendust saab uurida failidest pop3.js ja smtp.js.

Hetke staatuseks on, et SMTP server suudab kirju vastu võtta ning töödelda täielikult läbi kirja päised. Järgmise sammuna on kavas siis kirja sisu töötlemine. Kuna kirja sisu võib olla väga suur (sõltuvalt manuste olemasolust ja suurusest) tuleb seda teha jupikaupa, mitte korraga ja see lisab ühe keerukusastme juurde. Kõige suurem probleem, Base64 dekodeerimine jupi kaupa on praeguseks lahendatud.

Süsteemist siis niipalju, et põhiosa on kirjutatud JavaScriptis (Node.JS), vähemalt üks moodul C's (node-iconv), kasutajate autoriseerimise andmed ja sessioonide andmed asuvad Redis andmebaasis, kirjade sisu ja meta-info läheb MongoDB andmebaasi ning manused MongoDB GridFS failihoidlasse. GridFS annab mugava võimaluse jagada faile suure hulga serverite vahel - selleks tuleb luua MongoDB klaster ning selles saab failide jupitamise ja kokkukogumisega GridFS juba kergelt hakkama. Samuti saab faili juurde salvestada suure hulga suvalist meta-infot. See on midagi, mida tavaline failisüsteem ei võimalda.

Kogu idee asjal on selles, et reeglina hoitakse e-posti originaalkujul (mime formaat) tekstifailides. Kas siis kogu postkast on ühes suures failis või siis on eri kirjad eraldi failides samas kataloogis. Selge on see, et taoline struktuur ei skaleeru ja on üldse ebaefektiivne (näiteks manused salvestatakse kettal Base64 kodeeringus, mis on kolmandiku võrra suurem, kui originaalmaht jne). Minu lahendus hoiaks süsteemselt siis kõiki andmeid neile sobivaimas hoidlas (Redis, MongoDB, GridFS) ning nende andmete põhjal pandaks alles vajadusel (klient üritab kirja alla laadida või tuleb see serverist välja saata) kokku standardne mime formaadis e-kirja dokument.

Selline struktuur annab ka meeletu eelise näiteks veebipõhise e-posti kliendi loomiseks - pole vaja keskenduda millelegi muule kui kasutajaliidesele, kogu info on programmi jaoks loetaval kujul andmebaasis juba olemas, midagi teisendada pole vaja.

laupäev, 11. september 2010

Base64 striimi parsimine

Kuna POP3 protokolli implementeerimine node.js platvormil õnnestus ootamatult hästi, proovisin katsetada ka maailmast kirjade vastuvõtmist SMTP protokolli abil. Ka see õnnestus üsnagi valutult (vähemalt see osa, mis protokolli puutub) - lisasin nimeserverisse MX kirje; kirjutasin lihtsa skripti mis kuulab porti 25 liiklust; skript tunnistas lihtsamaid vajaminevaid käske nagu HELO, DATA jne. Panin seejärel skripti tööle ja üritasin sellele Gmaili'ist kirja saata. Suureks üllatuseks töötas kõik väga kenasti.

Seega tuligi mõte teha valmis juba täismahuline e-maili server. Koos e-posti kontodega, kirjade saatmise ja vastuvõtmisega ning POP3 ja IMAP liidesega kliendi jaoks. Eks paista, kas sellest projektist ka midagi reaalselt välja tuleb, aga hetkel tundub et kõik vajalik on säärase lihtsama projekti jaoks olemas (st. et ei arvesta väga standardiväliste quirkide'ga jne).

Üheks esimeseks probleemiks tekkis kirja vastuvõtul manuste töötlemine. Manused on MIME formaadis kirjades lihtsustatult esitatud nii:

-----suvaline-unikaalne-ääre-tähis-ascii-tähdega+\r\n
Content-Type: text/plain; name="manus.zip"
Content-Disposition: attachment; filename="manus.zip"
Content-Transfer-Encoding: base64

pOb3RlczoNCg0KTmV2ZXIgaW1wbGVtZW50IGEgSlVNUCB0YWcgd2l0aG91dCBhIGNvcnJlc3Bv
bmRpbmcgQUQgdGFnLCBhcyB0aGlzIHdpbGwgcmVzdWx0IGluIG5vIGltcHJlc3Npb25zIG9yIGNs
aWNrcyBiZWluZyBjb3VudGVkIGZvciB0aGUgYXNzb2NpYXRlZCB0YW
[....]
-----suvaline-unikaalne-ääre-tähis-ascii-tähdega+\r\n

Kuna reeglina on manuseks mitte tekstifailid, vaid binaarsed failid, on kõikide baitide probleemivabaks transpordiks kasutusel base64 kodeering. Base64 kasutab vaid 64 võimalikku sümbolit, mis kõik on "prinditavad", mis tähendab et ühe "päris" baidi asemel (kaheksa bitti) saab ühe sümboliga edasi anda vaid 6 bitti (64 = 26).

Probleem tekib suurte manustega - juhul kui manus pole näiteks mitte 2 kB või 100 MB siis kogu selle faili korraga mällu lugemine ja seejärel base64->baitideks teisendamine kõlab üsna kahtlase väärtusega ideena. Seega tuleks teisendus teha juba faili sisselugemisel. Node.JS näiteks ei annagi kogu infot korraga edasi, vaid lõikab sissetuleva info umbkaudu 10 kB suurusteks juppideks.
socket.on("data", function(data){
    console.log(data); // data suurus ei ole suurem kui 10kB
});

Sellise umbmäärase pikkusega lõikude korral aga ei ole base64 dekodeerimine alati võimalik. Oluline on õigest sümbolist alustada ja õigest lõpetada, vastasel korral läheb bitijada nihkesse ja seetõttu tekivad ka baidid väga valed.

Kuidas seda olukorda siis lahendada, nii et saaks dekodeerida base64 andmestriimi?

Väga lihtsalt, kui algus on teada, saab sellest andmeid teisendada 4 baidiste lõikudena ning ülejääk jätta järgmise korrani.
var current = "";
socket.on("data", function(data){
    // NB! tegelikult on andmetüüp mitte string vaid Buffer!
    current += data;
    base64_decode(current.substr(0, current.length-current.length % 4));
    current = current.substr(-current + data.length % 4); // jääk
});

Juhul kui tegu oleks tavalise striimiga, siis piisaks ka määrangust, et striimi kodeeringuks on base64. MIME tüüpi kirja puhul aga on tegu ühe pika stringiga, kus kodeeringud vahelduvad vastavalt kontekstile. Seega tuleb ise jälgida mis andmetega parasjagu tegemist on.

teisipäev, 7. september 2010

POP3 server Node.JS jaoks

Implementeerisin POP3 protokolli toe Node.JS rakenduste jaoks - http://github.com/andris9/n3

Tegu ei ole küll POP3 serveriga selle traditsioonilises mõttes, st. et rakendus ei pea ilmitingimata serveerima klientidele (Outlook, Thunderbird, Mail App vms.) kindla kasutaja postkasti sisu, vaid mida iganes. Näiteks Twitteri sõnumeid või RSS uudiste kokkuvõtet viimasest kontrollist.

Testitud kliendid: Thunderbird 3, Mail App (Max OSX 10.5, 10.6), Outlook Express, Entourage, iPhone Mail. Tekkinud anomaaliad: Outlook Express näitas viimast HTML tag'i vigasena. Paragraafi lõpetamise asemel oli kirja lõpus </

reede, 3. september 2010

Ilmus mu artikkel ajakirjas A&A

Sain täna TTÜ kirjastusest kätte ajakirja A&A uue numbri (2/2010), kus on sees minu artikkel Google App Engine kasutamisest (kahjuks pole seda online kuskilt viidata). Kiirelt otsides leidsin terve rivi bakalaureusetöid ja vähemalt ühe magistritöö, mis allikana viitavad mõnele A&A artiklile, seega on tegu täiesti respektaabli avaldamiskohaga :)

Ainus prolbeem antud ajakirja juures on, et seda on praktiliselt võimatu kuskilt osta. Mina näiteks pole seda kuskil müügil näinud. Ausalt öeldes, ma ei teadnudki sellest ajakirjast suurt midagi enne kui mulle pakuti võimalust sinna oma artikkel kirjutada. Kuulnud olin, aga näinud mitte.

neljapäev, 2. september 2010

node-markdown

Vormistasin väga hea Showdown skripti Node.JS mooduliks nimega node-markdown. Tegu on siis Markdown süntaksi konverteerijaga, mis teeb Markdown tekstist HTML koodi. Installida saab mooduli npm kaudu käsuga npm install node-markdown.

Kasutamine on lihtne - tuleb laadida konverteerimisfunktsioon ja seda saabki seejärel otse kasutada.

var md = require("node-markdown").Markdown,
    html = md(markdown_text);


Täpsemalt võib lugeda mooduli lehelt.

Miks üldse Markdowni kasutada?

Tegu on "naturaalse" süntaksiga, mis on eeskuju võtnud tekstilise e-posti vormistamisest. Idee seisneb selles, et ka töötlemata tekst peaks olema silmale arusaadav ning ei paistaks otseselt kodeeringuna välja.

Näiteks

See on pealkiri
-------------------

Siin tuleb mummudega nimekiri:

  * Nimekirja element nr 1
  * Nimekirja element nr 2
  * Nimekirja element nr 3


Näeb ju arusaadav välja. Kodeerijast läbi lastes muutuks "See on pealkiri"
<H2> elemendiks, "Siia tuleb mummudega nimekiri" <P> paragrafiks ja tärnidega read <UL> nimekirjaks.

esmaspäev, 30. august 2010

Kuidas töötab AJAX?

Ma olen sellest kunagi aastaid tagasi juba juttu teinud, aga nüüd otsustasin põhjalikumalt asjad kirja panna ning tulemuseks on seletus, kuidas teha AJAX päringuid ILMA väliste teekideta (jQuery, Prototype vms.).

Muudest juttudest on veel pooleli sündmuste haldus (hetkel on kaetud ainult DOM level 0). Üritan selle teema millalgi uuesti käsile võtta.

kolmapäev, 25. august 2010

IFRAME skriptimine

Kirjutasin lühikese ülevaate IFRAME elemendi skriptimisest http://bit.ly/cdUwq6

IFRAME element on teinekord väga mugav variant näiteks kolmanda osapoole koodi isoleerimiseks muust lehest või siis brauseri puuduste ülesaamiseks. Reeglina kasutatakse seda WYSIWYG tekstitoimetite kuvamiseks (vanemad brauserid ei toeta selleks palju sobilikumat contentEditable parameetrit) ning ka "AJAX üleslaadimist" (mis tegelikult pole küll väga AJAX, aga see selleks). Sellisel juhul suunatakse faili üleslaadimise vorm IFRAME elemendi pihta, kui fail saab üles laetud käivitab freimi sisse tekkinud skript põhilehel teavituse üleslaadimise lõppemise kohta ja leht saab kasutajale vastava teate anda.

laupäev, 21. august 2010

Vana tnnistus

Lappasin enda vanu pabereid ja leidsin sellise tunnistuse (kliki pildil, et suuremalt näha):


Peale emotsionaalse väärtuse pole sellel paberil küll suurt midagi, aga vahva ikka.

See tuletas ka meelde, et peale keskkooli astusin ju Kõrgemasse Sõjakooli ja sain esimese 10 hulgas sisse. Täiendavaks sisseastumise tingimuseks oli aga, et tuleb minna koheselt ajateenistusse ning läbida seal vähemalt seersandikursused. Ajateenistusse läksin, aga kursustele mitte ja ainsaks saadud diplomiks ongi seesama 3 nädalase USA õppuse läbimise diplom (õppuste ajaks "ülendati" reamehest jaoülem-tõlgiks). Samal sügisel astusin TTÜ'sse ja militaarkarjääri peale pole enam mõelnud :)

reede, 20. august 2010

Node.JS v.0.2

Täna uuenes lisaks muudele asjadele ka Node.JS ja seda versioonile v.0.2. Tegu ei ole millegi põhimõttelise uuendusega vaid API fikseerimisega - kogu 0.2 perekonna juures ei tohiks API väga muutuda. Seega midagi sellist nagu promise objektide kaotamisega, kus suur hulk programme lakkasid töötamast, lähiajal enam juhtuda ei tohiks.

NETI.ee uuendus nüüd kõigile nähtav

Täna hommikul kell 05.50 uuenes NETI.ee kujundus. Senini sai uut kujundust näha aadressilt eval.neti.ee, kuid nüüd on see NETI pealehel kõigile avatud.

Lisaks värskendatud välimusele on lisandunud näiteks pisipildid otsingutulemuste juures, kataloogikirjete juurde tekkis info ettevõtte andmetega, sisse saab logida Facebook/Google kontoga jms. Ettevõtete andmete juures olev kaart muuseas ei ole staatiline pilt vaid hiirega täiesti vabalt lohistatav.

PS. Kuna Elion uuendas eile vanemate digibokside kasutajaliidest, siis tekkis ka sinna uus NETI.ee (Menüü -> Internet -> NETI). Eriti soovitan tähelepanu pöörata digiTv NETI esilehel olevale "Kasiino" lingile :)

neljapäev, 19. august 2010

Garage48 osalejate rollide jaotumine

Garage48 üritus on avalikustanud seekordsed osalejad, kuid tekkis huvi mis ulatuses eri rollidega inimesed üksteise suhtes jagunevad. Koostasin seega lihtsa graafiku, kus on rollide järgi inimesed tulpadesse pandud.

(Kliki pildidl, et näha suuremalt)

laupäev, 7. august 2010

Konkurss JS1K

Kes veel ei tea, siis parasjagu on käsil konkurss JS1K mis võtab vastu kuni 1 KB (1024 B) JavaScripti programme. Programmid ei tohi sõltuda ühestki välisest failist, ainsaks boonuseks on võistluslehel juba asetsev CANVAS element, mida saab mugavalt ära kasutada. Konkurss kestab kuni 10. septembrini.

Minu osalemistöö leiab siit.

pühapäev, 1. august 2010

node.ee

Paar päeva tagasi sain tööle tõenäoliselt esimese avaliku node.JS serveri Eestis - node.ee. Tegu pole ses suhtes küll 100% node.JS serveriga, kuna antud platvorm ei paista maailmale kätte otse, vaid läbi nginx proksi. See tähendab, et hetkel kõik aadressid kujul node.ee/node/* teenindatakse just node.JS poolt, muude eest aga hoolitseb juba nginx.

Sellega seoses olen juba paari asja katsetanud ja ühe huvitava projektina proovisin teha kliendipoolset andmebaasi liidest. See tähendab, et server näitab välja otsad andmebaasiga majandamiseks, kuid konkreetseid tegevusi teeb selle liidese abil juba kliendi pool ehk brauseris jooksev JavaScript. Tulemust saab näha siit. Kõik päringuandmed teisendatakse vahepeal lihtsalt objektidest JSON'i kujule ja vastupidi kuid edasi antakse kliendile/andmebaasile need juba originaalkujul.

See tähendab, et kui klient koostab mingisuguse päringu (objekti kujul, näiteks {eesnimi:"Peeter", perenimi:"Meeter"}), teisendatakse see JSON'iks, saadetakse serverile, server taastab JSON'ist uuesti objekti ja annab selle andmebaasile töötlemiseks. Täpselt sama, ainult et vastupidises järjekorras, toimub ka andmete saatmisega andmebaasist tagasi kliendile. Server otsib näitena toodud päringu alusel üles kõik kirjed, kus eesnimi on Peeter ja perekonnanimi Meeter ja tagastab need massiivina kliendile.

Juhul kui selline baas on kasutajapõhine ning erinevate kasutajate baasid on üksteisest eraldatud (näiteks serveri poolt lisatud prefiksid tabeli nimedes), peaks tulemus olema piisavalt stabiilne, et a) kliendil on väga suur vabadus käia andmetega ringi nagu soov on ning b) üks klient ei saa ligi teise kliendi andmetele. Suurepärane näiteks veebilehel olevatele widgetitele enda seadete salvestamiseks jms.

Huvitav teema indeed!

laupäev, 31. juuli 2010

Statistika: JavaScripti kasutamine Eesti veebilehtedes

Eelmistes postitustes tundsin huvi milliseid JavaScripti teeke Eesti veebilehed kõige parema meelega kasutavad. Esialgu uurisin käsitsi lappasin läbi 30 enam külastatavat veebilehte, kuid kuigi tulemused olid muidugi huvitavad, ei andnud see suuremat pilti Eesti veebilehtede kohta üldisemalt. Seega kirjutasin lihtsa roboti, mis võttis ette kõigi Eesti veebilehtede esilehed ja kontrollis nendes kasutatud teeke.

Tulemused pole 100% usaldusväärsed kuid piisavalt ülevaatlikud. Probleemid tekkisid peamiselt kahest asjast - kas ebaõnnestus serveriga ühendus (läks kirja, et sait ei kasuta JavaScripti) või oli lehel näiteks osa skripte pandud tegelikult HTML kommentaaride sisse. Robot otsis aga skripte regulaaravaldistega, ning kommentaare ei jälginud. Nii jäid esimesel juhul osad asjad märkimata, aga teisel juhul märgiti üle asju, mida tegelikult ei kasutatud. Samuti jäi teegi kasutamine märkimata, kui see laeti lehele dünaamiliselt (näiteks Google Ajax API goole.load abil). Kokkuvõttes aga ei tohiks need probleemid lõpptulemust väga mõjutada, kuna valim oli suhteliselt suur (~50 000 veebilehte).

NB! Kõik numbrid on graafikutel protsentides.

1. JavaScripti kasutamine üldisemalt

Selles testis vaatasin, kas sait a) ei kasutanaud üldse JavaScripti, b) kasutas mõnda tuntud teeki või c) ei kasutanud ühtegi teeki, vaid midagi muud (siia alla lähevad ka Google Analytics jms loendurprogrammid).



2. Teekide omavaheline osakaal

Järgmisena vaatasin, milline on teekide kasutamise omavaheline osakaal - milliseid teeke kasutatakse rohkem, milliseid vähem. Teeke kasutavaid saite oli üldse kokku 19%.



3. Teekide versioonid

Viimasena uurisin täpsemalt teegi siseselt, millised konkreetsed versioonid kindlast teegist kasutusel on ja millised on nende omavahelised suhted.

3.1 jQuery

jQuery on enimkasutatud teek ja seega on ka erinevaid versioone kasutusel seinast seina.



3.2 MooTools



3.3 Prototype



3.4 YAHOO



3.5 Ext-JS



3.6 DOJO



Nagu näha on olukord üsna kirju. Teekide hulgas troonib suurelt jQuery, mida kasutab sama palju veebilehti, kui kõiki teisi kokku. Samas aga on veel väga palju veebilehti, mis teeke ei kasuta. Siinkohal tundub olevat mõistlik kasutada Google AJAX API majutust teekide serveerimiseks - juhul kui kasvõi väike osa veebisaitidest seda kasutavad, muutub üsna tõenäoliseks et kui kasutaja esimest korda sinu saidile satub, on tal saidil kasutatav teek juba brauseri poolt puhverdatud ja seega on säästetud ekstra 100kB lehe laadimisaega ja mis veel tähtsam - 100kB võrguliiklust. Üks klient ei tähenda suurt midagi, aga kümne tuhande kliendi puhul on see juba terve gigabait.

kolmapäev, 28. juuli 2010

JavaScripti teekide kasutamine Eesti lehtedel vol 2

Panin eile käima roboti, mis kammib läbi Eesti veebilehtede esikülgi (kasutan NETI serverite nimekirja) ja lootsin, et täna õhtuks on tulemused käes. Paraku on robot väga aeglane ja praeguseks on läbi käidud vaid pool 50 000 aadressist, seega mingeid lõplikke järeldusi veel teha ei saa. Kui robot on oma töö ükskord lõpetanud, siis panen tulemused kindlasti üles.

Küll aga võib niipalju öelda, et praeguseks kogutud andmete põhjal on juba selgunud paar huvitavat asja. Esiteks fakt mida võis juba varem eeldada - suurem osa veebilehtedest ei kasutagi (vähemalt populaarsemaid) teeke. Teiseks veidi üllatuslikuks momendiks on enamkasutatavate teekide järjekord. Esimene on oodatult suure ülekaaluga jQuery, kuid järgmised kohad läksid minu ennustustest veidi lahku. Aga sellest kõigest siis, kui lõplikud tulemused käes.

Seega kuigi uuringutulemusi veel päris kätte täna ei saanud, õnnestus mul lõpuks ometi saada üles Node.JS server aadressile node.ee. Siiani olen katsetanud localhostis ning veidi ka Amazon EC2 virtuaalmasinas, kuid Amazon on lihtsalt katsetamiseks kallis (ligi 1000 krooni kuus kui server töötab 24/7), localhosti aga väljastpoolt keegi ligi ei pääse. Täna siis lõpuks tellisin Zone virtuaalse privaatserveri ja seadistasin selle jooksutama Nginx + Node.JS + mongoDB.

Nginx'i installisin Node.JS ette puhtpraktilistel kaalutlustel - et saaks kasutada serverit ka tavalise failiserverina (näiteks kasvõi HTML ja pildifailide serveerimiseks), Node.JS puhul on see veidi ebamugavam. Pealegi saan nii Node.JS'i taustal restartida, ilma et server sel ajal eetrist ära kaoks. Failid jõuavad ikka kohale, lihtsalt Node.JS skriptide poole pöördudes tuleb sel ajal Error 503 Bad Gateway.

MongoDB valisin andmebaasiks seetõttu, et mul õnnestus see ära installida. Tahtsin tegelikult kasutada CouchDB'd, kuid miskipärast kukkus paketihaldur selle installi peale nii pröökama, et kogu paketimajandus läks veidi nihu (ei saanud enam midagi peale ega maha installida) ja pidin Google abil veidi nikerdama enne kui kõik korda sain. CouchDB aga ei hakanudki tööle ja võtsin selle asemel siis eelpoolmainitud MongoDB.

kolmapäev, 21. juuli 2010

Flash upload Linuxi all nüüd korras

Kes on Linuxi kasutaja, peaks olema kokku puutunud probleemiga kus veebisaitidel failide üleslaadimisel brauser suuremate failide korral hangub - olukord tekib juhul kui üleslaadimiseks kasutatakse Flashi komponenti. Flash võimaldab küll tekitada kena cross-browser üleslaadimise progressiriba, kuid Linuxis on Flash selle koha pealt senini bugine olnud - nimelt ei edastanud Flash skriptile onprogress sündmust ning teatas andmed alles üleslaadimise lõpus, senini aga oli brauser lihtsalt jäätunud olekus. Väiksemate failide puhul polnud see probleemiks, kuna need läksid nii kähku üles, et väike kangestumine ei seganud, aga suuremate failide korral on lood muidugi teised.

Igatahes, juba kuu aega on väljas Flashi versioon 10.1, mis selle probleemi lahendab. Kellel ikka veel brauser failide üleslaadimisel pangestub, kontrollige Flashi versiooni ja vajadusel uuendage.

esmaspäev, 19. juuli 2010

Milline on kasutatavaim JavaScript library Eesti lehtedel?

Selleks on jQuery. Vaatasin läbi 30 külastatavamat Eesti veebilehte ja leidsin, et jQuery on kasutusel neist 19 lehel (63%), Prototype 2 lehel (7%) ja EXT-JS 1 lehel (3%). Omaloomingut või netist copy-paste teel omandatud menüüskripte jms. kasutas 8 lehte (27%). Mõnevõrra üllatuslikult polnud esimese 30 hulgas ühtegi YUI ega DOJO põhjal tehtud lehte, võibolla see muutuks, kui vaadata vähe suuremat valimit (kuigi ma isiklikult arvan, et suurem valim suurendaks ainult jQuery osakaalu ning muud ei midagi)


esmaspäev, 12. juuli 2010

HTML Notifications API

HTML5 Notifications API võimaldab veebilehtedel brauseriaknast "välja murda" ja kuvada erinevaid teavitusi veebilehe asukohast sõltumatult töölaual sarnasena näiteks MSN Messengeri sisselogimisteavitustega. Pikemalt kirjutasin sellest siin.

neljapäev, 8. juuli 2010

JavaScripti moodulite vaheline suhtlus JavaScriptis

Tänapäevased keerulisemad veebilehed koosnevad tõenäoliselt mitmetest eri komponentidest, mida suuremal või vähemal määral manatakse lehele JavaScripti abil. Nendeks komponentideks võivad muu hulgas olla näiteks järgmised elemendid - sisselogimise vorm mis avaneb hiirekliki peale (algselt on näha ainult tekst "Logi sisse"); bännerite kuvamine lehel; pisike interaktiivne kast ilmateateinfoga (saab valida piirkonda, aega jne); lightbox efekt piltide kuvamiseks; valuuta konverteerimise vidin jms.

Kuigi eemalt vaadates pole neil komponentidel palju ühist, võib siiski juhtuda olukordi kus eri komponendid vajavad võimalust üksteisega suhtlemiseks, et selle abil enese tööd parendada. Näiteks kui kasutaja valib ilmateate kastis mõne muu asukoha, kui algselt määratud, tahaks bännerite kuvaja võibolla näidata kasutajale ainult neid bännereid, mis on valitud piirkonnaga seotud. Või kui kasutaja passiivsuse tõttu keskkonnast automaatselt välja logitakse, ilma uut lehenäitamist tegemata, tahaksid tõenäoliselt kõik komponendid sellest teada saada, nii et suudaksid endid sobivalt ümber häälestada. Rääkimata siis veel sellistest juhtudest, kus seosed on palju otsesemad - kui kasutaja end välja logib/välja logitakse tuleks taastada vahepeal eemaldatud sisselogimise link.

Tõenäoliselt tegelevad kõikide nende operatsioonidega erinevad moodulid. Sisselogimisega üks, bännerite kuvamisega teine ja ilmateatega hoopis kolmas. Seega tuleb välja mõelda mingi mõistlik viis, kuidas need eraldiseisvad moodulid üksteisega suheldud saaks.

Naabrite eiramine

Kõige triviaalsem on lihtsalt teist moodulit eirata ja selle tegevused ise läbi viia. Näiteks kui ilmateate kastis on element <a id="tervitus_tekst"> mis sisseloginud kasutajale teatab "Tere [nimi]!" ning väljaloginud kasutajale "Tere kasutaja!", siis kutsudes esile väljalogimisprotsessi klikkides lehel lingil "Logi välja", ei anna kasutaja haldamise moodul sellest väljalogimisest ilmateate moodulile teada, vaid lihtsalt kirjutab selles oleva kasutajainfoga seotud elemendi üle.

Kasutaja logib välja ->
kasutajate moodul:
$("tervitus_tekst").innerHTML="Tere kasutaja!"

Antud lähenemine on võibolla lihtsustamise mõttes mõistlik väga väiksemahuliste, kuid kindlasti mitte keerulisemate elementide korral. Esiteks võib selline käitumine teise mooduli katki teha - too eeldab, et lehel on üks asi, tegelikult on aga hoopis midagi muud. Samuti võib kõik katki minna, kui lehel polegi ilmateate moodulit. Pealegi lõhutakse nii päris korralikult modulaarsust, kus iga komponent peaks olema eraldiseisev tervik ning pärsitakse ka skaleeruvust programmikoodi mõistes - taoline tegevus on alati erand ning kui erandid kipuvad kuhjuma, on oodata suuri probleeme.

Tagasikutse funktsioonide kasutamine

Keerulisem viis oleks seada üles tagasikutsefunktsioonide registreerimise süsteem. Sellisel juhul ilmateate moodul ütleks lehe laadimisel, et erinevate sisselogimisega seotud sündmuste korral tuleks käivitada parameetrina antud tagasikutsefunktsioon. Juhul kui nüüd kasutaja välja logib, võtab kasutajate haldamise moodul ette kõik sedasi registreeritud tagasikutsefunktsioonid ja paneb need ükshaaval käima. Nii saab ilmateate moodul teada, et kasutaja on välja loginud ja asendab ise tervituse teksti vastavaga.

Tagasikutse funktsiooni registreerimine ->
ilmateate moodul:
kasutajate_moodul.notify_logout_callback = function(){
    ilmateate_moodul.kasutaja_logis_välja();
};

Kasutaja logis välja ->
kasutajate moodul:
kasutajate_moodul.notify_logout_callback();

Antud lähenemine on juba tunduvalt parem kui eelmisena näidatud variant - seekord on moodulid üksteisest juba üsna eraldatud, kumbki teise tööpõllule ei roni. Samuti ei teki ka probleeme, kui ilmateate moodul lehelt üldse puudu on. Küll aga tekivad probleemid siis, kui lehel puudub sisselogimise moodul. Moodulid on endiselt üksteisest kaudselt sõltuvad, kuna vähemalt üks eeldab teise olemasolu (ilmateate moodul eeldab kasutajate mooduli olemasolu).

Kohandatud sündmuste kasutamine

Nõrgalt seotud elementide korral tasuks kõige rohke kaaluda just kohandatud sündmuste kasutusele võttu. Süsteem oleks sarnane nagu tavaliste tagasikutse funktsioonide kasutamise korralgi - ilmateate moodul registreerib tagasikutse funktsiooni, mis käivitatakse juhul kui kasutaja haldamise moodul on kasutaja välja loginud, kuid seekord ei registreeritaks seda sündmust mitte otse kasutajate haldaja pihta, vaid hoopis kohandatud sündmusena mõne suvalise DOM elemendi, näiteks document pihta.

Kui nüüd kasutaja välja logib, annab kasutajate haldamise moodul sellest teada, aga mitte otse ilmateate moodulile, vaid hoopis sellele sõnumikandjana kasutatavale elemendile. Too võtab ükshaaval ette kõik huvitatud ja käivitab nende tagasikutse funktsioonid.

Kohandatud sündmuse registreerimine ->
ilmateate moodul:
document.observe("kasutaja:logis-välja", function(event){
    ilmateate_moodul.kasutaja_logis_välja();
});

Kasutaja logis välja ->
kasutajate moodul:
document.fire("kasutaja:logis-välja");

Viimane lähenemine on käsitletutest kõige turvalisem - kõik moodulid on üksteisest täielikult eraldatud. Kuna üksteisega suhtlemiseks kasutatakse vahendajat, ei puutu ükski moodul otseselt teisega kunagi kokku. Nii võib suvaline moodul heita üles teate, et nüüd midagi juhtus ja juhul kui mõnda teist moodulit see huvitama peaks, saab ta selle teate kätte. Kui aga selliseid huvitatuid pole, ei lähe ka midagi katki. Samuti kui mõni moodul määrab ära teda huvitava sündmuse, siis ei juhtu ka midagi sellest, kui ühtegi taolist sündmust esilekutsuvat moodulit lehel polegi.

Täiendavaks plussiks kohandatud sündmuste kasutamise korral oleks veel "tasuta kaasa tulevad" sündmuste haldamise võimalused - iga käivitatud tagasikutse funktsioon saab standardsel viisil edasist sündmuse täitmist peatada (event.stopPropagation), saab tegevust ka maha registreerida, nii et seda järgmiste sündmuste juures ei kasutata (document.stopObserving) jne.

Vaata ka seda: Ajaxian, How Custom Events Will Save Us All

NB! Näidetes on kasutatud Prototype käsklusi, sama on võimalik saavutada ka kõigi teiste sarnaste teekide abil.

laupäev, 3. juuli 2010

node.js jooksutamine Amazon EC2 virtuaalserveris

Kirjutasin oma wikisse juhendi node.js kasutamiseks Amazon EC2 virtuaalmasinas. Juhendit saab lugeda siit.

Lühidalt tuleb teha järgnevat
- Luua EC2 virtuaalserver
- installeerida selles C kompilaator ja Git versioonihaldus
- Laadida Git abil node.js lähtefailid serverisse
- kompileerida node.js lähtekoodidest lõplik rakendus
- käivitada .js fail, mis defineerib lihtsa veebiserveri

Täpsemalt saab siis lugeda eespool viidatud aadressilt.

kolmapäev, 26. mai 2010

MediaWiki ja jStorage

Avastasin, et MediaWiki projekti ühe laienduse juures on kasutusel minu loodud jStorage (Y) MediaWiki subersionis on küll üks veidi vanem versioon kui hetkel aktuaalne, kuid kui kiirelt üle vaatasin, milleks nad seda kasutavad, siis polegi tõenäoliselt uuemat versiooni vaja. Viimaseks suuremaks uuenduseks oli nimelt XML elementide salvestamise tugi.

XML elemente on võimalik saada näiteks Ajax'i kaudu serverist laadides - XMLHttpRequest nimetus peaks juba ütlema, mille jaoks see algselt mõeldud oli. XML objektid aga ei ole nagu tavalised JavaScripti objektid, vaid midagi DOM objektide laadset (tegelikult ongi mõlema puhul tegu enamvähem sama asjaga). Seega XML objekti salvestades ei saa seda lihtsalt JSON stringiks muuta ja hiljem tagasi - oluline on säilitada mitte ainult andmed, vaid ka vajalikud meetodid ja omadused XML puu töötlemiseks (firstChild, childNodes, getElementsByTagName jne), mida tava-objektidel pole.

Õnneks on võimalik XML objekte suhteliselt kerge teisendada objekti kujult tekstilisele ja vastupidi, nii lisasingi jStorage sisse väikese lisamooduli nimega XMLService, mis siis vajadusel need teisendused ära teeb (salvestamisel tekstiks ja laadimisel uuesti XML objektiks).

Kusjuures kuna XML ei vaja teisendamiseks suhteliselt mahukat teeki, nagu näiteks JSON, oleks võibolla mõnikord täiesti mõistlik just seda andmeedastusvormi kasutada. Kuigi arvestades keerukuse taset JSON'iga võrreldes, siis ma tõenäoliselt lähen siiski alati JSON'i teed, täiendava teegi vajalikkusest hoolimata. Kogu see XML puus ringi "jalutamine" paneb paremal juhul lihtsalt pea valutama, aga halvemal juhul jäävadki õiged asjad leidmata.

neljapäev, 13. mai 2010

PubSubHubbub

Kirjutasin oma wikisse sissejuhatava artikli kuidas kasutada PubSubHubbub protokolli blogipostituste korjamiseks blogidest reaalajas.

reede, 30. aprill 2010

JS: "this" ja sulundid 2 (HTML inline sündmused)

JavaScript võimaldab kasutada mitmesuguseid sündmuseid - näiteks on võimalik käivitada programmi funktsioon kui kasutaja klikib lehel mingil kindlal elemendil või kui hiir liigub üle selle elemendi. Kuigi kõige lihtsam on seda teha otse HTML koodi sees, nn. inline (<div onclick="alert('klikk!')">kliki mind</div>), siis soovitatav kasutusjuht oleks siiski defineerida sündmus HTML koodist eraldi, eristada programmiline osa dokumendist (element.addEventListener("click", callback, false);). Mida aga teha siis, kui meil on tarvis siiski kasutada inline sündmust, kuid tegu pole mitte "päris" sündmusega, vaid hoopis kohandatud, ise defineeritud ja väljakutsutava sündmusega?

Ütleme et meil on olukord, kus tahame HTML elementidele lisada inline kohandatud sündmuseid, näiteks pookida elemendile külge väljamõeldud sündmust nimega kell12, mis rakendub juhul kui kell saab 24:00 (eeldusel et sama lehekülg on brauseris sel ajal lahti)

<span onkell12="alert(this.innerHTML)" style="display:none">Käes on südaöö!</span>

Sündmuse parameetri prefiksiks on seatud sarnaselt teistele inline sündmustele (onclick, onmouseover jne) "on", kuid see ei ole tehniline vaid põhimõtteline seadistus, sama hästi võiks see olla lihtsalt "kell12" või "kellasyndmus". Näite järgi peaks kell 24:00 tulema kasutajale teade, mille sisuks on konkreetse SPAN elemendi sisu (innerHTML, "Käes on kesköö!").

Kuidas siis sellist sündmust tööle panna?

Esiteks tuleb elemendist sündmuse  sisu kuidagi kätte saada. Seda saab teha getAttribute meetodiga.

var syndmus = span_element.getAttribute("onkell12","");

Kui sündmuse sisu on käes, tuleks see käivitada. Aga kuna tegu pole mitte funktsiooni-, vaid tekstilise väärtusega, tuleb saadud tekst implementaatoris programmiks muuta. Selle jaoks kasutatakse reeglina käsklust eval. Antud käsklus võtab parameetrina saadud teksti ning muudab selle JavaScripti koodiks. Näiteks nii - eval("alert(1)");

Siinjuhul eval siiski ei kõlba, kuna kuidagi on vaja siduda this väärtus käivitatavas tekstis õige SPAN elemendiga, eval puhul oleks this väärtuseks aga window. Eelmisest postitusest tutvustatud bind meetodit vms. ei saa siin otse kasutada, kuna eval käivitab, mitte ei tagasta funktsiooni. Siin tuleb appi konstruktor Function - nimelt Function konstruktor käitub eval käsule sarnaselt, teisendades teksti programmiks, kuid käivitamise asemel tagastab funktsiooniobjekti ning sellele saab juba rakendada this väärtuse sidumiseks meetodit call.

Function(syndmus).call(span_element);

Antud konstruktsioon kõigepealt loob tekstilisest lähtekoodist (muutuja syndmus sisu) funktsiooniobjekti; ning seejrel funktsiooni meetod call seob funktsioonis this väärtuseks HTML elemendi span_element ning käivitab niiviisi saadud funktsiooni.

Kokkuvõttes oleks skript siis järgmine. Seadistatakse timeout käsklus, mis käivitatakse täpselt kell 24:00. Lehelt otsitakse käivituse käigus üles kõik SPAN elemendid ning üritatakse kasutada nende elementide onkell12 atribuute.

window.setTimeout(function(){
    var spans = document.body.getElementsByTagName("span");
    for(var i=0, len = spans.length; i<len; i++){
         Function(
           spans[i].getAttribute("onkell12","")
         ).call(spans[i]);
    }
},new Date(+new Date()+86400000).setHours(0,0,0,0)-new Date());
Järgmises postituses üritan millalgi kirjutada XHMTL elementide laiendamisest oma nimeruumi elementidega (näiteks nagu FaceBook'i XFBML elemendid ) - seal muutub oluliseks ka siin postituses kirjeldatud inline sündmuste süsteem, kuna nii on mugav võõraid elemente enda lehele külge pookida.

neljapäev, 29. aprill 2010

JS: Kuidas hoida "this" sulundites alles

Üheks tuntud probleemiks JavaScriptis on muutuja this sidumine funktsioonis a) aktiivse objekti meetodi juurde või b) kõigil teistel juhtudel globaalse objekti window juurde. Sulundite korral kui funktsiooni deklaratsioon asub objekti meetodi sees ei liigu this sulundfunktsiooni edasi ning see tekitab tihti segadust.

var omadus = 321;
var objekt = {
    omadus: 123,
    meetod: function(){
        alert(this.omadus); // 123
        window.setTimeout(function(){
            alert(this.omadus); // 321
        },0);
    }
}
objekt.meetod();

Nagu näites näha asub objekti meetodi meetod sees kaks alert käsklust, mis mõlemad väljastavad this.omadus sisu. Esimene alert käsklus asub otse objekti meetodis, teine aga selle meetodi skoopi jäävas sulundfunktsioonis. Mõlemad kasutavad küll sama nimetust muutuja esitamiseks (this.omadus), kuid ometigi on tulemus täiesti erinev - esimesel juhul korrektselt 123, teisel juhul aga hoopis 321.

Siit selgubki, et this on seatud korrektselt ainult vahetult meetodi skoobis, kuid mitte sulundina selle sees. Viimasel juhul on ei viita this enam mitte aktiivsele objektile, milleks peaks olema näite juures objekt, vaid hoopis globaalsele objektile window. Kuna aga kõik "globaalsed muutujad," (st. ei kuulu kindlasse skoopi) sh. omadus on tegelikult window objekti omadusteks, siis this.omadus anonüümses sulundfunktsioonis teisendub window.omadus ja sellest juba lihtsalt omadus muutujaks, mille väärtuseks ongi näites 321.

Juhul kui kasutusel on Prototype raamistik saab olukorra mugavaks lahendamiseks kasutada raamistiku poolt pakutavat vahekihti loovat meetodit bind, mis seob this väärtuse funktsioonis vajaliku objektiga.

var objekt = {
    omadus: 123,
    meetod: function(){
        alert(this.omadus); // 123
        window.setTimeout((function(){
            alert(this.omadus); // 123
        }).bind(this),0);
    }
}
objekt.meetod();

See meetod võtab funktsiooniobjekti kujul (function(){}) ning rakendab sellel Prototype lisatud funktsiooni meetodit bind mis omakorda tagastab uue funktsiooniobjekti, mis antakse setTimeout parameetriks.

Kuidas bind töötab?

Juhul kui Prototype puudub võib sellise funktsiooni meetodi lihtsalt ise teha.

Function.prototype.bind = function(){
    var that = this,
        params = Array.prototype.slice.call(arguments),
        obj = params.shift();
    return function(){
        that.apply(obj, params);
    }
}

Näites on defineeritud kolm muutujat:
that on viide funktsiooniobjekti juurde, params on funktsiooni kõiki parameetreid sisaldav arguments objekt teisendatuna massiiviks (arguments on massiivilaadne objekt, aga mitte massiiv) ning obj, milleks saab params massiivi esimene objekt.

Funktsioon tagastab uue funktsiooniobjekti, mille käivitamisel käivitatakse esialgne funktsioon, aga juba korrektselt seatud this väärtusega.

Arvestada tuleb ainult et native objektide prototype muutmine pole reeglina kunagi hea mõte ning võibolla tasuks funktsiooni meetod defineerida hoopis eraldisesiva funktsioonina.

neljapäev, 15. aprill 2010

GitHub kasutamise õpetus

Kuna viimasel ajal on vaja olnud mitmete projektide jaoks kasutada GitHub versioonihaldust, siis kirjutasin selle jaoks vastava juhendi http://tahvel.info/git_github

kolmapäev, 24. märts 2010

Sattusin maksutõusu ohvriks

Tellisin eBayst Roomba varuosi $69 eest ning ebameeldiva üllatusena lisandus sellele summale veel 200 krooni impordimaksu ning 100 krooni vormistamise teenustasu. Samas kui eelmine aasta tellisin korduvalt $100 hinnaklassis tooteid ning kunagi ei pidanud mingeid makse maksma.


Paki hind oli 69 dollarit millele lisandus 21 dollarit postikulu. Mina aga maksin makse mitte kauba hinna järgi 69 dollarit, vaid 69+21 = 90 dollari (1000 krooni) eest. Nimelt uue korra järgi arvutatakse postikulu samuti toote hinna sisse.

Ma saan sellest aru, et riik langetab maksuvabu määri, kuid üks asi millele ma kuidagi pihta ei saa, on transpordikulu lisamine toote hinnale ja minu arust on see täiesti ajuvaba. Kuidas on postikulu seotud impordiga? Kasvõi selles mõttes, et suur osa "makstud postiteenusest" sooritatakse väljaspool Euroopa Liitu, kuna postikulu sisaldab muu hulgas nii USA sisest transporti kui ka Atlandi ületamist. Kuidas saab Eesti Vabariik küsida maksu Atlandi ületamise teenustasu pealt?

---

Selle aasta maksumäärad on siis järgmised - alla 22 euro (sh. postikulu) on maksuvaba, alla 150 euro (sh. postikulu) tuleb tasuda käibemaks (impordimaks) ning paberite vormistamise eest Eesti Postile teenustasu. Üle 150 eurose paki eest tuleb tasuta nii käibemaks kui tollimaks.

Alternatiivne võimalus on muidugi tellida asju ainult Hiinast (sh. Hong Kong jms.), kuna seal kiputakse deklareerima kõike väljuvat kui "Gift, $10". Kusjuures seda isegi juhul mil kauba reaalne väärtus on väiksem kui $10. Kui tegu on väikese asjaga, millele suurt postikulu ei lisandu, saab mahtuda lubatud 22 euro piiridesse ja makse seega maksma ei pea.

Selline oli siis minu tänane maksude-optimiseerimise rubriik.

reede, 19. märts 2010

jStorage plugin JavaScriptis andmete lokaalseks salvestamiseks

Hakkasin hiljuti oma DOMCached teeki ümber kirjutama ning kuna see muutus suhteliselt palju ning ei ole suures ulatuses tagasiulatuv, siis vormindasin selle ümber täiesti uueks projektiks, mille nimeks sa jStorage www.jstorage.info.

Plugin vajab töötamiseks kas Prototype või jQuery teeki ning suudab salvestada infot brauseris.


neljapäev, 18. märts 2010

Kuidas võtta vastu Fortumo SMS sõnumeid Google App Engine rakenduses?

Fortumo pakub väga mugavat võimalust luua erinevaid SMS põhiseid tasulisi rakendusi - selle jaoks on olemas Fortumo Dünaamiline SMS-teenus. Paraku on kõik juhendid Fortumo lehel ainult PHP jaoks, seega Google App Engines jooksva Pythoni jaoks on vaja ise leiutada.

Probleemi lahendamiseks lõin lihtsa näidisrakenduse, mis oskab Fortumo poolt saadetud SMS sõnumite infot vastu võtta ning andmebaasi salvestada. Rakendus võtab sõnumi vastu ning salvestab selle andmebaasi. Rakenduse esileht kuvab andmebaasist kuni 100 viimast sõnumit uuemad eespool. Kõike keerulisemat saab juba sellele põhjale ise ehitada.

Näidisrakenduse info ning viited lähtekoodi juurde leiab siit.

teisipäev, 16. märts 2010

Kuidas vabaneda lärakast Äripäeva bännerist?

Äripäev on tore leht ja seda tasub kindlasti vaadata. Samas püsilugejal (eriti widescreen ekraaniga, kus ekraan on küll lai, aga mitte eriti kõrge), kes on muidu tubli ja AdBlock'i ei kasuta, on kindlasti too Äripäeva tobe lärakas päisebänner pinnuks silmas. Iphone brauseriga vaadates scrollib bänner muu lehega kaasa, minnes artikli edenedes ilusti serva taha, aga tavabrauseriga on bänner fixed positsiooniga, mis teeb ta üsna ebameeldivaks kui mitte öelda lausa häirivaks.

Õnneks saab selle läraka suhteliselt kerge vaevaga eemaldatud. Selle jaoks pole vaja teha muud kui lisada brauseri URL reale järgmine rida ja vajutada Enter klahvi.

javascript:void($("#siteHeaderContainer").remove())

Peale seda peaks bänner imeväel ekraanilt kaduma. Veel mugavam on teha uus järjehoidja brauseri järjehoidjaribale, niiöelda bookmarklet'ina. Selle jaoks tee sellel lingil parem hiireklõps, vali "Lisa järjehoidja" ning asukohaks määra järjehoidjariba. Kui nüüd Äripäeva vaadata ja klõpsata järjehoidjaribal uut järjehoidjat, kaob bänner ära.

Aga miks see järjehoidja või URL niiviisi töötab?

Brauseri URL riba on seotud hetkel brauseri poolt avatud dokumendiga - tegu oleks nagu millegi lingi laadsega, kus pead klikkimise asemel lingi aadressi ise kirjutama. Linke aga on mitmesuguseid - on http protokolli suunas minevaid linke, on https protokolli suunas minevaid, on ka ftp ja maili saatmiseks mailto lingid, kuid lisaks on veel olemas javascript link, mille järel oleva "aadress" käivitatakse tavalise JavaScriptina nagu see käivituks eesoleva lehe enda seest.

Kuna URL riba on mõeldud siiski lehelt minema navigeerimiseks, üritab brauser seda teha ka javascript: URL'i puhul. Juhul kui käivitatud JavaScript tagastab mingi väärtuse (kusjuures false on ka väärtus!!!), üritab JavaScript seda väärtust lehel "avada". Antud väärtus teisendatakse tekstikujule ning seatakse eesoleva brauseri sisuks (valge leht, kus on rida teksti). Kirjuta URL-i ribale näiteks

javascript:1+1

ja vajuta Enterit. Tulemuseks on valge taustaga leht, kus on kiri 2.

Bänneri eemaldamisel selline tulemus muidugi ei rahulda - kui bänner on ära koristatud, peaks õige leht siiski ette jääma. Aga kuidas mitte tagastada väärtust, kui isegi false on väärtus? Point on selles, et JavaScriptis on olemas ka mitte-väärtus, milleks on undefined. Kui tagastadagi undefined, siis ei oska brauser kuhugi minna ning olemasolev leht jääbki ette. Seega tuleks JavaScript käsk lõpetada undefined väärtuse tagastamisega.

undefined väärtust saab esile manada mitmeti. Kõige lihtsam oleks see kirjutada lihtsalt avaldise lõppu - tagastusväärtuseks on viimase käsu tulemus. Kuna viimane käsk on undefined, siis on ka selle tagastus undefined. Teine võimalus on sisestada käivitatav kood mõne funktsiooni parameetriks, millel enda tagastusväärtus puudub. Eriti hästi sobib selle jaoks void (pole tehniliselt võttes küll mitte funktsioon, vaid operaator), kuna see ei tee üldse mitte midagi.

javascript:1+1;undefined
javascript:void(1+1)

Mõlemal juhul käivitatakse avaldis 1+1, kuid lehelt ära ei navigeerita.

Järgmiseks tuleb tegeleda bänneri eemaldamisega. Kuna Äripäev kasutab jQuery teeki, saab bänneri DOM elemendi mugavalt $ funktsiooniga kätte. Vastasel korral tuleks kasutada pikemat ja kohmakamat document.getElementById("id") vormi (vs. lühike $("#id"))). Kui viide bänneri DOM elemendi juurde on olemas, tuleb see DOM puust eemaldada. "Normaalsetes" oludes tuleks seda teha käsuga dom_element.parentNode.removeChild(dom_element), aga jQuery pakub õnneks tunduvalt mugavamat ja kiiremat varianti - DOM elemendi meetodit remove.

Kui nüüd Äripäeva HTML lähtekoodist järgi vaadata, on bänneri elemendi id väärtuseks siteHeaderContainer. Pannes selle kokku saadud teadmistega, tuleb välja, et bänneri kustutamiseks tuleb käivitada käsklus

$("#siteHeaderContainer").remove()

JavaScripti käivitamiseks paneme saadud käskluse void parameetriks ja moodustame sellest javascript: URL'i ning saamegi kokku vajaliku rea:

javascript:void($("#siteHeaderContainer").remove())

Nii, et ei midagi keerulist!

kolmapäev, 3. märts 2010

Google App Engine kasutamise juhend

Kirjutasin aine Veebiprogrammeerimine raames Google App Engine kasutamise juhendi, mida saab lugeda siit (täismahus PDF kujul saab otse laadida siit).

Google App Engine'ga tegelemise suureks plussiks on lihtne tõik, et alustada saab selles tasuta. Ja tegu pole mingi trial'iga, mis mingi aja pärast otsa saab, vaid kogu teenus ongi määratud limiitide ületamiseni tasuta.

Limiidid aga on loodud USA, mitte Eesti turgu silmas pidades - tõenäoliselt saaks suurema osa Eesti jaoks loodud asju majutada Google App Engine platvormil niimodi ära, et ei peaks kunagi sentigi majutuse eest maksma. Ainus reaalne tasuta kasutamise piirang, millest võib kergelt üle minna, on kuni 1GB kettapinda, aga ka sellele pinnale mahutab juba nii mõndagi.

Suurimaks miinuseks on uue programmeerimiskeele (Python) selgeksõppimine, kuna PHP, mida enamus veebitegijaid kasutab seal ei tööta. Samuti on pakutav andmebaas suhteliselt piirav võrrelduna näiteks MySQL'iga, aga see tuleb erinevatest eesmärkides. Google App Engine andmebaasi esimene ja kõige tähtsam ülesanne on olla ligipääsetav võimalikult paljudele kasutajatele võimalikult rohke kasutamise korral. Ehk et lugemine on ka väga suure koormuse all kiire, kirjutamine aga on aeglane ka juhul kui on ainult üks kasutaja.

Seega ajalehe veebi jaoks, kus andmebaasi lisatakse päevas mingi suhteliselt väike arv uusi artikleid, aga kust päritakse neid artikleid sadu tuhandeid kui mitte miljoneid kordi päevas, sobib antud platvorm ideaalselt. Andmebaasiteenuseks kus on vaja muuta korraga suures koguses kirjeid ning teha nende kirjete hulgas väga keerulisi otsinguid, see jälle niiväga ei sobi.

esmaspäev, 1. märts 2010

Tegin endale WIKI

Tegin endale WIKI programmeerimisalase info jaoks aadressile www.tahvel.info. Kirjutan ise, aga lugeda saavad kõik, samuti on võimalik kõike kirjutatut eksportida OpenOffice ning PDF formaadis (kuigi PDF eksportija on veidi katki).

Kolisin sinna ka oma JavaScripti alased materjalid ning kirjutasin juurde Google App Engine kasutamise õpetuse.

reede, 12. veebruar 2010

Veelkord WebKit'i kohalikust andmebaasist

Kirjutasin millalgi kuidas iPhone brauseris JavaScriptiga andmete säilitamiseks SQLite andmebaasi kasutada, tollal jäid osad asjad siiski segaseks, peamiselt andmebaasiga ühenduse loomisel kasutatavate parameetrite tähendus.

Andmebaasiga saab ühenduse järgnevalt:
db = openDatabase(db_name:String, 
db_version:String, db_description:String, db_size:Int);

Mida aga need parameetrid siis õigupoolest ikkagi tähendavad?

db_name - iseenesest selge, tegu on avatava andmebaasi nimega. Juhul kui sellist andmebaasi ei ole, siis see luuakse. See väärtus peab olema unikaalne, kuna andmebaase eristatakse just nime järgi.

db_version - selle parameetri kohalt oli varem segadus, kõik näited kasutasid väärtusena stringi "1.0" ja seetõttu tundus tegu olevat mingi konstandiga. Tegelikult on see rakenduse looja määrata ning tähistab andmebaasi kasutatavat versiooni. Juhul kui brauseris on juba andmebaas olemas, aga vahepeal on saidi soft ja sellega seoses andmebaasi ülesehitus uuenenud, siis saab antud parameetri järgi andmebaasimootorile teada anda, kas temal olev andmebaas on ikkagi korrektne või juba vananenud. Pikemalt on antud teemal juttu Safari Dev Center'is.

db_description - andmebaasi pikem nimetus, kui nimi peab olema unikaalne, siis see võib olla suvaline, kuna on ainult informatiivse iseloomuga

db_size - maksimaalne andmebaasi suurus baitides. Juhul kui andmebaas kasvab sellest väärtusest suuremaks, teavitatakse kasutajat ning küsitakse andmebaasi suurendamiseks kasutaja luba (vähemalt teoorias, ma ise pole proovinud).

kolmapäev, 13. jaanuar 2010

timefield jQuery plugin

Tegin uue jQuery plugina, mis võimaldab luua Facebook stiilis ajainfo tekste - ehk selliseid mis uuendavad end ise. Näiteks kui jätad lehe lahti, siis 10 minuti pärast ei ole lehel tekst "lisatud 8 sekundit tagasi", vaid "lisatud 10 minutit tagasi" nii nagu peab.

Dokumentatsiooni ja lähtekoodi leiab Google Code lehelt. Demo saab näha siit.

Mõtlesin küll Google Code asemel Githubi kasutada, aga kogu see täiendav majandamine (võtmete genereerimine, kohalik ja globaalne checkout jne) tundus nii väikse jupi jaoks liiga tüütu. SVN on ses suhtes minu meelest mugavam.

jQuery jaoks on plugin seetõttu, et jQueryl on olemas vastav "ökosüsteem". Ma isiklikult eelistan Prototype't, aga selle jaoks tehtud pluginaid ei ole kuskile ühtsesse kohta võimalik üles laadida.

esmaspäev, 11. jaanuar 2010

jscode.org

Uuendasin jscode.org lehel asuvad PDF faili, millest peaks saama minu JavaScripti teemalise käsiraamatu esimene osa. Olen juba nii mõndagi teisest osast valmis kirjutanud, kuid ülevaolevas failis tegin peamiselt vaid kosmeetilisi muudatusi, midagi sinna juurde ei pannud. Olen sellega juba päris rahul, arvatavasti umbes selliseks see esimene osa jääbki nagu praegu juba on.

reede, 8. jaanuar 2010

Miks ma ei käi Sportlandis

Mulle tuli just järjekordne reklaam-SMS Sportlandilt. Üldiselt ma väga reklaamsõnumeid ei loe, aga ega ma niikuinii Sportlandis ei käi. Põhjus on selle jaoks üsna lihtne, selleks on Sportlandi elunõme kliendikaardipoliitika.

Sportlandis saab iga ostu pealt kliendikaardiga 5% alla. Mul on kliendikaart olemas ja seega kvalifitseerun ilusti soodustuse saajaks. Viga poliitikas seisneba aga selles, et soodustuse saamiseks peab kaart ostu hetkel kaasas olema. Minul aga on igasugu kliendikaarte niigi palju ja kuna ma just iga päev spordipoes ei käi, siis seda kaarti mul kaasas reeglina ka ei ole.

Kui ma nüüd isegi satun mõnes kaubanduskeskuses Spordlandist mööda minema ja näen seal midagi ostmisväärset, siis ma ostu päris kindlasti ei soorita. Ja nii on juhtunud liialdamata vähemalt 10 korda. Ei osta ma seeõttu, et ma ei taha raha kaotada - kuna mul kliendikaarti kaasas pole, siis soodustust ma ei saa. Samas aga tean, et tegelikult peaks hind olema minu jaoks 5% odavam ning seda soodustust mitte kasutades viskan sisuliselt oma raha minema.

Nii et jäägi muud üle, kui edasi kõndida. Osta tahaks küll, aga lollina tunda (kui maksan üle) ka ennast tunda ei taha.