User:Kruhly/jqCountingCardGame.js

//Created by Robert J. Kruhlak //License: CC-BY //Inspiration: Elated.com //Modification of Drag and Drop and Card Style from Elated.com //See http://www.elated.com/res/File/articles/development/javascript/jquery/ // drag-and-drop-with-jquery-your-essential-guide/card-game.html


 * (function($){

var symbols=['&clubs;', '&hearts;', '&spades;', '&diams;', '&loz;', '&nabla;', '&Delta;', '&infin;']; var myNumbers = [ 1, 2, 3, 4, 5, 6,7,8,9,10,11,12,13,14,15, 16,17,18,19,20];

$.fn.countingCardGame = function(options) {

var defaults = { numbers: myNumbers, // Numbers used for matching and counting __correctCards: 0, // Initialize to zero, increment when cards are placed correctly __incorrectCards: 0, cardColor: 'purple', cardPileLabel: 'Take a card', slotsTableColor: 'blue', lowCard: 1, highCard: 1, minCard: 1, maxCard: myNumbers.length, minColumns: 1, maxColumns: 7, cardColumns: 5, symbolIndex: 1, symbols: symbols, shufflePictures: true, shufflePicturesLabel: 'Shuffle Pictures', shuffleNumbers: true, shuffleNumbersLabel: 'Shuffle Numbers', successMessage: 'You did it!', randIds: 1, animate: true, };     if (options){ checkIntOptions(defaults, options); options.minColumns = checkMinMax(options.minColumns,options.maxColumns); options.lowCard = checkMinMax(options.lowCard,options.highCard); }     var options = $.extend(defaults, options);

// support mutltiple elements if (this.length > 1){ this.each(function {       $(this).countingCardGame(options)       }); return this; }     var ids = { debug: 0, obj: this.attr('id'), license: 'CC-BY', functionPanel: 'functionPanel', slot: 'slot', slotsTable: 'slotsTable', cardTable: 'cardTable', cardTableRow: 'cardTableRow', cell: 'cell', card: 'card', cardFace: 'cardFace', cardPile:  'cardPile', panelList: 'panelList', shufflePictures: 'shufflePicts', shuffleNumbers: 'shuffleNums', shuffleFaces: 'shuffleFaces', resetButton: 'resetButton', rangeSlider: 'slider-range', lowCard:  'low', highCard: 'high', columnsSlider: 'columnsSlider', columnsSliderLabel: 'cardTableColumns', symbolsSlider: 'symbolsSlider', symbolsSliderLabel: 'symbol', successMessage: 'successMessage', };     //Prepend a random string to ids so more than one game // can be put on a page. Needs to be done before // other objects are populated with ids.

if (options.randIds){ ids=prependRandomString(ids);}

var labels = {

cardPile: options.cardPileLabel, shufflePictures: options.shufflePicturesLabel, shuffleNumbers: options.shuffleNumbersLabel, lowCard: 'Low Card', highCard: 'High Card', resetButton: 'Reset', shuffleFaces:  'Suffle Faces', columnsSlider: 'Columns: ', symbolsSlider: 'Symbol: ', };

var symbolsSlider = { min: 0, max: (symbols.length-1), value: options.symbolIndex, id: ids.symbolsSlider, labelId: ids.symbolsSliderLabel, label: labels.symbolsSlider, symbols: symbols, };

var columnsSlider = { min: options.minColumns, max: options.maxColumns, value: options.cardColumns, id: ids.columnsSlider, labelId: ids.columnsSliderLabel, label: labels.columnsSlider, };     var sliders = {'columns': columnsSlider, 'symbols': symbolsSlider};

this.getOptions = function { return options; };

this.printOptions = function{ alert(dump(options)); };

this.getIds = function { return ids; };     this.getLabels = function { return labels; };

this.getSliders = function { return sliders; };

//Private functions //UI // Card faces are added to cardSlots function addCardFace(id, num, symbol, opts, ids){

var visArray=[];

for (var i in opts.numbers){ visArray.push(0); }       // set visible symbols for (i=0; i<num; i++){ visArray[i]=1; }       // Randomize the placement of the visible symbols visArray=fisherYates(visArray);

// need to send number as data for droppable code $(' ').data( 'number', num ) .attr( 'id', ids.cardFace + id ) .appendTo('#' + ids.slot+ id);

// A card face can hold 20 symbols five rows by 4 columns // is the max that will fit in a slot var maxFaceRows = 5;     // May be able to generalize this var maxFaceColumns = 4;

for (j=0; j<maxFaceRows; j++){ $(' ').attr('id', ids.cardFace+ id +'r' + j)                        .appendTo('#' + ids.cardFace + id);

for (i = j*maxFaceColumns; i < ((j+1)*maxFaceColumns); i++){ $(' '+symbol+'  ').attr('id', ids.cardFace + id + 'c' + i)                                                .appendTo('#' + ids.cardFace + id +'r' + j); // above is necessary because drag and drop don't play well with tables. $('#' + ids.cardFace +id+'c'+i).addClass("hidden"); if (symbol == "&hearts;" || symbol == "&diams;" || symbol == "&loz;" || symbol == "&Delta;" ){ $('#' + ids.cardFace + id + 'c' + i).addClass("red"); }            if (visArray[i]){ $('#' + ids. cardFace + id + 'c' + i).toggleClass("hidden"); }         }        }      };

function addCardSymbol(ind,symbols){

if (parseInt(ind)>=0 && parseInt(ind)< symbols.length ){ return symbols[ind]; }else{ return symbols[0]; }     };

function addCardSlots( picts, ids, opts ){

var myMin = $('#' + ids.lowCard).val; var myMax = $('#' + ids.highCard).val; picts = picts.slice((myMin-1),myMax); var myColumns = $( '#' + ids.columnsSlider ).slider( 'value' ); var myRows = Math.ceil(picts.length/myColumns);

if ($('#' + ids.shufflePictures).is(':checked')){ picts=fisherYates(picts); }

resizeUI(myRows,myColumns,ids); removeHTML(ids.cardTable); createCardSlots(picts, myRows, myColumns, ids, opts);

if (ids.debug){ alert($('#' + ids.obj).css('height'))} };

function resizeUI(myRows, myColumns, ids){ //Used when card columns, number of cards change. var sTableWidth=98*myColumns; var sTableHeight = 142*myRows; var contentWidth = 315+sTableWidth; var contentHeight = 30+ sTableHeight;

$('#' + ids.slotsTable).css('width', sTableWidth + 'px'); $('#' + ids.obj).css({'width': contentWidth + 'px', 'height': contentHeight + 'px'}); $('#' + ids.obj).css({'min-width': contentWidth + 'px'});

};     function createCardSlots( picts, myRows, myColumns, ids, opts){

var pictIndex=0; var correctCards = opts.__correctCards; for ( ii = 0; ii < myRows; ii++) {

$(' ').attr('id', ids.cardTableRow + ii).appendTo('#' + ids.cardTable);

for ( jj=(ii*(myRows-1)); jj < ((ii*(myRows-1)) + myColumns); jj++ ) { if (pictIndex < picts.length) { $(' ').attr('id', ids.cell + pictIndex).appendTo('#' + ids.cardTableRow + ii); $(' ').data( 'number', picts[pictIndex]) .data( 'ids', ids ) .data( 'opts', opts) .attr( 'id', ids.slot + picts[pictIndex] ) .appendTo( '#' + ids.cell + pictIndex ) .droppable( {                                            accept: '#' + ids.cardPile + ' div',                                             hoverClass: 'hovered',                                             drop: handleCardDrop                                           } ); var myPict = picts[pictIndex]; addCardFace(myPict, myPict, addCardSymbol($( '#' + ids.symbolsSlider ).slider( 'value' ), opts.symbols),             opts,ids); }           pictIndex=pictIndex+1; }      }

};

function addCardPile(numbers, ids, opts){

removeHTML(ids.cardPile); numbers=numbers.slice(($('#' + ids.lowCard).val-1), $('#' + ids.highCard).val); numbers=numbers.reverse; // reverse so 1 starts when unshuffled

if ($('#' + ids.shuffleNumbers).is(':checked')){ numbers=fisherYates(numbers); }      return createCardPile(numbers, ids, opts);

};     function animateTopCard(myShift,myDuration){ $('.animate').animate({'marginLeft': myShift, 'marginTop': -myShift},myDuration, 'swing') .animate({'marginLeft':'10px', 'marginTop': '10px'}, myDuration, 'swing', function {               animateTopCard(myShift,myDuration);}); };

function stopAnimation{

$('.animate').stop(true,true) .animate({'marginLeft':'10', 'marginTop' : '10'},1000) .stop(true,true).removeClass('animate');

};

function addLicense(id){ $('#' + id).html('').css({'padding': '10px'}) };

function createCardPile( numbers, ids, opts ){ var cPId = '#' + ids.cardPile; var cardId = ids.card; var topCardId = '#'+cardId+numbers[numbers.length-1]; if (ids.debug){alert($('#' + ids.obj).attr('id'));}

for ( var i in numbers) { $(' ' + numbers[i] + ' ').data( 'number', numbers[i] ) .attr( 'id', cardId + numbers[i] ,'z-index',i,'position','absolute') .css( {'background': opts.cardColor}) .appendTo( cPId).draggable( {             containment: $('#' + ids.obj),              stack: cPId + ' div',              cursor: 'move',              revert: true          } ); }       $(topCardId).addClass('animate').hover(function{stopAnimation;}); return '#'+cardId+numbers[numbers.length-1]; };

function createFunctionPanel(ids, opts, labels){ // will be the right panel in UI       var cPId=ids.cardPile; return $(' ').addClass( 'functionPanel') .attr('id', ids.functionPanel) .append(myLabel(cPId, labels.cardPile)) .append(myDiv(cPId).addClass('cardPile')) .append(createPanelList(ids,opts,labels)); };

function createPanelList(ids,opts,labels){

return myList(ids.panelList) .append(addCheckboxToList(ids.shufflePictures, labels.shufflePictures,opts.shufflePictures)) .append(addCheckboxToList(ids.shuffleNumbers, labels.shuffleNumbers,opts.shuffleNumbers)) .append(addButtonToList(ids.resetButton,labels.resetButton,'resetButton')) .append(addInputTextboxToList(ids.lowCard, labels.lowCard)) .append(addInputTextboxToList(ids.highCard, labels.highCard)) .append(addDiv(ids.rangeSlider)) .append(addP(ids.symbolsSliderLabel)) .append(addDiv(ids.symbolsSlider)) .append(addP(ids.columnsSliderLabel)) .append(addDiv(ids.columnsSlider)) .append(addDiv(ids.license)); };

function createSuccessMessage(ids, opts){

$(' ').appendTo($('#' + ids.obj)) .attr('id', ids.successMessage) .css('position', 'absolute') .addClass('successMessage') .append($(' '+opts.successMessage +' ')) .append($(' ').attr('id','stats')) .append( $(' ')                            .html('Play Again')                            .click({'ids': ids, 'opts': opts}, handleClick)                          );

hideSuccessMessage(ids); };

function hideSuccessMessage(ids){ // Hide the success message var $sMId = $('#' + ids.successMessage); var $objId = $('#' + ids.obj); $sMId.hide; $sMId.css( {         'top': $objId.position.top,          'left': $objId.position.left,          'width': '0',          'height': '0',          } ); };

// Event Handlers

function handleCardDrop( event, ui ) { var slotNumber = $(this).data( 'number' ); var cardNumber = ui.draggable.data( 'number' ); var ids= $(this).data( 'ids'); var opts = $(this).data('opts'); // currently sent as data // because handleCardDrop can't take any other parameters

var $sMId =$('#' + ids.successMessage);

// If the card was dropped to the correct slot, // change the card colour, position it directly // on top of the slot, and prevent it being dragged // again if ( slotNumber == cardNumber ) { ui.draggable.addClass( 'correct' ); ui.draggable.draggable( 'disable' ); $(this).droppable( 'disable' ); ui.draggable.position( { of: $(this), my: 'left top', at: 'left top' } ); ui.draggable.draggable( 'option', 'revert', false ); opts.__correctCards++;

} else { opts.__incorrectCards++; }

// If all the cards have been placed correctly then display a message // and reset the cards for another go

if ( opts.__correctCards == ($('#' + ids.highCard).val-$('#' + ids.lowCard).val+1)) { $('#stats').html('Correct: ' + opts.__correctCards + ' Incorrect: ' + opts.__incorrectCards); $sMId.show;

var $objId = $('#'+ ids.obj);

$sMId.animate( {           left: $objId.position.left + $objId.width/4,            top: $objId.position.top + $objId.height/4,            width: '300px',            height: '130px',            opacity: 1          } ); }

};

function setHandleClick(id, ids, opts){ // ids, and opts needed as parameters for handleClick // so that the game can be updated properly. $('#' + id).click({'ids': ids, 'opts': opts}, handleClick); };

function updateSlider { var ids = $(this).data('ids'); var opts = $(this).data('opts'); if (ids.debug) {alert( 'Check a value of ids array ' +ids.cardPile);} if (ids.debug) {alert('Check a value of opts array ' + opts.numbers);} resetGame(opts,ids); };

function handleClick(event){ ids=event.data.ids; opts = event.data.opts; if (ids.debug){ alert('HandleClick event.data.ids.cardPile=' + ids.cardPile);} if (ids.debug) {alert('HandleClick event.data.opts.numbers' + opts.numbers);} resetGame(opts,ids); };

function resetGame(opts,ids){ // Reset the game stopAnimation; hideSuccessMessage(ids,opts); opts.__correctCards = 0; opts.__incorrectCards = 0; var card1Id=addCardPile(opts.numbers,ids, opts); addCardSlots(opts.numbers,ids, opts);

if (opts.animate) { jQuery.fx.interval = 50; $('.animate').delay(1000).animate({'marginLeft':'10px'},50); animateTopCard(-40,2000); }     };

//Sliders

function createSlider( o, ids, opts){

var $id = $('#'+ o.id).data('ids', ids) .data('opts', opts); var $labelId = $('#'+ o.labelId);

$id.slider({             min: o.min,              max: o.max,              value: o.value,              slide: function(event, ui) {                  $labelId.html(o.label + ui.value);              },              stop: updateSlider,          }); //Render initial value $labelId.html( o.label + $id.slider( 'value' ) ); // In this instance it is a          $id.find('a').css({'background':'transparent','border':'none','text-align':'center','text-decoration':'none'}).html('&#9660;');

};

function createSymbolSlider( o, ids, opts){

var $id = $('#'+ o.id).data('ids', ids) .data('opts', opts); var $labelId = $('#'+ o.labelId);

$id.slider({             min: o.min,              max: o.max,              value: o.value,              slide: function(event, ui) {                  $labelId.html(o.label + addCardSymbol(ui.value, opts.symbols));              },              stop: updateSlider,          }); //Render initial value $labelId.html( o.label + addCardSymbol( $id.slider( 'value' ), opts.symbols )); // In this instance it is          $id.find('a').css({'background':'transparent','border':'none','text-align':'center','text-decoration':'none'}).html('&#9650;');

};

function createRangeSlider(o,ids, opts){

var $sliderId = $('#' + ids.rangeSlider ).data('ids', ids) .data('opts', opts); var $lowId = $( '#' + ids.lowCard ); var $highId = $( '#' + ids.highCard );

$sliderId.slider({ 			  range: true,  			  min: o.minCard,  			  values: [ o.lowCard, o.highCard ],  			  max: o.maxCard,          slide: function( event, ui ) {  			     $lowId.val( ui.values[ 0 ] );             $highId.val( ui.values[ 1 ] );  			     },          stop: updateSlider  		  }); // Render initial values $lowId.val( $sliderId.slider( "values", 0 )).attr('disabled',true); $highId.val( $sliderId.slider( "values", 1 )).attr('disabled',true); $sliderId.find($('a')[0]).css({'background':'transparent no-repeat','border':'none','text-align':'left','text-decoration':'none'}).html('&#9654;'); $sliderId.find($('a')[1]).css({'background':'transparent','border':'none','text-align':'right','text-decoration':'none'}).html('&#9664;');

};

//Utilities function dump(obj) { var out = ''; for (var key in obj) { out += key + ": " + obj[key] + "\n"; }       return out; };     function checkMinMax(low,high){ if (low > high){ return high; }     };

function checkIntOptions(defaults, options){ for (var key in defaults){ switch(key){ case 'minCard': case 'maxCard': case 'lowCard': case 'highCard': case '__correctCards': case '__incorrectCards': case 'minColumns': case 'maxColumns': case 'cardColumns': case 'symbolIndex': case 'randIds': if (key in options) { if (!isInt(options[key])){ options[key]=defaults[key]; console.log(key + ' is not an integer. Reverting to default.'); alert(key + 'is not an integer. Reverting to default.'); } else { options[key]=Math.abs(options[key]); }             }              break; default:

if (key in options){ console.log(key + ' is ' + options[key]); }         }        }      };

function isInt(val){ //regex options /^[0-9]+$/.test(val); if((parseFloat(val) == parseInt(val)) && !isNaN(val)){ return true; } else { return false; }     };

function prependRandomString(ids){

var randomString = 'R'+Math.floor(10000*Math.random).toString;

for (var key in ids){ if (key != 'debug' && key !='obj' && key != 'license'){ ids[key] = randomString + ids[key]; }         }        return ids; };

function removeHTML(id){ $('#' + id).html( '' ).removeData; };     function myTable(id){ return $(' ').attr('id', id); };

function myLabel(id,text){ return $(' ').html(text); };

function myDiv(id){ return $(' ').attr( 'id', id); };

function myP(id){ return $(' ').attr( 'id', id); };

function myList(id){ return $('').attr( 'id', id) .css({'list-style-image':'none','list-style-type':'none','padding': '10'}); };

function addDiv (id){ return $('').append(myDiv(id)); };

function addP(id){ return $('').append(myDiv(id)); };

function addCheckboxToList(id,text,checked){

if (checked){ var $in=$('').attr('id',id); } else { var $in=$(' ').attr('id',id); }         return $('').append(                          $in                       ).append(                          $(''+text+' ')                       ); };

function addButtonToList(id, text, myClass){ return $('').append(                                 $('  ').attr('id', id).addClass(myClass).html(text)                              ); };

function addInputTextboxToList( id, text){ return $('</li>').append(                                 $(' ' + text + ' ')                               ).append(       	                        $('<input type="text" id=' + id + '  maxlength="4" size="4"/>')                               ); };

function fisherYates(myArray){ //Randomizes the order of the array. var i = myArray.length; while(i--){ var j=Math.floor(Math.random*(i+1)); var tempi=myArray[i]; myArray[i]=myArray[j]; myArray[j]=tempi; }       return myArray; };

//Initialization function initialize(obj){ var opts=obj.getOptions; var ids = obj.getIds; var labels = obj.getLabels; var sliders= obj.getSliders;

obj.append(myDiv(ids.slotsTable)            .append(myTable(ids.cardTable)));

$('#'+ids.slotsTable).addClass('slotsTable') .css({'background': opts.slotsTableColor}); $('#'+ids.cardTable).addClass('cardTable') .css({'background': opts.slotsTableColor});

obj.append(createFunctionPanel(ids, opts, labels)); createSlider( sliders.columns, ids, opts); createSymbolSlider( sliders.symbols,ids, opts); createRangeSlider(opts,ids, opts); createSuccessMessage(ids, opts); addLicense(ids.license); setHandleClick(ids.resetButton, ids, opts); setHandleClick(ids.shufflePictures, ids, opts); setHandleClick(ids.shuffleNumbers, ids, opts);

// Create the pile of shuffled cards var card1Id=addCardPile(opts.numbers, ids, opts);

// Add cards to Table

addCardSlots(opts.numbers, ids, opts);

// flesh out the game area // in the event that the game is attached // to a zero height div. var cssObj = {'min-height' : '520px', 'min-width' : '800px', 'text-align': 'center', '-moz-user-select': 'none', '-webkit-user-select': 'none', 'user-select' : 'none', 'margin' : '0 auto 0 20px', };

obj.css(cssObj); if (opts.animate) { jQuery.fx.interval = 50; $('.animate').delay(1000); animateTopCard(-40,2000); };

return obj; };

return initialize(this); } })(jQuery);