pwnt.be

Highlighting XHTML

As you’re well aware by now, you can search my blog posts by clicking on the little magnifier on the bottom right. This feature was already pretty decent, if I do say so myself, but today, I added some extra niceness: search queries are now highlighted.

This may seem trivial to implement, but, in fact, requires quite a bit of black magic. After all, we’re dealing with XHTML, which means we can’t use nested tags for highlighting. In addition, since it’s a whole word search, tags may be in the middle of words, e.g. to emphasize a single character.

For future reference, the algorithm I used is the following:

  1. Split the HTML into tags and character data. For example, the string

    <p>Hello, <em>world</em>!</p>

    would result in an array containing

    '<p>' 'Hello, ' '<em>' 'world' '</em>' '!' '</p>'
  2. Next, hold on to the tags from the array above, and create a new string where you replace all the tags with an arbitrary placeholder, e.g. a null byte. In the example, you’d get

    \0Hello, \0world\0!\0

    Update: In addition, do the same for HTML entities; otherwise, the entity codes might get split up.

  3. Now for the tricky part. Create a regular expression that will match all the words you need, but work the placeholder into it so that it may occur between each pair of characters. For example, if you wanted to match both “hello” and “world”, the regular expression would be

    /(h\0?e\0?l\0?l\0?o|w\0?o\0?r\0?l\0?d)/is

    The i switch stands for case independence; the s switch expands the expression over multiple lines, which isn’t necessary for this case.

    That won’t cut it though. That is to say, it will also match parts of words, e.g. “worlds”. To prevent this, you use lookbehind and lookahead. In my case, I put

    (?<![\p{L}\p{N}])

    in front and

    (?![\p{L}\p{N}])

    at the back, which, for the example, results in

    /(?<![\p{L}\p{N}])(h\0?e\0?l\0?l\0?o|w\0?o\0?r\0?l\0?d)(?![\p{L}\p{N}])/is

    Obviously, you’ll never actually get to see the expression as it should be dynamically generated.

  4. The worst part’s over. Now, the basic idea is to apply the generated expression to the string with null bytes from the second step, replacing each match with its own value, surrounded by whichever bit of HTML you wish to use for highlighting—in my case, <em class="highlight"> and </em>.

    Another caveat though. To avoid getting nested tags, you need to split up the highlighting whenever a new tag starts. Fortunately, tags are all represented by null bytes, so surrounding each null byte in the matching string with a closing and opening highlighting element gets the job done.

  5. Almost there now. The highlighting itself has already happened, but the original tags still need to be restored. This is probably the easiest step: just replace all null bytes with consecutive elements from the array of tags from step 2 and you’re done.

My eventual implementation is in Pwnt_Highlighter. If you wish to use it, go right ahead, but like everything else on this site, it’s licensed BY-NC-ND. Neat improvements are always welcome.

One final note: since my site uses MySQL’s Unicode features, the matching still isn’t 100% accurate when you use international characters. That is to say, MySQL thinks é and e are the same character, while PHP disagrees. The result is that, for example, the database layer will match “résumé” and “resume” for the same query, but since PHP does the highlighting, it will only match what you entered literally.

Yeah, Kerning is a Bitch

I noticed this a long time ago, but I suppose I’m the only one. Look at the Kubuntu logo:

The Kubuntu logo
The Kubuntu logo

Never mind the bitchass glass effects and drop shadow. Focus on the spacing between the letters K and U, and compare it to the other letters. I suppose they really like to emphasize that even the logo is just an extension of Ubuntu.

Or, as O’Toole puts it

“Typography? Aesthetics? What's that and how can I apt-get it?”

That being said, I am writing this on my Kubuntu laptop.

More JavaScript

Another addition I would like to see in MooTools—or, better yet, ECMAScript in general—is the equivalent of Java’s String.equalsIgnoreCase(String) function. Sure, a first implementation is trivial:

String.extend({
  'equalsIgnoreCase': function(str) {
    return (this.toLowerCase() == str.toLowerCase()
      && this.toUpperCase() == str.toUpperCase());
  }
});

… but if you want it to be a little more efficient, you’ll avoid inspecting both strings in their entirety first and use String.charAt(int) instead. If your motto is ‘Code is Poetry’, you’ll end up with something like …

String.extend({
  'equalsIgnoreCase': function(str) {
    var i = -1;
    if (this.length == str.length) {
      i = 0;
      while (i < this.length
      && this.charAt(i).toLowerCase() == str.charAt(i).toLowerCase()
      && this.charAt(i).toUpperCase() == str.charAt(i).toUpperCase()) {
        i++;
      }
    }
    return (i == this.length);
  }
});

Since ECMAScript is weakly typed and doesn’t have a Character class like Java does, the actual comparison still uses string functions. I wonder how much this influences the algorithm’s performance.

Update: I added the String.toUpperCase() calls, because Java’s implementation has them too. I’m no expert when it comes to international characters.

Folklore

Since I’ve got tags now, I’m going to start a new chapter. From now on, I will be blogging in my native language Dutch when relevant. Dutch posts will all receive the tag ‘Nederlands’. English will remain the primary language of this blog.

Dames en heren politici, legt u mij nu eens uit wat u daar drie weken heeft uitgespookt op Hertoginnedal. Behoed ons voor een nieuw Verdrag van Rome, maar…

  1. Aan de Vlaamse politici: Hoeveel onder u geloofden er drie weken geleden nog dat de Walen Franstaligen de minste toegeving zouden doen op communautair vlak?

  2. Aan de Waalse politici (dewelke de Nederlandse taal machtig zijn): Bent u bekend met het woord ‘democratie’? Hoelang plant u deze schertsvertoning nog vol te houden?

Maakt u zich geen zorgen, waarde lezer: ik zal niet vervallen in een tirade over hoe de Waal op onze rug leeft. Neen, ik zoek het een paar kilometer dichter bij huis, want wie ‘staatshervorming’ zegt, zegt ‘tsjeef’ en wie ‘tsjeef’ zegt, die zegt ‘Leterme’.

Vazal van het eerste uur Bart De Wever én concullega Bart Somers waren er vandaag alweer als de kippen bij om te benadrukken dat Yves een goede formateur is, maar dat er met de Walen Franstaligen nu eenmaal niets aan te vangen valt. Conclusie: zelfs de beste formateur zou nog het onderspit delven. En wie ben ik om daar iets tegen in te brengen?

Natuurlijk valt er met die verdomde Walen Franstaligen niets aan te vangen. Hoe zou u zelf zijn? Om De Morgen te citeren:

Grote institutionele hervormingen inzake fiscaliteit, justitie, gezondheidszorg of de spoorwegen zouden volgens Di Rupo gevaarlijk en schadelijk kunnen zijn voor de Franstaligen.

Tot 10 juni klonk het of Leterme die verdomde Walen Franstaligen een paar toontjes lager zou laten zingen, maar eens belast met de regeringsvorming, ijvert diezelfde Leterme voor een tweederdemeerderheid voor een staatshervorming waar iedereen beter van wordt. Kom kom, Yves, laat ons een kat een kat noemen: het is hoog tijd dat de machtsverhoudingen in dit land worden rechtgetrokken en dat gaat nu eenmaal niet zonder slag of stoot. En dat weten die verdomde Walen Franstaligen ook wel.

Is dit schrijven bedoeld als een constructieve oplossing voor het communautaire debacle? Neen, ik ben geen politicus. Het is louter mijn bedoeling het onnut te benadrukken van een campagne die, uitgenomen dat duivelse “goed bestuur”, uitsluitend draaide om de staatshervorming. Misschien heeft het de modale Vlaming (terecht) wakkergeschud, maar we zijn nu 2 maanden verder en veel vooruitgang is er nog niet geboekt.

Zelfs al ben ik dan Vlaams-nationalist, ik vind het volslagen idioot om uit de verkiezingsuitslag te besluiten dat Vlaanderen zich uitgesproken achter een ingrijpende staatshervorming schaart. Is tsjeef door het kartel een synoniem geworden van flamingant?
Als de Vlaming zich echt zo bekommerde om de belangen van de deelstaten, dan was er wel eerder een signaal gekomen. En ook dát weten de Walen Franstaligen ook wel. Hou nu dus maar op dat imago te cultiveren van de separatistische Vlaming! En laat er geen twijfel over bestaan dat die boodschap is bedoeld voor beide zijden van de taalgrens.

Hoe moet het nu verder? Ja, als we dat eens wisten. Een dikke 2 weken geleden, toen iedereen die niet op Hertoginnedal vertoefde, al wist op wat voor fiasco we afstevenden, schetste Lijst Dedecker volgend scenario:

Over een week slaat iedereen de armen in de lucht, want ‘We geraken er niet uit!’ Volgende stap, want Leterme werkt wel degelijk stap voor stap, is de Koning: Sire, Help Ons, want het land is in gevaar! Zie, de vakbonden zeggen het ook, en de mensenrechten lopen gevaar, en de stookolie slaat op. Sire, help ons uit de crisis!

Kijk, noemt u dat nu gerust dramatisch; ik doe dat immers ook en ik heb überhaupt voor die mensen gestemd en zou het opnieuw doen. Het politieke klimaat verplicht een oppositiepartij zich er nu eenmaal toe zich te profileren middels boude uitspraken. Kijkt u echter even voorbij de scherts, dan ontwaart u vast een akelig accurate weergave van de huidige omstandigheden – met een week vertraging dan wel.

Wat voorspelt datzelfde artikel verder nog? Een nog grotere ‘Non’ tegen het communautaire, tegen roomsblauw en tegen de vooruitgang. Want zo gaat dat nu eenmaal in ons federale Belgenland. Het wordt allemaal afwachten, maar gelooft u er nog in?

En, Yves, waar ben je nu met je goed bestuur?

Schizophrenic Lite-On Burners

As you may recall, I purchased a Lite-On LH-20A1S a while ago. As it turns out, that wasn’t entirely accurate.

In fact, the device identified itself as the illustrious DH-20A1S. While there is no mention of them on the Lite-On website, the DHs appear to be rebadged versions of the LHs, which I assume makes them targeted at system builders. Never having bought any Lite-On equipment before, I don’t know if it’s okay for Codima to sell a DH model separately, let alone call it an LH.

That being said, after some Google-fu, I can be pretty sure that the DH-20A1S is in fact identical to the LH-20A1S. There is however an inconvenience: since it identifies itself as a generic DVD burner, you cannot use Lite-On’s firmware updaters. And by “cannot”, I mean you can, but you need to grab modified ones from codeguys.rpc1.org and use the Flash Utility from that site. After a reboot, you’ve got yourself a real Lite-On, with the latest firmware—which is a good thing, because pretty much every piece of DVD packaging tells you to consult the manufacturer of your burner for the latest firmware.

I guess that’s what I get for buying cheap affordable hardware.

Disorientation
Continuity
Retributions
Koop eens een Nokia Lumia 800
Samuel Debruyn
Bizar Hairdressing & Beyond
Hanne, Hanne, Ruxi, Wim, Tim, Sarina, Lies, Lynn, erwin, Ano, Frederick, Jacqueline, Wazaaa, Tim, Rebecca, Charlie
Lplayer for the Rest of Us
fieryy-AA, jesus2099, Tim, jesus2099, Tim, jesus2099, Tim, PixelPirate
Automating OpenVPN Connection on Windows XP
blanky, sky, Tim, Geb, 12vpn, Tim, neecom
Simple Linear Regression with JFreeChart
Nicolas Machado, Sascha, Tim, Sascha, Tim, Sascha
Colophonics