Disclaimer: Welkom bij onze TechBlog!
Dit is een artikel in ons TechBlog. Ons Techblog bevat artikelen, geschreven door onze developers, over dingen die ze zijn tegengekomen tijdens hun werkzaamheden. Deze artikelen gaan (meestal) over een technisch onderwerp, en zijn met name bedoeld om te vermaken en (soms) te informeren. Waarom deze disclaimer? Zie het als een vorm van voorsorteren.

vrijdag 19 december 2025
Auteurs: Lars Waage, Jeroen Mimpen

Sorteren

Ons verhaal begon, zoals dat wel vaker gaat, met een opmerking van Casper. Tijdens onze vaste meeting op dinsdag benoemde hij dat het best veel werk was om ons takenlijstje gesorteerd te houden op volgorde van datum, want dit moest handmatig. De reactie was ook gebruikelijk: Henk beaamde het bedachtzaam en Jeroen zat zoals gewoonlijk niet te luisteren.

Het is ook een lastig probleem. In tegenstelling tot onze zelf ontwikkelde tool Streamline, waarin we zelf eenvoudig dit soort wijzigingen kunnen aanbrengen, kan dat hier niet. De software die we gebruiken voor onze tickets en todo-lijsten is namelijk een commerciële tool (een webapplicatie), waarvan we geen toegang hebben tot de broncode. We kunnen immers niet al onze tools zelf ontwikkelen, er moet ook nog wat tijd over blijven om te werken voor onze klanten. Weinig aan te doen dus, hooguit kunnen we erover mopperen.

Dit is Casper. Casper heeft een sorteringsprobleem.

Dit is Casper. Casper heeft een sorteringsprobleem.

De week erna vond onze vaste meeting weer plaats. Het leek weer een normale meeting. Henk was weer scherp aanwezig, en Jeroen zat weer eens niet op te letten. Het sorteermoppermoment stond op het punt van aanbreken. Maar dit keer verliep het anders. Op het moment dat Casper begon met handmatig sorteren, sprong Henk op en zei: "Ho! Stop!".

Muis nog half in de hand, zijn stem - anticiperend op de woorden van Henk - gevuld met hoop, vroeg Casper: "Is het eindelijk zover? Hebben ze eindelijk een sorterings-feature toegevoegd!?".

"Nee." Zei Henk, terwijl hij liet zien hoe we nu de lijst automatisch kunnen sorteren. "Zij hebben die feature niet toegevoegd. Wij hebben die feature toegevoegd. Of Lars, om specifiek te zijn."

Casper juichte van vreugde: hij hoeft vanaf nu nooit meer handmatig lijstjes te sorteren! Vreugde voldoet waarschijnlijk onvoldoende om Caspers emotie te beschrijven. Ik geloof dat er op een gegeven moment zelfs confetti en champagne bijgehaald werd, en er een optocht door de binnenstad van Hoorn werd georganiseerd. Kortom: het was een van de mooiste dagen van zijn leven.

Oké, het bovenstaande verhaal is misschien een licht overdreven dramatisering van hoe ik het ervoer (waarschijnlijk omdat ik weer niet zat op te letten). En het automatisch sorteren was niet meteen mogelijk, want in werkelijkheid moest Casper eerst wat installatie-stappen doorlopen, voordat de knop hiervoor in de user interface verscheen.

De essentie van mijn (licht overdreven) verhaal klopt verder wel: de interface van een tool die wij niet zelf ontwikkelen hebben we uitgebreid met deze functionaliteit. Maar, dit is toch een commerciële webapplicatie, waar wij de broncode niet van hebben? Hoe heeft Lars dit dan in kunnen bouwen? In dit artikel laat ik vanaf hier Lars aan het woord om dat uit te leggen.

Sorteren in Basecamp

Bij Webenable gebruiken we Basecamp als ticketsysteem voor alle werkzaamheden. Het sluit heel goed aan op onze werkzaamheden, maar het kan altijd beter. Een functionaliteit die we gebruiken, is het zetten en controleren van een due date op een ticket. Maar als je een lijst hebt met veel tickets, dan zie je tussen de bomen het bos niet meer. Je kunt tickets wel in een andere volgorde slepen, maar dan blijf je aan de gang. En er is niet eens een sorteerfunctie! Normaal gesproken dien je dan een wijzigingsverzoek in en wacht je een tijdje terwijl je workarounds aan het verzinnen bent... tenzij je vermoedt dat je het zelf kunt en dat een mooie uitdaging vindt.

Google Chrome extensions

We gebruiken standaard Google Chrome als browser en daarbinnen kun je extensies maken, waarmee je websites kan manipuleren. Een Chrome-extensie bestaat uit een manifest en JavaScript-bestanden voor de uitvoering. Het manifest is een JSON-bestandje dat beschrijft wat de extensie doet, welke JavaScript-bestanden iets moeten doen, wat deze JavaScript-bestanden mogen doen en waar (welke URLs) ze dat moeten doen.

Background worker en tabbladen

Er is altijd een JavaScript-bestand voor een background worker, die centraal binnen Chrome iets kan doen op basis van events die afgaan op de verschillende tabbladen. Chrome kent een library met events die afgaan als er iets gebeurt op een tabblad. Zo hebben wij chrome.webRequest.onCompleted geïmplementeerd. Bij het onCompleted event kun je vanuit de background worker met chrome.tabs.sendMessage een bericht versturen naar het tabblad waar het om gaat. En daar kun je dan een tweede JavaScript-bestand injecteren dat het chrome.runtime.onMessage event oppakt. Met die constructie kunnen we code aftrappen om op dat tabblad daadwerkelijk iets te gaan doen. Zo kunnen we de DOM van de betreffende pagina manipuleren (hij is immers 'Completed'). We kunnen op zoek gaan naar de juiste CSS-classes om ergens te komen waar je een optie wilt toevoegen, in ons geval een sorteeroptie in het menu. Bij die knop implementeren we dan het onclick event voor het sorteren.

Sorteren

Maar hoe gaan we automatisch een ticket verplaatsen? Als je in Basecamp via Developer Tools gaat kijken wat er gebeurt als je tickets sleept, dan zie je dat er een functie in een JavaScript-library wordt aangeroepen met als parameters: het ID van het ticket, ID van de parent-lijst en de nieuwe positie in die lijst. Die zorgt voor de verwerking daarvan bij Basecamp, dus dat zouden we prima kunnen gebruiken.

Het totaalplaatje om te doen wordt dan:

  1. Loop door de DOM op zoek naar tickets met een due date.
  2. Onthoud de ticket-ID's en de due date.
  3. Er blijken ook meerdere lijsten te zijn, dus onthoud ook de parent-IDs, zodat we de tickets binnen de lijsten in één keer kunnen sorteren.
  4. Sorteer de lijst.
  5. Loop door de lijst en roep voor elk ticket de library functie aan met de parent en de nieuwe positie.
  6. Ververs het scherm als alles klaar is.

Dus, mooi achter de onclick bouwen en gaan? Helaas.

Security

Omdat Chrome extensions wel erg kunnen ingrijpen op de werking van een arme onwetende website, is het ook een security-risico. Daarom heeft Google de toegang tot JavaScript op de pagina geblokkeerd. Je kunt er nog wel omheen werken, door in het manifest voor het JavaScript-bestand de WORLD (een soort security-scope) in te stellen op MAIN in plaats van ISOLATED. Dan kun je op de pagina overal bij, maar dan kun je weer niet de Chrome-events gebruiken. Het is dus het één of het ander.

Maar wat nou als we twee JavaScript-bestanden gebruiken, een met scope MAIN en de ander met scope ISOLATED? ISOLATED ontvangt nog steeds het Chrome-event, past de DOM aan met ons menu-item, en bij de onclick van dat item doen we een window.postMessage naar MAIN.
MAIN heeft window.addEventListener en krijgt daarmee het seintje door dat hij zijn gang kan gaan. Die doet al het bovenstaande werk, inclusief het aanroepen van de nu wel beschikbare JavaScript-library call.

En dat werkt! Het resultaat is een naadloze integratie. Iedereen die het ziet, zou denken dat het standaard Basecamp-functionaliteit is, maar dat is het niet.

Conclusies

  • Met Chrome-extensions kun je ver gaan om extra gedrag in een bestaande applicatie te injecteren.
  • De constructies zijn best complex, maar de beveiliging was goed te omzeilen.
  • Installeer alleen third party Chrome extensions die je echt vertrouwt.
  • Als uitdagingen slagen, dan vinden we dat gaaf en worden we blij.

Open source update (2025-12-22)

We hebben onze extension open source gemaakt.

Wil jij net zo blij worden als Casper?

Ga dan naar onze GitHub-repository en probeer onze extension.