punchcard 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/VERSION +1 -1
- data/lib/public/js/app.js +10 -0
- data/lib/public/js/jquery.timeago.js +141 -0
- data/lib/punchcard/person.rb +1 -1
- data/lib/punchcard.rb +2 -1
- data/lib/views/layout.haml +4 -1
- data/lib/views/screen.sass +8 -4
- data/punchcard.gemspec +2 -1
- metadata +3 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.2
|
data/lib/public/js/app.js
CHANGED
@@ -1,9 +1,19 @@
|
|
1
1
|
// Update status for all people
|
2
2
|
function update_status() {
|
3
|
+
var in_office = [];
|
3
4
|
$.getJSON('/status.json', function(json) {
|
4
5
|
$.each(json, function(index, person) {
|
5
6
|
update_person(person);
|
6
7
|
});
|
8
|
+
$('abbr.timeago').timeago();
|
9
|
+
|
10
|
+
title = 'Punchcard.'
|
11
|
+
if ($('.gravatar.pending').size() > 0) {
|
12
|
+
title = 'In office: ' + $('.gravatar.pending').map(function() {
|
13
|
+
return $(this).attr('title');
|
14
|
+
}).get().join(', ');
|
15
|
+
}
|
16
|
+
$('head title').html(title);
|
7
17
|
});
|
8
18
|
};
|
9
19
|
|
@@ -0,0 +1,141 @@
|
|
1
|
+
/*
|
2
|
+
* timeago: a jQuery plugin, version: 0.9.2 (2010-09-14)
|
3
|
+
* @requires jQuery v1.2.3 or later
|
4
|
+
*
|
5
|
+
* Timeago is a jQuery plugin that makes it easy to support automatically
|
6
|
+
* updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
|
7
|
+
*
|
8
|
+
* For usage and examples, visit:
|
9
|
+
* http://timeago.yarp.com/
|
10
|
+
*
|
11
|
+
* Licensed under the MIT:
|
12
|
+
* http://www.opensource.org/licenses/mit-license.php
|
13
|
+
*
|
14
|
+
* Copyright (c) 2008-2010, Ryan McGeary (ryanonjavascript -[at]- mcgeary [*dot*] org)
|
15
|
+
*/
|
16
|
+
(function($) {
|
17
|
+
$.timeago = function(timestamp) {
|
18
|
+
if (timestamp instanceof Date) return inWords(timestamp);
|
19
|
+
else if (typeof timestamp == "string") return inWords($.timeago.parse(timestamp));
|
20
|
+
else return inWords($.timeago.datetime(timestamp));
|
21
|
+
};
|
22
|
+
var $t = $.timeago;
|
23
|
+
|
24
|
+
$.extend($.timeago, {
|
25
|
+
settings: {
|
26
|
+
refreshMillis: 60000,
|
27
|
+
allowFuture: false,
|
28
|
+
strings: {
|
29
|
+
prefixAgo: null,
|
30
|
+
prefixFromNow: null,
|
31
|
+
suffixAgo: "ago",
|
32
|
+
suffixFromNow: "from now",
|
33
|
+
seconds: "less than a minute",
|
34
|
+
minute: "about a minute",
|
35
|
+
minutes: "%d minutes",
|
36
|
+
hour: "about an hour",
|
37
|
+
hours: "about %d hours",
|
38
|
+
day: "a day",
|
39
|
+
days: "%d days",
|
40
|
+
month: "about a month",
|
41
|
+
months: "%d months",
|
42
|
+
year: "about a year",
|
43
|
+
years: "%d years",
|
44
|
+
numbers: []
|
45
|
+
}
|
46
|
+
},
|
47
|
+
inWords: function(distanceMillis) {
|
48
|
+
var $l = this.settings.strings;
|
49
|
+
var prefix = $l.prefixAgo;
|
50
|
+
var suffix = $l.suffixAgo;
|
51
|
+
if (this.settings.allowFuture) {
|
52
|
+
if (distanceMillis < 0) {
|
53
|
+
prefix = $l.prefixFromNow;
|
54
|
+
suffix = $l.suffixFromNow;
|
55
|
+
}
|
56
|
+
distanceMillis = Math.abs(distanceMillis);
|
57
|
+
}
|
58
|
+
|
59
|
+
var seconds = distanceMillis / 1000;
|
60
|
+
var minutes = seconds / 60;
|
61
|
+
var hours = minutes / 60;
|
62
|
+
var days = hours / 24;
|
63
|
+
var years = days / 365;
|
64
|
+
|
65
|
+
function substitute(stringOrFunction, number) {
|
66
|
+
var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
|
67
|
+
var value = ($l.numbers && $l.numbers[number]) || number;
|
68
|
+
return string.replace(/%d/i, value);
|
69
|
+
}
|
70
|
+
|
71
|
+
var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
|
72
|
+
seconds < 90 && substitute($l.minute, 1) ||
|
73
|
+
minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
|
74
|
+
minutes < 90 && substitute($l.hour, 1) ||
|
75
|
+
hours < 24 && substitute($l.hours, Math.round(hours)) ||
|
76
|
+
hours < 48 && substitute($l.day, 1) ||
|
77
|
+
days < 30 && substitute($l.days, Math.floor(days)) ||
|
78
|
+
days < 60 && substitute($l.month, 1) ||
|
79
|
+
days < 365 && substitute($l.months, Math.floor(days / 30)) ||
|
80
|
+
years < 2 && substitute($l.year, 1) ||
|
81
|
+
substitute($l.years, Math.floor(years));
|
82
|
+
|
83
|
+
return $.trim([prefix, words, suffix].join(" "));
|
84
|
+
},
|
85
|
+
parse: function(iso8601) {
|
86
|
+
var s = $.trim(iso8601);
|
87
|
+
s = s.replace(/\.\d\d\d+/,""); // remove milliseconds
|
88
|
+
s = s.replace(/-/,"/").replace(/-/,"/");
|
89
|
+
s = s.replace(/T/," ").replace(/Z/," UTC");
|
90
|
+
s = s.replace(/([\+-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
|
91
|
+
return new Date(s);
|
92
|
+
},
|
93
|
+
datetime: function(elem) {
|
94
|
+
// jQuery's `is()` doesn't play well with HTML5 in IE
|
95
|
+
var isTime = $(elem).get(0).tagName.toLowerCase() == "time"; // $(elem).is("time");
|
96
|
+
var iso8601 = isTime ? $(elem).attr("datetime") : $(elem).attr("title");
|
97
|
+
return $t.parse(iso8601);
|
98
|
+
}
|
99
|
+
});
|
100
|
+
|
101
|
+
$.fn.timeago = function() {
|
102
|
+
var self = this;
|
103
|
+
self.each(refresh);
|
104
|
+
|
105
|
+
var $s = $t.settings;
|
106
|
+
if ($s.refreshMillis > 0) {
|
107
|
+
setInterval(function() { self.each(refresh); }, $s.refreshMillis);
|
108
|
+
}
|
109
|
+
return self;
|
110
|
+
};
|
111
|
+
|
112
|
+
function refresh() {
|
113
|
+
var data = prepareData(this);
|
114
|
+
if (!isNaN(data.datetime)) {
|
115
|
+
$(this).text(inWords(data.datetime));
|
116
|
+
}
|
117
|
+
return this;
|
118
|
+
}
|
119
|
+
|
120
|
+
function prepareData(element) {
|
121
|
+
element = $(element);
|
122
|
+
if (!element.data("timeago")) {
|
123
|
+
element.data("timeago", { datetime: $t.datetime(element) });
|
124
|
+
var text = $.trim(element.text());
|
125
|
+
if (text.length > 0) element.attr("title", text);
|
126
|
+
}
|
127
|
+
return element.data("timeago");
|
128
|
+
}
|
129
|
+
|
130
|
+
function inWords(date) {
|
131
|
+
return $t.inWords(distance(date));
|
132
|
+
}
|
133
|
+
|
134
|
+
function distance(date) {
|
135
|
+
return (new Date().getTime() - date.getTime());
|
136
|
+
}
|
137
|
+
|
138
|
+
// fix for IE6 suckage
|
139
|
+
document.createElement("abbr");
|
140
|
+
document.createElement("time");
|
141
|
+
})(jQuery);
|
data/lib/punchcard/person.rb
CHANGED
@@ -33,7 +33,7 @@ class Person < ActiveRecord::Base
|
|
33
33
|
:email => email,
|
34
34
|
:gravatar_url => gravatar_url(:size => 80),
|
35
35
|
:pending => !!pending?,
|
36
|
-
:checked_in_at => pending?.
|
36
|
+
:checked_in_at => pending? ? pending?.checked_in_at.getutc.iso8601 : nil
|
37
37
|
}
|
38
38
|
end
|
39
39
|
|
data/lib/punchcard.rb
CHANGED
@@ -10,7 +10,8 @@ ActiveRecord::Base.logger = Logger.new(STDOUT)
|
|
10
10
|
ActiveRecord::Base.logger.level = Logger::WARN
|
11
11
|
ActiveRecord::Migration.verbose = false
|
12
12
|
|
13
|
-
|
13
|
+
# Heroku-deployed apps will have this
|
14
|
+
if File.exist?('config/database.yml')
|
14
15
|
dbconfig = YAML.load(File.read('config/database.yml'))
|
15
16
|
ActiveRecord::Base.establish_connection dbconfig[ENV['RACK_ENV']]
|
16
17
|
else
|
data/lib/views/layout.haml
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
%title Punchcard
|
5
5
|
%script{:src => "http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"}
|
6
6
|
%script{:src => 'http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js'}
|
7
|
+
%script{:src => "/js/jquery.timeago.js"}
|
7
8
|
%script{:src => "/js/app.js"}
|
8
9
|
:plain
|
9
10
|
<link rel="stylesheet" href="/css/blueprint.css" type="text/css" media="screen" />
|
@@ -14,7 +15,9 @@
|
|
14
15
|
<li id="${_id}">
|
15
16
|
<img src="${gravatar_url}" title="${name}" class="gravatar {{if pending == true}}pending{{/if}} ${pending}"/>
|
16
17
|
<div class="person">${name}</div>
|
17
|
-
|
18
|
+
{{if checked_in_at}}
|
19
|
+
<abbr class="timeago" title="${checked_in_at}"></abbr>
|
20
|
+
{{/if}}
|
18
21
|
</li>
|
19
22
|
</script>
|
20
23
|
|
data/lib/views/screen.sass
CHANGED
@@ -8,11 +8,12 @@ $yellow: #fc0
|
|
8
8
|
#main
|
9
9
|
width: 800px
|
10
10
|
margin-top: 30px
|
11
|
-
padding:
|
11
|
+
padding: 20px 20px 10px 20px
|
12
12
|
margin-left: auto
|
13
13
|
margin-right: auto
|
14
|
-
background: #eadab6
|
15
|
-
-
|
14
|
+
background: #eadab6
|
15
|
+
border-top: 10px solid #f8bba6
|
16
|
+
-moz-box-shadow: 3px 3px 0px #d2bd8e
|
16
17
|
|
17
18
|
.clear
|
18
19
|
clear: both
|
@@ -34,9 +35,12 @@ $yellow: #fc0
|
|
34
35
|
.gravatar
|
35
36
|
margin-bottom: 0
|
36
37
|
|
37
|
-
.
|
38
|
+
.timeago
|
38
39
|
color: #888
|
39
40
|
font-size: 16px
|
41
|
+
text-decoraion: none
|
42
|
+
border: none
|
43
|
+
display: block
|
40
44
|
position: relative
|
41
45
|
top: -10px
|
42
46
|
|
data/punchcard.gemspec
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{punchcard}
|
8
|
-
s.version = "0.2.
|
8
|
+
s.version = "0.2.2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Christoph Olszowka"]
|
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
|
|
31
31
|
"lib/public/cardbg.png",
|
32
32
|
"lib/public/css/blueprint.css",
|
33
33
|
"lib/public/js/app.js",
|
34
|
+
"lib/public/js/jquery.timeago.js",
|
34
35
|
"lib/punchcard.rb",
|
35
36
|
"lib/punchcard/person.rb",
|
36
37
|
"lib/punchcard/punch.rb",
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 2
|
8
|
-
-
|
9
|
-
version: 0.2.
|
8
|
+
- 2
|
9
|
+
version: 0.2.2
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Christoph Olszowka
|
@@ -170,6 +170,7 @@ files:
|
|
170
170
|
- lib/public/cardbg.png
|
171
171
|
- lib/public/css/blueprint.css
|
172
172
|
- lib/public/js/app.js
|
173
|
+
- lib/public/js/jquery.timeago.js
|
173
174
|
- lib/punchcard.rb
|
174
175
|
- lib/punchcard/person.rb
|
175
176
|
- lib/punchcard/punch.rb
|