Damian CugleyAlleged Tarot 2002

How the dealer works

JavaScript (ECMAScript)

SVG files can have Javascript programs embedded in them, just like HTML. So my deal.svg graphic is linked to a JavaScript file dealer.js which defines a bunch of variables and functions for decoding the script parameters, and "dealing" cards. The outermost svg element in the picture has an onload attribute that invokes a function ol that ties them all together.

The first thing it does is call scanOpts which extracts the parameters to the script from the URL. The params recognized at present are lo, which must name one of the layouts it the "database" of layouts (described later), and q, the question text. An example URL defining those parameters would be deal.svg?lo=horseshoe&q=Will+Frodo+reach+Mordor%2F (where the plus signs represent spaces, and %2F is an encoded question mark).

Then ol creates an array of 78 numbers representing the cards and calls shuffle to rearrange them randomly. Finally it calls dealLayout to generate images of cards "drawn" from the deck at positions on the screen defined by the layout.

Dealing cards with DOM

Each card is represented by a bunch of SVG elements—the rectangle that is the backdrop, the image of the card, the text, etc. Rather than try to write JavaScript code that creates all these elements, I created a template in the defs section which is cloned once for each dealt card. This uses the DOM functions getElementById to locate the items that need to be modified (such as the link, whose xlink:href attribute must be modified), and finally cloneNode to make a copy that can be appended to the document.

The advantage of using getElementById each time is that it avoids having the code that makes the per-card changes having to know the details of how the cards are drawn. I should be able to make changes to the SVG template without breaking the dealing part.

Storing layouts as XML

A layout is an arrangement of dealt cards, eahc of which needs to have an indication of its role in the spread (this card represents the seeker, that one the future, and so on). At first I stored this in the JavaScript as a dictionary (=object) mapping layout names to a sequence of card coordinates:

var layoutDefs = {
    three: [0,0,'past', 1,0,'present', 2,0,'future'],

This worked well enough, but when it came to the Celtic Cross spread—which has one card laid crosswise—I realized this was too limited for the long term. So I rewrote the dealLayout function to instead use XML data stored in the SVG document itself:

<lo:layouts xmlns:lo="http://www.alleged.org.uk/pdc/tarot/">
    <lo:layout id="three">
        <lo:card col="0" row="0" title="past"></lo:card>
        <lo:card col="1" row="0" title="present"></lo:card>
        <lo:card col="2" row="0" title="future"></lo:card>

This goes in the defs section, along with the template for the cards. (I cannot find a reference in the SVG specification that makes it explicit that embedding random XML data in SVG is kosher, but it seems to be implied by wording on the SVG section of the W3C site.)

The JavaScript code uses the DOM conventions to read the information needed to lay out the cards. Using XML means that it was easy to add another attribute indicating a rotated card—and makes it easy to extend the format when adding new exotic layouts in future. For example, longer descriptions of the significance of each of the positions in the spread?

Converting questions to numbers

The querent’s question affects the deal through the "shuffling" process. The shuffling is controlled by a sequence of 78 pseudo-random numbers, and the pseudo-random number sequence in turn depends on a seed number. The function questionToSeed converts the character string in to a single number. The algorithm used is a simple one I found after a little time with Google. (Try g_str_hash X31_HASH).


The textbook I had at my side while doing the JavaScript programming is the rhino book: David Flanagan, JavaScript: The Definitive Guide 4/e (O’Reilly, Jan 2002).