IBA verklaart Antiquiteiten van Bookweb en andere hilariteiten

Volgens de website bestaat de WebCie al sinds 1 januari 1970, dat is zo’n 25 jaar voor het uitvinden van het concept “website”! Volgens Git, ons versiebeheersysteem (wat vroeger SVN was (wat vroeger CVS was)), bestaat de code van de website al vanaf 1 juli 2000. Bookweb is het antiekste deel van de website en als je dit leest, is het nog steeds niet vervangen met een modern en superfancy nieuw boekenweb! Allemaal veranderingen in de vereniging kunnen we als jaarringen terugvinden in de code.

De huidige grote revisie van onze websitecode heet WhosWho4 omdat het de vierde editie is van ons ledenzoeksysteem. In het begin was WhosWho nog gewoon een collectie programmaatjes geschreven in de programmeertaal C, om de ledenadministratie bij te houden, maar ook al in code uit oktober 2000 wordt code geïntroduceerd die we nog altijd gebruiken. Hierbij zit bijvoorbeeld de functie tryPar, om parameters uit webrequests te lezen.

We hebben een overlevingsgrafiekje gemaakt, van welk percentage van de code gecommit op een bepaalde datum nog terug te vinden is in de huidige code.
survival_plot
Om eerlijk te zijn hebben we geen idee hoe al die plotselinge verspringingen zijn gebeurd, maar vermoedelijk hangt het samen met werken met branches (die in SVN neerkomen op alle code copy-pasten naar een andere map) en/of gegenereerde code. Je kunt ook interessante patronen ontdekken in de totale hoeveelheid regels code waar de website op gegeven momenten uit bestond. In deze grafiek zijn ook allemaal rare verspringingen te zien, die vermoedelijk met de verspringingen in de andere grafiek te maken hebben.
stack_plot

We hebben nog een paar interessante statistieken te geven over de Git-repository die we gebruiken bij de WebCie. Op het moment van schrijven (25 oktober 2017) bestaat de repository uit 15962 commits wat neerkomt op een gemiddelde van 2.5 per dag, of 4.9 per dag als je alleen de dagen meerekent waarop een commit gedaan is. De repository is 6324 dagen oud, waarvan op 3252 dagen een commit heeft plaatsgevonden. Over de gehele geschiedenis zijn er 1950084 regels toegevoegd en zijn er 1285980 regels verwijderd, waarmee je een huidig aantal van 664104 regels in de repo hebt. Niet geheel tegen de verwachtingen in zijn de maanden juli en augustus de minst actieve maanden; het aantal commits in deze maanden is samen goed voor slechts 8% van de totale commits.

In zulke oude code kom je nog wel eens “interessante” constructies tegen, zoals de onbegrijpelijke amalgamatie aan if-statements die in Bookweb draait als de boekencommissaris een rapport van de verkopen wil hebben en op de knop “Poep CSV-bestand uit” drukt. Leveranciers verwijder je niet, die worden “Weggeflikkerd”. Gaat er iets mis in deze brij, dan verschijnen er ook mooie foutmeldingen zoals “Whaa, het ging mis met $error, schop meteen een technisch persoon in zijn ballen!!!!” (waarbij $error staat voor dingen als 'WAAROM HEB IK GEEN TYPE?'). Inconsistenties in de administratie worden vermeld in de variabele $hedzerregels, vermoedelijk als belediging aan het adres van een zekere Hedzer die in het bestuur en WebCie zat. Als je de juiste rechten hebt, wordt de link naar deze pagina in het menu toegevoegd door de “Financieelmaaktnietuitmenucontenthook”. Als je als commissielid leden wilt inschrijven voor je activiteit maar je hebt geen leden geselecteerd wordt je door website op je vingers getikt met de melding “Je moet wel iemand selecteren Willem!”.

Groetjes, De ldap-check, de ldap-check, Kentucky Fried Chicken en de ldap-check

Ook gebruikers kunnen er wat van, met behulp van de doe-het-zelf sitebouwer de Publisher. Zo zit er in een mapje met verscheidene HTML-pagina’s uit 2006 ook de pagina kim, met als enige inhoud “Je bent lief!”, gemaakt door de toenmalige boekencommissaris, die naar verluidt een oogje had op de toenmalige commissaris intern. Verder zijn hier de pagina bedankt!, prachtige ouderwetse screenshots screenshots en archeologisch bewijs voor ledenpassen te vinden.

Toch kunnen we de mooiste lelijke constructies vinden in interne data. Het systeem dat voor Nederlandse tekst (waar beschikbaar) een Engelse tekst opzoekt, verwerkt opmerkingen die beginnen met VOC:, vernoemd naar de Vertaal- en Onderhoudscommissie van vier jaar terug, inmiddels vervangen door de Webredactie, die inmiddels is opgegaan in de PromoCie. Dit vertaalsysteem geeft soms de melding “FOUT: draai _(‘verfris’) in de wortel van je (mineraal?)bronnen”, als het wil dat je het scriptje “update” uitvoert in de map waar de source code te vinden is, ook wel “root” genoemd.

if(!$ster->checkUrl($entry->getName())) //Als dit fout gaat is er echt stront aan de knikker!

Mails correct versturen blijkt overigens ook niet zo makkelijk: omdat een mailprogramma anders omgaat met witregels dan een browser, dacht iemand slim te zijn en overbodige witruimte als volgt aan te pakken: neem alle juiste witruimte en vervang dat met de string “#SOEPMES#”, gooi alle andere witregels weg en vervang alle SOEPMESsen met de juiste witregels. En nu maar hopen dat niemand SOEPMES in zijn activiteitomschrijving vermeldt, zo zegt het bijbehorende commentaar.

Omdat er veel WebCieërs zijn die graag debuggen met de PHP-functie var_dump (een functie waarmee je meteen de inhoud van een variabele op het scherm weergeeft) wil het nog wel eens voor komen dat er een var_dump blijft staan in de code die gecommit wordt. Omdat er in het verleden kennelijk iemand was die nogal de neiging had dit erg vaak te doen zijn er maatregels genomen; iedere keer als je een var_dump probeert te committen krijg je de vriendelijke foutmelding “Eerst je debug code verwijderen, loeki.”.

Ben je dus een fan van oude code uitspitten om te snappen hoe het ooit had moeten werken, of juist iemand die in een avondje zulke fantastiche constructies uitdenkt, ook voor jou is het bij de WebCie altijd een feestje!

Ork ork ork, soep eet je met een…

#SOEPMES#.

IBA verklaart glob

Oh my Glob!

Met globben kun je heel efficiënt bestanden in de terminal selecteren, zodat je nog minder hoeft in te typen en wordt aangezien voor tovenaar door de mindere gebruikers. Welke doorgewinterde LaTeXgebruiker is het ook niet zat om na elke druk op de compilatieknop door tientallen meukbestanden te navigeren? De macht van het globben geeft je eindelijk de mogelijkheid een einde maken aan al die troep. Bovendien is het woord “glob” ook gewoon leuk om te zeggen.

Deze commando’s zijn allemaal getest in bash versie 4.2.46, wat je per default tegenkomt als je op het A–Eskwadraatsysteem een terminal opstart. Het voorbeeldmapje kun je op het A–Eskwadraatsysteem terugvinden in /home/mensjes/timb/troep/glob.

De allersimpelste glob is *, wat simpelweg alle zichtbare dingen in de huidige map betekent. Dat is voor ons meteen ook een beetje nutteloos, want rm * gooit ook al je niet-meukbestanden weg.

$ echo *
boot kat klok raket rat submapje zat

Gelukkig kun je de onderdelen die in de bestandsnaam moeten voorkomen ook aan * vastplakken, zodat je rm *.aux kunt gebruiken om alle .aux-meukbestanden die LaTeX maakt, weg te gooien. Je mag ook meerdere globs en verplichte stukken tegelijk gebruiken.

$ echo k*
kat klok


$ echo *k*t
kat raket

Daarnaast zijn er globs die maar één letter aangeven. Op de plek van ? mag elk karakter staan, maar niet meer dan één.

$ echo ?at
kat rat zat

Om een keuze tussen karakters aan te geven, is er de glob [...]. Tussen de haakjes zet je de karakters die je wilt hebben. Om niet een heel alfabet te hoeven typen, kun je ook een bereik aangeven met een streepje, zoals [a-z] voor alle kleine letters. Je hebt ook [^...] om juist alles behalve de inhoud van de haakjes te krijgen zoals:

$ echo [kr]at
kat rat
$ echo [p-z]at
rat zat
$ echo [^r]at
kat zat

Uitgebreidere globs

Om geavanceerdere trucs uit te halen, moet je uitgebreidere globs aanzetten. Het commando om dat te doen is shopt -s extglob globstar.Als je uit meerdere opties wilt kiezen, geeft extglob je de glob @(...). Zet de opties met een | ertussen in de haakjes. Aangeven hoe vaak iets herhaalt kan met ?(...) voor 0 of 1 keer, *(...) voor 0 of meer en +(...) voor 1 of meer. Tenslotte heb je nog !(...) die juist alles behalve de inhoud van de haakjes kiest.

$ echo ra@(ket|t)
raket rat
$ echo !(*at)
boot klok raket submapje

Het globstar-gedeelte slaat op de glob **, die niet alleen de inhoud van de huidige map kiest, maar ook alle submappen.

$ echo **
boot kat klok raket rat submapje submapje/ondergroep submapje/onderzeeboot submapje/verwisseling zat

Deze **-globs kun je niet zomaar uitbreiden met karakters erna, anders begrijpt bash het niet als glob. Gelukkig kun met een / ertussen toch verplichte onderdelen toevoegen. Let op: deze slash betekent niet dat de glob altijd naar een submap gaat.

$ echo **boot
-bash: no match: **boot
$ echo **/*boot
boot submapje/onderzeeboot

Creatief misbruik maken van glob

Je moet goed opletten dat globben ingebouwd is in bash, en een programma dus niet weet of zijn invoer ingetypt of geglobd is. Zo kunnen bestandsnamen per ongeluk opeens opties worden, wat ervoor zorgt dat je commando’s per ongeluk iets onverwachts gaan doen. Normaal weigert rm om mappen weg te gooien, tenzij je het de optie -r geeft.

$ rm submapje
rm: cannot remove `submapje': Is a directory
$ rm -r submapje
$

Als een of andere onverlaat het bestand -r aanmaakt, ziet rm * na globben uit als rm -r boot kat klok raket rat submapje zat, en gaan je mappen er dus toch aan.

$ touch -- -r
$ rm *
$ echo *
-bash: no match: *

Een andere populaire bron van bugs is de combinatie van glob en variabelen. Als je een variabele en een glob combineert, wordt eerst de waarde van de variabele ingevuld, en dan de glob uitgebreid.
Als je variabele geen waarde heeft (bijvoorbeeld door een typfout) kan je glob plots /* worden, wat als argument voor bijvoorbeeld rm nogal spectaculaire resultaten oplevert.

$ MAPJE=submapje echo $AMPJE/*
/archief /bin /boot /dev /etc /home /lib /lib64 /lost+found /media /mnt /opt /proc /rhev /root /run /sbin /scratch /srv /sys /tmp /usr /var

Aan de andere kant kun je misbruik van globben handig gebruiken om je bestanden juist te beschermen: als je rm de optie -I meegeeft, vraagt het commando eerst of je het wel zeker weet. Zet je dus een bestand genaamd -I in je belangrijke mappen, heb je een klein beetje extra bescherming tegen je eigen stommiteiten.

IBA verklaart Benamite

Je zult er waarschijnlijk nog nooit van gehoord hebben: Benamite. Toch gebruik je het iedere keer weer als je gebruik maakt van de A–Eskwadraatwebsite. Benamite is het virtuele filesystem van de website. Veel websites gebruiken een fysieke filesystem als structuur van de website. Dit houdt in dat er ergens op een server een mapje moet bestaan. Als je op de website naar bijvoorbeeld www.website.nl/index.html wilt gaan, betekent dat dat er in het mapje op de server een bestand index.html moet bestaan en dat de webserver ook nog de juiste rechten moet hebben om dat bestand te kunnen lezen. Je kunt wel begrijpen dat, als je website uit veel verschillende pagina’s bestaat, je dus heel veel verschillende bestanden nodig hebt, wat nogal veel overhead geeft. De A–Eskwadraatwebsite heeft op dit moment rond de 4000 verschillende unieke pagina’s waarbij de pagina’s die een variabele url hebben, zoals bijvoorbeeld a-eskwadraat.nl/Leden/5794, niet meegenomen zijn. Om de overhead te besparen is er ooit jaren geleden (in 2006 al) Benamite gemaakt.

Hoe werkt Benamite dan? Stel, je gaat naar de pagina https://www.a-eskwadraat.nl/Service/Bugweb/. We vergeten het https://www.a-eskwadraat.nl/-gedeelte want dat geeft alleen het domein aan waarmee de server gevonden kan worden en heeft voor Benamite geen verdere functie. We houden dus de uri /Service/Bugweb/ over. Deze gaan we door Benamite halen. Benamite gaat als eerste de eerste '/' bekijken, wat de root is van de site. Hij kijkt of het wel bestaat in Benamite, of het een map is en of de huidige gebruiker er wel bij mag. Als dit allemaal goed is, gaat Benamite door naar het volgende gedeelte: 'Service/'. Ook hier kijkt hij weer of het bestaat, etc, etc. Op die manier werkt Benamite de delen van de uri een voor een af. Eenmaal bij het laatste aangekomen, zijn er een aantal opties. Als het laatste element een bestand is in Benamite, dan opent hij het bestand. Als het een map betreft, probeert hij het bestand 'index.html' in die map te openen (de naamgeving is door de jaren heen niet veranderd en eigenlijk niet meer van toepassing, want we hebben nauwelijks nog html-bestanden op de website). Als dat bestand niet bestaat, stuurt hij een foutmelding naar de gebruiker.

Dan is er ook nog de speciale ‘variabele entry’. Dit zorgt ervoor dat je bijvoorbeeld elk lid kan bekijken op /Leden/*lidnr*. Een variabele entry is een map met wat extra’s: je kan eisen van de variabele entry wat het moet zijn, bijvoorbeeld een activiteit-id, een commissie-id etc en of het zichtbaar is voor de huidige gebruiker. Vervolgens accepteert de variabele entry alles wat hieraan voldoet. Wat gebeurt er dan, als je dus naar /Leden/5794/ gaat? Benamite handelt eerst /Leden/ af en als dit allemaal goed gaat, komt hij bij het gedeelte 5794/ uit. Er staat in de map /Leden/ geen map of bestand 5794, maar wel een variabele entry. Wat Benamite nu gaat doen, is: hij gaat kijken wat de variabele entry moet zijn. In dit geval moet de variabele entry een lidnummer zijn van een bestaand lid en moet degene die ingelogd is, het lid kunnen zien. Als dit allemaal goed is gegaan, stuurt Benamite het door naar het volgende gedeelte. In alle bestanden die in de map of submappen van de variabele entry staan kunnen we vervolgens met speciale php-functies alle variabele entries uit de url halen en op die manier de juiste objecten erbij halen.

Bestanden in Benamite kunnen verschillende types zijn en dus verschillende functies hebben. Het meest standaard is het normale bestand, waarmee we functies in php-bestanden kunnen aanroepen om op die manier een pagina te maken. Daarnaast hebben we geüploade bestanden, dit zijn bestanden die naar de site zijn geüpload, zoals bijvoorbeeld pdf’jes, afbeeldingen etc. Dan zijn er ook nog Publisherbestanden. Dit zijn bestanden waarvan de html wordt opgeslagen, zodat die later door gebruikers van de website aangepast kan worden en je dus niet in de code van de website hoeft te hekken om de html aan te passen. Dit wordt bijvoorbeeld gebruikt voor pagina’s zoals /Vereniging/Kamer/. Benamite heeft ook links, als een uri eindigt in een link, stuurt Benamite je meteen door naar waar de link naar toe linkt. Als laatste is er nog de php-haak. Dit is een soort midden tussen een normaal bestand en een variabele entry in. Bij een php-haak wordt de rest van de url die nog over is en niet door benamite verwerkt is, rechtstreeks aan de php-haak doorgegeven die er dan vervolgens mee kan doen wat ie wil. Php-haken werden vroeger voornamelijk gebruikt bij de menuutjes; zo werd bijvoorbeeld ‘Open de boekverkoop’, ‘Sluit de boekverkoop’ als de boekverkoop al geopend was. Tegenwoordig worden php-haken nauwelijks meer gebruikt op de A–Eskwadraatwebsite.

Al met al maakt Benamite het beheren van onze fantastische website een stuk eenvoudiger en hopelijk kijk ook jij nu weer anders tegen de website aan dan dat je voorheen deed. Lijkt het je nou leuk om nog meer over Benamite te weten te komen of wil je graag zelf knutselen aan de A–Eskwadraatwebsite, kom dan vooral een keertje langs op onze wekelijkse Werkfeestjes op de dinsdagavond in de A–Eskwadraatwerkkamer!

IBA Verklaart POST Requests

Zo rond het begin van de jaren negentig kwam Tim Berners-Lee op het idee van het WereldWijde Web. Alle informatie op de wereld wordt daarmee aan elkaar gekoppeld met behulp van pagina’s die links bevatten naar andere pagina’s. Met een browserprogramma dat HTTP (Hypertext Transfer Protocol) spreekt, kan iedereen deze pagina’s ophalen van servercomputers die over de hele wereld verspreid zijn. Meer dan twintig jaar later is het principe van het web nog ongeveer hetzelfde gebleven, maar dan compleet anders opgevat. Pagina’s zijn geen statische documenten meer die ergens op een harde schijf staan, maar interactieve programma’s die doen alsof ze statische documenten zijn die ergens op een harde schijf staan. Daarbij komt nog aardig wat kijken.

Lees verder IBA Verklaart POST Requests

IBA Verklaart Achievements

Zoals velen van jullie al is opgevallen heeft de A–Eskwadraatwebsite sinds kort achievements voor alle ingelogde leden. Natuurlijk is het leuk om zoveel mogelijk achievements te verzamelen maar sommigen zijn misschien wel benieuwd hoe dat nou allemaal in z’n werk gaat, al die achievements.

Achievements kunnen we opdelen in twee categorieën, eenmalige achievements en de zogenaamde ‘medaille’-achievements. Voor eenmalige achievements hoeft 1 bepaalde actie 1 keer uitgevoerd te worden en bij medaille-achievements wordt je voortgang in een teller bijgehouden en er zijn oplopende drempelwaarden voor het halen van elk level van de achievement.

Voor elke eenmalige achievement houden we in een functie bij hoe de achievement gehaald kan worden. Op het moment dat een pagina geladen wordt kijken we of er een achievement gehaald is. Al onze pagina’s op de website worden met een speciale end-functie afgesloten. Aan deze end-functie is nu toegevoegd dat voor het lid dat ingelogd is de achievements gechecked worden. Als er nieuwe achievements zijn, worden deze met javascript bovenaan de pagina weergegeven. De achievementschecker wordt pas bij het afsluiten van de pagina aangeroepen, omdat als de functie bij het begin de pagina zou staan, zou het langer duren voordat de gebruiker een pagina te zien krijgt.

De check voor de medaille-achievements werkt net iets anders. Die functie ziet er als volgt uit:

static public function checkBSGPBadge($badgeNaam, Persoon $pers, $setlast = true)
{
    global $BADGES;

    $badgesBSGP = $BADGES['bsgp'];

    if(array_key_exists($badgeNaam, $badgesBSGP)) {
        $badge = $badgesBSGP[$badgeNaam];

        if($badge['multi'] == 'long') {
            $multiplier = array(1, 2, 5, 10);
        } elseif($badge['multi'] == 'short') {
            $multiplier = array(1, 2, 3, 4);
        }

        foreach($multiplier as $multi) {
            $res = PersoonBadge::badgeBSGPCount($badgeNaam, $pers);
            if($res >= $badge['value'] * $multi) {
                $persoonBadges = $pers->getBadges();
                $persoonBadges->addBadge($badgeNaam . '_' . ($badge['value'] * $multi), $setlast);
            }
        }
    } else {
        user_error("Badge " . $badgeNaam . " bestaat niet!");
    }
}

Eerst wordt er gekeken of de achievement die je wilt controleren (variabele $badgeNaam) wel bestaat in de lijst met bestaande achievements. Dan zijn er twee soorten medailleachievements: short en long. De long-achievements hebben als vermenigvuldigingsfactor 1, 2, 5 en 10 voor brons, zilver, goud en A–Eskwadraat respectivelijk en de short hebben als vermenigvuldigingsfactor 1, 2, 3 en 4. Dan wordt er voor iedere vermenigvuldigingsfactor gekeken of het resulterende getal de drempelwaarde overschrijdt.

Om een zo breed mogelijk aanbod aan achievements te kunnen doen en makkelijk nieuwe achievements te kunnen toevoegen worden de achievements in een json-string opgeslagen. Hier kan je zien wat er gebeurt als er een nieuwe achievement wordt toegevoegd bij een gebruiker:

public function addBadge($badge, $setlast = true)
{
    $badges_json_string = $this->getBadges();
    $last_json_string = $this->getLast();
    
    $badges = json_decode($badges_json_string);
    $last = json_decode($last_json_string);
    
    if(sizeof($badges) != 0 && in_array($badge, $badges)) {
        return;
    }   
    
    $badges[] = $badge;
    
    if($setlast) {
        if(sizeof($last) == 0 || (sizeof($last) != 0 && !in_array($badge, $last))) {
            $last[] = $badge;
        }   
    }   
    
    $badges_json_string = json_encode($badges);
    $this->setBadges($badges_json_string);
    
    if(sizeof($last) != 0) {
        $last_json_string = json_encode($last);
        $this->setLast($last_json_string);
    }   
    
    $this->opslaan();
}

Je kan zien dat eerst de huidige json-string met achievements opgehaald wordt uit het $this-object. Als de achievement al bij de gebruiker staat doen we natuurlijk niets. Dan wordt aan de gedecodeerde json-string (die nu een array is) de nieuwe achievement toegevoegd. Dit wordt dan vervolgens weer als een json-string opgeslagen. Alles met $last in de bovenstaande functie wordt gebruikt om op te slaan wat de nieuwe achievements van de gebruiker zijn sinds de gebruiker voor het laatst is ingelogd.

Dit geeft je een klein beetje een idee hoe de achievements op de A–Eskwadraatsite werken. Heb je zelf nog goede ideeën voor leuke achievements, voel je vrij om ze te melden in de IBA-chat of op Bugweb!

1 April

welkom1aprilDe opmerkzamere leden onder ons hebben afgelopen 1 april (sommigen al 3 minuten na het begin!) opgemerkt dat onze homepagina plots achterstevoren vertoond werd. Het leuke is dat de hele pagina nog precies hetzelfde werkte, alleen zat alles links wat voorheen rechts was en vice versa. Het is nog verrassend eenvoudig om dit voor elkaar te krijgen ook!

Om deze grap uit te voeren hebben we twee kleine dingetjes moeten toevoegen aan de webpagina’s. Als je niet precies weet wat er gebeurt, hoef je je geen zorgen te maken. Het is gewoon om een indruk te geven wat je allemaal met een webpagina aankan. Dit stukje code voeren wij uit als je een pagina opvraagt, nog voordat we hem naar je opsturen.

if(date('d-m') == '01-04' && substr($uriRef['request'], 0, 5) == '/Home') {
echo '<link rel="stylesheet" href="'.$this->scriptFileTime('/Layout/CSS/1april.css').'" type="text/css" media="screen">'."\r\n";
}

Ten eerste controleren we of het daadwerkelijk 1 april is. Zo konden we de spiegeling van tevoren installeren en gebeurde het automatisch op de dag zelf. De website is toch wat minder bruikbaar dus we hadden later op de dag ingesteld dat alleen de homepagina gespiegeld werd. Dit doet de code door te kijken of de opgevraagde pagina begint met ‘/Home’. Alleen als allebei de eisen waar zijn, voeren we de grap echt uit. Om dat te doen, voegen we een stukje toe aan de pagina, waarin staat dat de opmaak (stylesheet) van 1april.css uitgevoerd moet worden:

body {
-moz-transform: scaleX(-1) !important;
-o-transform: scaleX(-1) !important;
-webkit-transform: scaleX(-1) !important;
transform: scaleX(-1) !important;
}

In het bestand 1april.css vertellen we wat er precies moet gebeuren: we passen een transformatie toe waarbij we in de hele inhoud (body) de x-coordinaat met -1 vermenigvuldigen, oftewel een spiegeling. Verder zorgt !important er nog voor dat deze regel belangrijker is dan andere transformatieregels, zodat je vrijwel zeker daadwerkelijk de spiegeling zult zien.

Helaas is de wereld niet perfect. We moeten we de regel een aantal keer met kleine variates herhalen voor het geval dat het niet duidelijk is. We willen natuurlijk niet dat iemand onze geweldige grap kan missen. Alle populaire browsers ondersteunen net weer een andere editie van de nieuwere en geavanceerdere opmaak, zoals bijvoorbeeld transformaties (dat is geen wonder, als je precies wilt doen zoals het hoort, ben je ook een tijdje bezig). Gecko (de drijvende kracht achter Mozilla Firefox) verwacht dat je voor dergelijke fancy dingetjes altijd -moz- zegt, Opera eist een -o- en WebKit (waar bijvoorbeeld Google Chrome en Safari op gebaseerd zijn) wil juist weer -webkit-. Later bleek dat we voor Internet Explorer compleet vergeten waren om -ms-transform toe te voegen. Gelukkig worden na verloop van tijd steeds meer dingen gewoon standaard geaccepteerd, en kun je steeds meer van die voorvoegsels weglaten.

webcie1aprilDoe-Tip: als je zelf wilt spelen met transformaties en rotaties, kun je in de betere browsers dit heel eenvoudig doen, zonder eerst een hele website te hoeven bouwen.  Rechtsklik op wat je wil spiegelen en kies ‘Inspect Element’. Rechts zie je een heleboel opmaakregels, en die kun je zelf bewerken. Voeg een regel met transform: scaleX(-1); toe en bewonder de resultaten. Probeer ook eens andere transformaties zoals die uit dit lijstje.

Ben je na deze uitleg benieuwd geworden naar websites of heb je altijd zelf al een site willen maken? De WebCie geeft een reeks websitecursussen op 8, 15 en 22 april in BBL 109. Voor informatie kun je gaan naar de site van de WebCie.