
So, I drank a whole pot of coffee yesterday. And it was a black, black, pot of coffee–like the soul of an exploded quasar, black. Twelve hours later (midnight), I was buzzing. So, what did I do? I wrote a jQuery library.
Check out the project on github: jquery.playingcards.
Check out a demo here. The demo just writes the cards to the page and allows you to click them to flip the cards. Will add buttons for the other behaviors (shuffle, pile, etc) soon.
How?
I used jQuery as a base (it’s a jquery plugin). However, this code could easily be extracted out to be used with any other JavaScript framework or even stand alone.
The cards are drawn mostly in CSS, using images only in the case of face cards–though, I have committed an open source card font that will eventually be implemented as a configuration option in place of the css/images. The images are courtesy of David Bellot with his awesome SVG card set.
Why?
- I wanted to use github since just about every cool project I’ve seen in the last 6 months is hosted there.
- I wanted to make a public jQuery plugin (made a few before but only proprietarily for companies so I couldn’t share them)
- When interviewing developer candidates, I frequently ask that the person code a deck of cards with a few simple methods (init, shuffle, deal) and I realized that although I had thought a lot about this problem and seen other peoples code, I hadn’t taken the time to write my own
- Doesn’t everyone need a deck of cards? Why not have one in your JavaScript arsenal?
Get Involved
There’s still a lot that can be added to this library. If you want to develop a game pack extension for it, let me know. Wouldn’t it be cool to have a generic deck of cards and set of rules for every card game known to man, all written in JavaScript? I think so.
ToDo
- Buttons for running demo methods (shuffle, deal, pile, etc)
- Handling for including card.ttf as a font replacement for the css images (if set in config)
- Keeping track of hands/players? — probably an extension along with game rules
- Game rule packs (maybe these will extend a board that keeps track of hands, players, score, etc
Let the games begin!
follow on Twitter
I just created my life plan (or the start of one anyway–I’ll be editing this thing for a while) and I figured I’d share with you the code used to make it.
My live updated version is on my Portfolio: The Future as I See It.
Demo
$maxYear){
$diffYears = $year – $maxYear;
$year = $maxYear; // this is what we will use in mktime() then we can add seconds to that output
$addtime = $diffYears * 31536000; // years of seconds
}
// count leap years
$numYears = $year – date(‘Y’);
$leaps = floor($numYears/4);
$addtime -= $leaps * 86400; // subtract these excess days (to get accurate year count)
$date = mktime(0,0,0,$month,$day,$year) + $addtime;
$now = time();
$days = ceil(($date – $now)/86400);
if($days < 365){
return 'in '.$days .' day'.($days==1?'':'s');
}else{
$years = floor($days/365);
$days = $days%365;
return 'in '.$years.' year'.($years==1?'':'s').', '.$days.' day'.($days==1?'':'s');
}
}
$events = array(
array(
'yyyy'=>‘2010’,’mm’=>’01’,’dd’=>’20’,
‘name’=>’Move to Magdeburg, Deutschland’,
‘info’=>’My family will be moving to Magdeburg to experience living in Germany and to strenghten our deutsche Sprachfähigkeiten.’,
‘type’=>’travel’
),
array(
‘yyyy’=>’2010′,’mm’=>’06’,’dd’=>’15’,
‘name’=>’Produce an iPhone App’,
‘info’=>’This might get done before I move to Magdeburg but this is my personal deadline for a v1.0 release.’,
‘type’=>’work’
),
array(
‘yyyy’=>’2011′,’mm’=>’01’,’dd’=>’20’,
‘name’=>’Move Back to Seattle, WA USA’,
‘info’=>’Unless we decide to stay in Germany or move elsewhere’,
‘type’=>’travel’
),
array(
‘yyyy’=>’2013′,’mm’=>’07’,’dd’=>’15’,
‘name’=>’Accomplish a Free-Standing Backflip’,
‘info’=>’I\’ve done it on a trampoline and with spotters but never alone without aid. This is a life-long goal.’,
‘type’=>’fitness’
),
array(
‘yyyy’=>’2017′,’mm’=>’09’,’dd’=>’01’,
‘name’=>’Publish First Novel’,
‘info’=>’Contact me if you want to be one of my pre-publication reviewers’,
‘type’=>’creative’
),
array(
‘yyyy’=>’2021′,’mm’=>’07’,’dd’=>’22’,
‘name’=>’Give First TED Talk’,
‘info’=>’On what topic? Web, security, social media, nano-technology, engineering, the future, productivity in an age of overabundance? I\’m not sure yet–but it will be awesome.’,
‘url’=>’http://ted.com’,
‘type’=>’social’
),
array(
‘yyyy’=>’2026′,’mm’=>’07’,’dd’=>’22’,
‘name’=>’Engineering Degree from MIT’,
‘info’=>’Oh, yes, I will be going back to school. Probably many times in my life.’,
‘type’=>’education’
),
array(
‘yyyy’=>’2044′,’mm’=>’11’,’dd’=>’17’,
‘name’=>’Undergo First Major Rejuvination Treatments’,
‘info’=>’Certainly there will be many improvements to personal health management before this date, but this is the anticipated date of my first major operation (or likely, full system nanobot integration)’,
‘type’=>’fitness’
),
array(
‘yyyy’=>’2079′,’mm’=>’11’,’dd’=>’05’,
‘name’=>’Engage Dyson Sphere’,
‘url’=>’http://en.wikipedia.org/wiki/Dyson_sphere’,
‘info’=>’If not on the team that deploys it, I will at least be celebrating the event :)’,
‘type’=>’social’
)
);
$types = array();
foreach($events as $event){
if(empty($types[$event[‘type’]])) $types[$event[‘type’]] = 1;
else $types[$event[‘type’]]++;
}
ksort($types);
$typeLinks = ”;
foreach($types as $key=>$val){
$typeLinks .= ‘‘.$key.’ (‘.$val.’)‘;
}
?>
- =$event['yyyy'].'.'.$event['mm'].'.'.$event['dd']?> =$event['name']?>
if(!empty($event['url'])) echo '[more info]’;
?> =getUntil($event['yyyy'],$event['mm'],$event['dd'])?>
=$event['info']?>
=$event['type']?>
}
?>
The PHP/HTML
Of course, you will need to make sure you include jQuery. I recommend using the Google Hosted Ajax Library rather than hosting it yourself. There are many good reasons to do this (blog post soon to come).
Just place this either in your HTML head or just before your closing body tag:
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script> |
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
I’ve used PHPDoc blocks to supply inline documentation throughout the code. Post a comment with any questions.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
| <?php
$pageName = 'plan';
$pageTitle = 'The Future as I See It';
include('header.php');
/**
* get the years and days until a date
* this function is a bit of a hack until my server supports PHP 5.3.0
* which has date_diff, alias of DateTime::diff (http://www.php.net/manual/en/datetime.diff.php)
*/
function getUntil($year,$month,$day){
$year = intval($year);
$maxYear = 2035;
$addtime = 0;
if($year > $maxYear){
$diffYears = $year - $maxYear;
$year = $maxYear;
$addtime = $diffYears * 31536000; // years of seconds
}
// count leap years
$numYears = $year - date('Y');
$leaps = floor($numYears/4);
$addtime -= $leaps * 86400; // subtract these excess days (to get accurate year count)
$date = mktime(0,0,0,$month,$day,$year) + $addtime;
$now = time();
$days = ceil(($date - $now)/86400);
if($days < 365){
return 'in '.$days .' day'.($days==1?'':'s');
}else{
$years = floor($days/365);
$days = $days%365;
return 'in '.$years.' year'.($years==1?'':'s').', '.$days.' day'.($days==1?'':'s');
}
}
$events = array(
array(
'yyyy'=>'2010','mm'=>'01','dd'=>'20',
'name'=>'Move to Magdeburg, Deutschland',
'info'=>'My family will be moving to Magdeburg to experience living in Germany and to strenghten our deutsche Sprachfähigkeiten.',
'type'=>'travel'
),
array(
'yyyy'=>'2010','mm'=>'06','dd'=>'15',
'name'=>'Produce an iPhone App',
'info'=>'This might get done before I move to Magdeburg but this is my personal deadline for a v1.0 release.',
'type'=>'work'
),
array(
'yyyy'=>'2013','mm'=>'07','dd'=>'15',
'name'=>'Accomplish a Free-Standing Backflip',
'info'=>'I\'ve done it on a trampoline and with spotters but never alone without aid. This is a life-long goal.',
'type'=>'fitness'
),
array(
'yyyy'=>'2017','mm'=>'09','dd'=>'01',
'name'=>'Publish First Novel',
'info'=>'Contact me if you want to be one of my pre-publication reviewers',
'type'=>'creative'
),
array(
'yyyy'=>'2021','mm'=>'07','dd'=>'22',
'name'=>'Give First TED Talk',
'info'=>'On what topic? Web, security, social media, nano-technology, engineering, the future, productivity in an age of overabundance? I\'m not sure yet--but it will be awesome.',
'url'=>'http://ted.com',
'type'=>'social'
),
array(
'yyyy'=>'2026','mm'=>'07','dd'=>'22',
'name'=>'Engineering Degree from MIT',
'info'=>'Oh, yes, I will be going back to school. Probably many times in my life.',
'type'=>'education'
),
array(
'yyyy'=>'2044','mm'=>'11','dd'=>'17',
'name'=>'Undergo First Major Rejuvination Treatments',
'info'=>'Certainly there will be many improvements to personal health management before this date, but this is the anticipated date of my first major operation (or likely, full system nanobot integration)',
'type'=>'fitness'
),
array(
'yyyy'=>'2079','mm'=>'11','dd'=>'05',
'name'=>'Engage Dyson Sphere',
'url'=>'http://en.wikipedia.org/wiki/Dyson_sphere',
'info'=>'If not on the team that deploys it, I will at least be celebrating the event :)',
'type'=>'social'
)
);
?>
<h1><?=$pageTitle?></h1>
<div id="pageFilter">
<a href="javascript:pageFilter('all')" class="active all" title="Show All">show all</a><?php
$types = array();
foreach($events as $event){
if(empty($types[$event['type']])) $types[$event['type']] = 1;
else $types[$event['type']]++;
}
ksort($types);
foreach($types as $key=>$val){
echo '<a href="javascript:pageFilter(\''.$key.'\')" class="'.$key.'" title="Show '.ucfirst($key).'">'.$key.' ('.$val.')</a>';
}
?>
</div>
<ul class="timeline">
<?php
foreach($events as $event){
?>
<li class="<?=$event['type']?>">
<div class="head">
<span class="date"><?=$event['yyyy'].'.'.$event['mm'].'.'.$event['dd']?></span>
<span class="name"><?=$event['name']?></span>
<?
if(!empty($event['url'])) echo '[<span class="url"><a href="'.$event['url'].'">more info</a></span>]';
?>
<span class="until"><?=getUntil($event['yyyy'],$event['mm'],$event['dd'])?></span>
</div>
<div class="body">
<div class="info"><?=$event['info']?></div>
<div class="type"><?=$event['type']?></div>
</div>
</li>
<?
}
?>
</ul>
<script type="text/javascript">
function pageFilter(type){
$('#pageFilter a').removeClass('active');
$('#pageFilter .'+type).addClass('active');
if(type=='all'){
$('.timeline li').slideDown();
}else{
$('.timeline li').slideUp();
$('li.'+type).slideDown();
}
}
$('.type').bind('click',function(){
pageFilter($(this).html());
});
</script>
<?php include('footer.php'); ?> |
<?php
$pageName = 'plan';
$pageTitle = 'The Future as I See It';
include('header.php');
/**
* get the years and days until a date
* this function is a bit of a hack until my server supports PHP 5.3.0
* which has date_diff, alias of DateTime::diff (http://www.php.net/manual/en/datetime.diff.php)
*/
function getUntil($year,$month,$day){
$year = intval($year);
$maxYear = 2035;
$addtime = 0;
if($year > $maxYear){
$diffYears = $year - $maxYear;
$year = $maxYear;
$addtime = $diffYears * 31536000; // years of seconds
}
// count leap years
$numYears = $year - date('Y');
$leaps = floor($numYears/4);
$addtime -= $leaps * 86400; // subtract these excess days (to get accurate year count)
$date = mktime(0,0,0,$month,$day,$year) + $addtime;
$now = time();
$days = ceil(($date - $now)/86400);
if($days < 365){
return 'in '.$days .' day'.($days==1?'':'s');
}else{
$years = floor($days/365);
$days = $days%365;
return 'in '.$years.' year'.($years==1?'':'s').', '.$days.' day'.($days==1?'':'s');
}
}
$events = array(
array(
'yyyy'=>'2010','mm'=>'01','dd'=>'20',
'name'=>'Move to Magdeburg, Deutschland',
'info'=>'My family will be moving to Magdeburg to experience living in Germany and to strenghten our deutsche Sprachfähigkeiten.',
'type'=>'travel'
),
array(
'yyyy'=>'2010','mm'=>'06','dd'=>'15',
'name'=>'Produce an iPhone App',
'info'=>'This might get done before I move to Magdeburg but this is my personal deadline for a v1.0 release.',
'type'=>'work'
),
array(
'yyyy'=>'2013','mm'=>'07','dd'=>'15',
'name'=>'Accomplish a Free-Standing Backflip',
'info'=>'I\'ve done it on a trampoline and with spotters but never alone without aid. This is a life-long goal.',
'type'=>'fitness'
),
array(
'yyyy'=>'2017','mm'=>'09','dd'=>'01',
'name'=>'Publish First Novel',
'info'=>'Contact me if you want to be one of my pre-publication reviewers',
'type'=>'creative'
),
array(
'yyyy'=>'2021','mm'=>'07','dd'=>'22',
'name'=>'Give First TED Talk',
'info'=>'On what topic? Web, security, social media, nano-technology, engineering, the future, productivity in an age of overabundance? I\'m not sure yet--but it will be awesome.',
'url'=>'http://ted.com',
'type'=>'social'
),
array(
'yyyy'=>'2026','mm'=>'07','dd'=>'22',
'name'=>'Engineering Degree from MIT',
'info'=>'Oh, yes, I will be going back to school. Probably many times in my life.',
'type'=>'education'
),
array(
'yyyy'=>'2044','mm'=>'11','dd'=>'17',
'name'=>'Undergo First Major Rejuvination Treatments',
'info'=>'Certainly there will be many improvements to personal health management before this date, but this is the anticipated date of my first major operation (or likely, full system nanobot integration)',
'type'=>'fitness'
),
array(
'yyyy'=>'2079','mm'=>'11','dd'=>'05',
'name'=>'Engage Dyson Sphere',
'url'=>'http://en.wikipedia.org/wiki/Dyson_sphere',
'info'=>'If not on the team that deploys it, I will at least be celebrating the event :)',
'type'=>'social'
)
);
?>
<h1><?=$pageTitle?></h1>
<div id="pageFilter">
<a href="javascript:pageFilter('all')" class="active all" title="Show All">show all</a><?php
$types = array();
foreach($events as $event){
if(empty($types[$event['type']])) $types[$event['type']] = 1;
else $types[$event['type']]++;
}
ksort($types);
foreach($types as $key=>$val){
echo '<a href="javascript:pageFilter(\''.$key.'\')" class="'.$key.'" title="Show '.ucfirst($key).'">'.$key.' ('.$val.')</a>';
}
?>
</div>
<ul class="timeline">
<?php
foreach($events as $event){
?>
<li class="<?=$event['type']?>">
<div class="head">
<span class="date"><?=$event['yyyy'].'.'.$event['mm'].'.'.$event['dd']?></span>
<span class="name"><?=$event['name']?></span>
<?
if(!empty($event['url'])) echo '[<span class="url"><a href="'.$event['url'].'">more info</a></span>]';
?>
<span class="until"><?=getUntil($event['yyyy'],$event['mm'],$event['dd'])?></span>
</div>
<div class="body">
<div class="info"><?=$event['info']?></div>
<div class="type"><?=$event['type']?></div>
</div>
</li>
<?
}
?>
</ul>
<script type="text/javascript">
function pageFilter(type){
$('#pageFilter a').removeClass('active');
$('#pageFilter .'+type).addClass('active');
if(type=='all'){
$('.timeline li').slideDown();
}else{
$('.timeline li').slideUp();
$('li.'+type).slideDown();
}
}
$('.type').bind('click',function(){
pageFilter($(this).html());
});
</script>
<?php include('footer.php'); ?>
The CSS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
| #pageFilter {
font-size:.8em;
text-align:right;
margin-top:10px;
}
#pageFilter a{
border:1px solid #dde;
padding:3px 5px;
margin-left:5px;
}
.timeline {
list-style:none;
font-size:.8em;
}
.timeline li{
margin-top:10px;
border:1px solid #9cf;
}
.timeline .head {
background-color:#def;
padding:3px 5px;
}
.timeline .body {
padding:10px;
position:relative;
}
.timeline .date{
color:#036;
font-weight:bold;
margin-right:10px;
}
.timeline .name{
color:#666;
}
.timeline .until{
float:right;
}
.timeline .info{
padding-bottom:15px;
}
.timeline .type{
position:absolute;
bottom:0;
right:0;
border:1px solid #dde;
background-color:#ffe;
padding:3px 3px 3px 5px;
cursor:pointer;
}
a.all, .timeline .all .type{
background-color:#FFFFFF;
}
a.creative, .timeline .creative .type{
background-color:#F0FFFD;
}
a.education, .timeline .education .type{
background-color:#EEF4FF;
}
a.fitness, .timeline .fitness .type{
background-color:#F2EEFE;
}
a.social, .timeline .social .type{
background-color:#FDF5F5;
}
a.travel, .timeline .travel .type{
background-color:#FFFDED;
}
a.work, .timeline .work .type{
background-color:#F3FFEB;
} |
#pageFilter {
font-size:.8em;
text-align:right;
margin-top:10px;
}
#pageFilter a{
border:1px solid #dde;
padding:3px 5px;
margin-left:5px;
}
.timeline {
list-style:none;
font-size:.8em;
}
.timeline li{
margin-top:10px;
border:1px solid #9cf;
}
.timeline .head {
background-color:#def;
padding:3px 5px;
}
.timeline .body {
padding:10px;
position:relative;
}
.timeline .date{
color:#036;
font-weight:bold;
margin-right:10px;
}
.timeline .name{
color:#666;
}
.timeline .until{
float:right;
}
.timeline .info{
padding-bottom:15px;
}
.timeline .type{
position:absolute;
bottom:0;
right:0;
border:1px solid #dde;
background-color:#ffe;
padding:3px 3px 3px 5px;
cursor:pointer;
}
a.all, .timeline .all .type{
background-color:#FFFFFF;
}
a.creative, .timeline .creative .type{
background-color:#F0FFFD;
}
a.education, .timeline .education .type{
background-color:#EEF4FF;
}
a.fitness, .timeline .fitness .type{
background-color:#F2EEFE;
}
a.social, .timeline .social .type{
background-color:#FDF5F5;
}
a.travel, .timeline .travel .type{
background-color:#FFFDED;
}
a.work, .timeline .work .type{
background-color:#F3FFEB;
}
follow on Twitter