select a card from a deck

The cardstories JavaScript client was modified to show feedback when selecting a card. The cards are made bigger and overlapping to improve readability. The author of the game can only chose between seven cards instead of 36.

DOM tree and jqDock

The jqDock JQuery widget used to display the cards does not allow for a complex DOM tree and insists on working on a list of img or a containing img. Although this is understandable to simplify the layout of the dock, it is inconvenient to provide the level of feedback required when selecting a card from a deck. A workaround demonstrated in the examples is used: the content of the a element is replaced after jqDock is done calculating the positions. It relies on the fact that jqDock will not re-evaluate the size and positions of the element displayed after calling the onReady callback. The jquery.cardstories.js code snippet is as follows:

var template = $('.cardstories_card_template', element);
options.onReady = function(is_ready) {
 var links = $('a.cardstories_card', element);
 var html = template.html();
 links.each(function(index) {
    link.html(html);
 ...

highlight, ok and cancel

When the user clicks on a card, it is highlighted by changing the background images showing behind it. The background image is positionned (using CSS zIndex) behind the card. It is white when it is not selected and turns blue when it is selected. The user is then prompted to confirm or cancel its choice. The choice shows only after the user clicked on the card to make it clear that an action is required. To avoid confusing movements of the deck, it is frozen (the cards won’t move) after a card is selected. When the user clicks on the ok button, a callback is invoqued with the name of the card and the dock is returned to its normal state via the nudge function. If the cancel button is clicked, the deck is returned to its normal state and the user is expected to pick another card.

        select_cards: function(cards, titles, ok, element) {
            var confirm = $('.cardstories_card_confirm', element);
            var middle = confirm.metadata({type: "attr", name: "data"}).middle;
            var confirm_callback = function(card, index, nudge, cards_element) {
                confirm.toggleClass('cardstories_card_confirm_right', index > middle).show();
                $('.cardstories_card_confirm_ok', confirm).unbind('click').click(function() {
                    confirm.hide();
                    ok(card);
                    nudge();
                });
                $('.cardstories_card_confirm_cancel', confirm).unbind('click').click(function() {
                    confirm.hide();
                    nudge();
                });
            };
            var hand = $('.cardstories_cards_hand', element);
            return this.display_or_select_cards(cards, titles, confirm_callback, hand);
        },

testing the card selection

The jqDock widget will asynchronously load the images and call the onReady function when it is done. When writing test functions, it means the actual test needs to wait for this event to happen. The select_or_display_card function creates a JQuery deferred that will be fired in the onReady callback. This deferred is returned by all the functions relying on it (create, create_pick_card, select_card) and the test can be chained to it with the done function:

test("display_or_select_cards move", function() {
    setup();
    stop();
    expect(2);

    var root = $('#qunit-fixture .cardstories');
    var element = $('.cardstories_create .cardstories_cards_hand', root);
    var onReady = function(is_ready) {
      var first = $('.cardstories_card:nth(1)', element);
      var offset = $(first).offset();
      var height = stabilize(first, offset.left, offset.top);
      var width = $(first).width();
      ok(stabilize(first, offset.left + width / 2, offset.top) > height, 'card is enlarged when moving toward the center');
      equal(stabilize(first, offset.left, offset.top), height, 'card is resized to the same size when the mouse goes back to the original position');
      start();
    };
    $.cardstories.
        display_or_select_cards([1,2,3,4,5,6],
                                ['1','2','3','4','5','6'],
                                function() {},
                                element).
        done(onReady);
  });

cards chosen by the author

Instead of displaying all 36 cards, 7 are drawn at random and displayed to the game author to chose from. If the author is unhappy about the possibilities, (s)he can start a new game and will be dealt new cards:

        create_deck: function() {
            var deck = [];
            for(var i = 1; i <= 36; i++) {
                deck.push(i);
            }
            var cards = [];
            for(var i = 0; i < 7; i++) {
                cards.push(deck.splice(Math.floor(Math.random() * deck.length), 1)[0]);
            }
            return cards;
        },