event_calendar 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +29 -0
- data/Rakefile +62 -0
- data/VERSION +1 -0
- data/assets/javascripts/calendar.js +55 -0
- data/assets/javascripts/prototype-1.6.1.js +4874 -0
- data/assets/stylesheets/calendar.css +84 -0
- data/assets/stylesheets/yui-2.7.0.css +13 -0
- data/init.rb +1 -0
- data/lib/calendar/event.rb +46 -0
- data/lib/calendar/stylesheet.sass +133 -0
- data/lib/calendar/template.mab +110 -0
- data/lib/calendar/week.rb +34 -0
- data/lib/event_calendar.rb +97 -0
- data/lib/tasks/generate.rake +87 -0
- data/test/calendar_test.rb +13 -0
- data/test/test_helper.rb +11 -0
- metadata +122 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
.calendar table {
|
2
|
+
border-collapse: collapse;
|
3
|
+
margin: 0;
|
4
|
+
width: 100%; }
|
5
|
+
.calendar table td {
|
6
|
+
border-bottom: 0px solid transparent !important;
|
7
|
+
width: 14.285%; }
|
8
|
+
.calendar .days {
|
9
|
+
position: relative; }
|
10
|
+
.calendar .days table.grid {
|
11
|
+
z-index: 1; }
|
12
|
+
.calendar .days .events {
|
13
|
+
left: 0px;
|
14
|
+
position: absolute;
|
15
|
+
table-layout: fixed;
|
16
|
+
text-align: left;
|
17
|
+
top: 0px;
|
18
|
+
z-index: 2; }
|
19
|
+
.calendar .days .events td {
|
20
|
+
border-bottom-width: 0px;
|
21
|
+
border-left-width: 0px;
|
22
|
+
border-color: transparent;
|
23
|
+
overflow: hidden;
|
24
|
+
padding: 0;
|
25
|
+
text-overflow: ellipsis;
|
26
|
+
white-space: nowrap; }
|
27
|
+
.calendar .days .events td a {
|
28
|
+
background-color: transparent;
|
29
|
+
cursor: pointer;
|
30
|
+
display: block;
|
31
|
+
padding: 1px 3px;
|
32
|
+
text-decoration: none; }
|
33
|
+
.calendar .days .events td .fields {
|
34
|
+
display: none; }
|
35
|
+
.calendar .days .events tr.grid td {
|
36
|
+
border-top-width: 0px;
|
37
|
+
height: 0px; }
|
38
|
+
|
39
|
+
.calendar {
|
40
|
+
border-bottom: 1px solid #d4d5d4; }
|
41
|
+
.calendar table td {
|
42
|
+
border-color: #d4d5d4;
|
43
|
+
border-width: 1px; }
|
44
|
+
.calendar .navigation td {
|
45
|
+
border-color: transparent; }
|
46
|
+
.calendar table.grid {
|
47
|
+
background-color: #fefffe;
|
48
|
+
height: 125px; }
|
49
|
+
.calendar .label {
|
50
|
+
background-color: #ebede2;
|
51
|
+
border-top-width: 1px;
|
52
|
+
color: black; }
|
53
|
+
.calendar .header .label {
|
54
|
+
background-color: #2f302f;
|
55
|
+
color: white; }
|
56
|
+
.calendar .days .events td {
|
57
|
+
border-top-width: 3px; }
|
58
|
+
.calendar .days .events td a {
|
59
|
+
background-color: #9aac9a;
|
60
|
+
color: white;
|
61
|
+
-khtml-border-radius: 5px;
|
62
|
+
-moz-border-radius: 5px;
|
63
|
+
-webkit-border-radius: 5px;
|
64
|
+
border-radius: 5px; }
|
65
|
+
.calendar .days .events td:hover a, .calendar .days .events td.hover a {
|
66
|
+
background-color: #6f7f6f; }
|
67
|
+
.calendar .days .events td.continuation a {
|
68
|
+
-khtml-border-radius-bottomleft: 0px;
|
69
|
+
-moz-border-radius-bottomleft: 0px;
|
70
|
+
-webkit-border-bottom-left-radius: 0px;
|
71
|
+
border-bottom-left-radius: 0px;
|
72
|
+
-khtml-border-radius-topleft: 0px;
|
73
|
+
-moz-border-radius-topleft: 0px;
|
74
|
+
-webkit-border-top-left-radius: 0px;
|
75
|
+
border-top-left-radius: 0px; }
|
76
|
+
.calendar .days .events td.continued a {
|
77
|
+
-khtml-border-radius-bottomright: 0px;
|
78
|
+
-moz-border-radius-bottomright: 0px;
|
79
|
+
-webkit-border-bottom-right-radius: 0px;
|
80
|
+
border-bottom-right-radius: 0px;
|
81
|
+
-khtml-border-radius-topright: 0px;
|
82
|
+
-moz-border-radius-topright: 0px;
|
83
|
+
-webkit-border-top-right-radius: 0px;
|
84
|
+
border-top-right-radius: 0px; }
|
@@ -0,0 +1,13 @@
|
|
1
|
+
/*
|
2
|
+
Copyright (c) 2009, Yahoo! Inc. All rights reserved.
|
3
|
+
Code licensed under the BSD License:
|
4
|
+
http://developer.yahoo.net/yui/license.txt
|
5
|
+
version: 2.7.0
|
6
|
+
*/
|
7
|
+
html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}input,button,textarea,select{*font-size:100%;}body{font:13px/1.231 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}select,input,button,textarea,button{font:99% arial,helvetica,clean,sans-serif;}table{font-size:inherit;font:100%;}pre,code,kbd,samp,tt{font-family:monospace;*font-size:108%;line-height:100%;}body{text-align:center;}#doc,#doc2,#doc3,#doc4,.yui-t1,.yui-t2,.yui-t3,.yui-t4,.yui-t5,.yui-t6,.yui-t7{margin:auto;text-align:left;width:57.69em;*width:56.25em;}#doc2{width:73.076em;*width:71.25em;}#doc3{margin:auto 10px;width:auto;}#doc4{width:74.923em;*width:73.05em;}.yui-b{position:relative;}.yui-b{_position:static;}#yui-main .yui-b{position:static;}#yui-main,.yui-g .yui-u .yui-g{width:100%;}.yui-t1 #yui-main,.yui-t2 #yui-main,.yui-t3 #yui-main{float:right;margin-left:-25em;}.yui-t4 #yui-main,.yui-t5 #yui-main,.yui-t6 #yui-main{float:left;margin-right:-25em;}.yui-t1 .yui-b{float:left;width:12.30769em;*width:12.00em;}.yui-t1 #yui-main .yui-b{margin-left:13.30769em;*margin-left:13.05em;}.yui-t2 .yui-b{float:left;width:13.8461em;*width:13.50em;}.yui-t2 #yui-main .yui-b{margin-left:14.8461em;*margin-left:14.55em;}.yui-t3 .yui-b{float:left;width:23.0769em;*width:22.50em;}.yui-t3 #yui-main .yui-b{margin-left:24.0769em;*margin-left:23.62em;}.yui-t4 .yui-b{float:right;width:13.8456em;*width:13.50em;}.yui-t4 #yui-main .yui-b{margin-right:14.8456em;*margin-right:14.55em;}.yui-t5 .yui-b{float:right;width:18.4615em;*width:18.00em;}.yui-t5 #yui-main .yui-b{margin-right:19.4615em;*margin-right:19.125em;}.yui-t6 .yui-b{float:right;width:23.0769em;*width:22.50em;}.yui-t6 #yui-main .yui-b{margin-right:24.0769em;*margin-right:23.62em;}.yui-t7 #yui-main .yui-b{display:block;margin:0 0 1em 0;}#yui-main .yui-b{float:none;width:auto;}.yui-gb .yui-u,.yui-g .yui-gb .yui-u,.yui-gb .yui-g,.yui-gb .yui-gb,.yui-gb .yui-gc,.yui-gb .yui-gd,.yui-gb .yui-ge,.yui-gb .yui-gf,.yui-gc .yui-u,.yui-gc .yui-g,.yui-gd .yui-u{float:left;}.yui-g .yui-u,.yui-g .yui-g,.yui-g .yui-gb,.yui-g .yui-gc,.yui-g .yui-gd,.yui-g .yui-ge,.yui-g .yui-gf,.yui-gc .yui-u,.yui-gd .yui-g,.yui-g .yui-gc .yui-u,.yui-ge .yui-u,.yui-ge .yui-g,.yui-gf .yui-g,.yui-gf .yui-u{float:right;}.yui-g div.first,.yui-gb div.first,.yui-gc div.first,.yui-gd div.first,.yui-ge div.first,.yui-gf div.first,.yui-g .yui-gc div.first,.yui-g .yui-ge div.first,.yui-gc div.first div.first{float:left;}.yui-g .yui-u,.yui-g .yui-g,.yui-g .yui-gb,.yui-g .yui-gc,.yui-g .yui-gd,.yui-g .yui-ge,.yui-g .yui-gf{width:49.1%;}.yui-gb .yui-u,.yui-g .yui-gb .yui-u,.yui-gb .yui-g,.yui-gb .yui-gb,.yui-gb .yui-gc,.yui-gb .yui-gd,.yui-gb .yui-ge,.yui-gb .yui-gf,.yui-gc .yui-u,.yui-gc .yui-g,.yui-gd .yui-u{width:32%;margin-left:1.99%;}.yui-gb .yui-u{*margin-left:1.9%;*width:31.9%;}.yui-gc div.first,.yui-gd .yui-u{width:66%;}.yui-gd div.first{width:32%;}.yui-ge div.first,.yui-gf .yui-u{width:74.2%;}.yui-ge .yui-u,.yui-gf div.first{width:24%;}.yui-g .yui-gb div.first,.yui-gb div.first,.yui-gc div.first,.yui-gd div.first{margin-left:0;}.yui-g .yui-g .yui-u,.yui-gb .yui-g .yui-u,.yui-gc .yui-g .yui-u,.yui-gd .yui-g .yui-u,.yui-ge .yui-g .yui-u,.yui-gf .yui-g .yui-u{width:49%;*width:48.1%;*margin-left:0;}.yui-g .yui-g .yui-u{width:48.1%;}.yui-g .yui-gb div.first,.yui-gb .yui-gb div.first{*margin-right:0;*width:32%;_width:31.7%;}.yui-g .yui-gc div.first,.yui-gd .yui-g{width:66%;}.yui-gb .yui-g div.first{*margin-right:4%;_margin-right:1.3%;}.yui-gb .yui-gc div.first,.yui-gb .yui-gd div.first{*margin-right:0;}.yui-gb .yui-gb .yui-u,.yui-gb .yui-gc .yui-u{*margin-left:1.8%;_margin-left:4%;}.yui-g .yui-gb .yui-u{_margin-left:1.0%;}.yui-gb .yui-gd .yui-u{*width:66%;_width:61.2%;}.yui-gb .yui-gd div.first{*width:31%;_width:29.5%;}.yui-g .yui-gc .yui-u,.yui-gb .yui-gc .yui-u{width:32%;_float:right;margin-right:0;_margin-left:0;}.yui-gb .yui-gc div.first{width:66%;*float:left;*margin-left:0;}.yui-gb .yui-ge .yui-u,.yui-gb .yui-gf .yui-u{margin:0;}.yui-gb .yui-gb .yui-u{_margin-left:.7%;}.yui-gb .yui-g div.first,.yui-gb .yui-gb div.first{*margin-left:0;}.yui-gc .yui-g .yui-u,.yui-gd .yui-g .yui-u{*width:48.1%;*margin-left:0;}.yui-gb .yui-gd div.first{width:32%;}.yui-g .yui-gd div.first{_width:29.9%;}.yui-ge .yui-g{width:24%;}.yui-gf .yui-g{width:74.2%;}.yui-gb .yui-ge div.yui-u,.yui-gb .yui-gf div.yui-u{float:right;}.yui-gb .yui-ge div.first,.yui-gb .yui-gf div.first{float:left;}.yui-gb .yui-ge .yui-u,.yui-gb .yui-gf div.first{*width:24%;_width:20%;}.yui-gb .yui-ge div.first,.yui-gb .yui-gf .yui-u{*width:73.5%;_width:65.5%;}.yui-ge div.first .yui-gd .yui-u{width:65%;}.yui-ge div.first .yui-gd div.first{width:32%;}#hd:after,#bd:after,#ft:after,.yui-g:after,.yui-gb:after,.yui-gc:after,.yui-gd:after,.yui-ge:after,.yui-gf:after{content:".";display:block;height:0;clear:both;visibility:hidden;}#hd,#bd,#ft,.yui-g,.yui-gb,.yui-gc,.yui-gd,.yui-ge,.yui-gf{zoom:1;}/*
|
8
|
+
Copyright (c) 2009, Yahoo! Inc. All rights reserved.
|
9
|
+
Code licensed under the BSD License:
|
10
|
+
http://developer.yahoo.net/yui/license.txt
|
11
|
+
version: 2.7.0
|
12
|
+
*/
|
13
|
+
body{margin:10px;}h1{font-size:138.5%;}h2{font-size:123.1%;}h3{font-size:108%;}h1,h2,h3{margin:1em 0;}h1,h2,h3,h4,h5,h6,strong,dt{font-weight:bold;}optgroup{font-weight:normal;}abbr,acronym{border-bottom:1px dotted #000;cursor:help;}em{font-style:italic;}del{text-decoration:line-through;}blockquote,ul,ol,dl{margin:1em;}ol,ul,dl{margin-left:2em;}ol li{list-style:decimal outside;}ul li{list-style:disc outside;}dl dd{margin-left:1em;}th,td{border:1px solid #000;padding:.5em;}th{font-weight:bold;text-align:center;}caption{margin-bottom:.5em;text-align:center;}sup{vertical-align:super;}sub{vertical-align:sub;}p,fieldset,table,pre{margin-bottom:1em;}button,input[type="checkbox"],input[type="radio"],input[type="reset"],input[type="submit"]{padding:1px;}
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'event_calendar'
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class Calendar
|
2
|
+
class Event
|
3
|
+
|
4
|
+
undef_method :id
|
5
|
+
|
6
|
+
def initialize(event, options)
|
7
|
+
@event, @options = event, options
|
8
|
+
end
|
9
|
+
|
10
|
+
def days(week_start = start_date, week_end = end_date)
|
11
|
+
(end_date > week_end ? week_end : end_date) - (start_date < week_start ? week_start : start_date) + 1
|
12
|
+
end
|
13
|
+
|
14
|
+
def end_date
|
15
|
+
self.end.to_date
|
16
|
+
end
|
17
|
+
|
18
|
+
def start_date
|
19
|
+
start.to_date
|
20
|
+
end
|
21
|
+
|
22
|
+
def method_missing(method, *args)
|
23
|
+
option = "event_#{method}".to_sym
|
24
|
+
if @options.has_key?(option)
|
25
|
+
evaluate_option(option)
|
26
|
+
else
|
27
|
+
@event.send(method, *args)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def evaluate_option(option)
|
34
|
+
value = @options[option]
|
35
|
+
case value
|
36
|
+
when Symbol
|
37
|
+
@event.send(value)
|
38
|
+
when Proc
|
39
|
+
proc.call(self)
|
40
|
+
else
|
41
|
+
value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
=round(!radius)
|
2
|
+
:-khtml-border-radius= !radius
|
3
|
+
:-moz-border-radius= !radius
|
4
|
+
:-webkit-border-radius= !radius
|
5
|
+
:border-radius= !radius
|
6
|
+
|
7
|
+
=unround_left()
|
8
|
+
:-khtml-border-radius-bottomleft= 0px
|
9
|
+
:-moz-border-radius-bottomleft= 0px
|
10
|
+
:-webkit-border-bottom-left-radius= 0px
|
11
|
+
:border-bottom-left-radius= 0px
|
12
|
+
:-khtml-border-radius-topleft= 0px
|
13
|
+
:-moz-border-radius-topleft= 0px
|
14
|
+
:-webkit-border-top-left-radius= 0px
|
15
|
+
:border-top-left-radius= 0px
|
16
|
+
|
17
|
+
=unround_right()
|
18
|
+
:-khtml-border-radius-bottomright= 0px
|
19
|
+
:-moz-border-radius-bottomright= 0px
|
20
|
+
:-webkit-border-bottom-right-radius= 0px
|
21
|
+
:border-bottom-right-radius= 0px
|
22
|
+
:-khtml-border-radius-topright= 0px
|
23
|
+
:-moz-border-radius-topright= 0px
|
24
|
+
:-webkit-border-top-right-radius= 0px
|
25
|
+
:border-top-right-radius= 0px
|
26
|
+
|
27
|
+
.calendar
|
28
|
+
|
29
|
+
table
|
30
|
+
border-collapse: collapse
|
31
|
+
margin: 0
|
32
|
+
width: 100%
|
33
|
+
|
34
|
+
td
|
35
|
+
border-bottom: 0px solid transparent !important
|
36
|
+
width: 14.285%
|
37
|
+
|
38
|
+
.days
|
39
|
+
position: relative
|
40
|
+
|
41
|
+
table.grid
|
42
|
+
z-index: 1
|
43
|
+
|
44
|
+
.events
|
45
|
+
left: 0px
|
46
|
+
position: absolute
|
47
|
+
table-layout: fixed
|
48
|
+
text-align: left
|
49
|
+
top: 0px
|
50
|
+
z-index: 2
|
51
|
+
|
52
|
+
td
|
53
|
+
border-bottom-width: 0px
|
54
|
+
border-left-width: 0px
|
55
|
+
border-color: transparent
|
56
|
+
overflow: hidden
|
57
|
+
padding: 0
|
58
|
+
text-overflow: ellipsis
|
59
|
+
white-space: nowrap
|
60
|
+
|
61
|
+
a
|
62
|
+
background-color: transparent
|
63
|
+
cursor: pointer
|
64
|
+
display: block
|
65
|
+
padding: 1px 3px
|
66
|
+
text-decoration: none
|
67
|
+
|
68
|
+
.fields
|
69
|
+
display: none
|
70
|
+
|
71
|
+
tr.grid td
|
72
|
+
border-top-width: 0px
|
73
|
+
height: 0px
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
!border_color = #D4D5D4
|
79
|
+
!border_width = 1px
|
80
|
+
|
81
|
+
!event_background_color = #9AAC9A
|
82
|
+
!event_hover_background_color = #6F7F6F
|
83
|
+
!event_color = #FFF
|
84
|
+
!event_spacing = 3px
|
85
|
+
|
86
|
+
!grid_background_color = #FEFFFE
|
87
|
+
!grid_height = 125px
|
88
|
+
|
89
|
+
!header_label_background_color = #2F302F
|
90
|
+
!header_label_color = #FFF
|
91
|
+
|
92
|
+
!label_background_color = #EBEDE2
|
93
|
+
!label_color = #000
|
94
|
+
|
95
|
+
.calendar
|
96
|
+
border-bottom= !border_width "solid" !border_color
|
97
|
+
|
98
|
+
table td
|
99
|
+
border-color= !border_color
|
100
|
+
border-width= !border_width
|
101
|
+
|
102
|
+
.navigation td
|
103
|
+
border-color: transparent
|
104
|
+
|
105
|
+
table.grid
|
106
|
+
background-color= !grid_background_color
|
107
|
+
height= !grid_height
|
108
|
+
|
109
|
+
.label
|
110
|
+
background-color= !label_background_color
|
111
|
+
border-top-width= !border_width
|
112
|
+
color= !label_color
|
113
|
+
|
114
|
+
.header .label
|
115
|
+
background-color= !header_label_background_color
|
116
|
+
color= !header_label_color
|
117
|
+
|
118
|
+
.days .events td
|
119
|
+
border-top-width= !event_spacing
|
120
|
+
|
121
|
+
a
|
122
|
+
background-color= !event_background_color
|
123
|
+
color= !event_color
|
124
|
+
+round(5px)
|
125
|
+
|
126
|
+
&:hover a, &.hover a
|
127
|
+
background-color= !event_hover_background_color
|
128
|
+
|
129
|
+
&.continuation a
|
130
|
+
+unround_left()
|
131
|
+
|
132
|
+
&.continued a
|
133
|
+
+unround_right()
|
@@ -0,0 +1,110 @@
|
|
1
|
+
div.calendar.send(calendar.date.strftime('%B').downcase).send("#{calendar.id}!") do
|
2
|
+
div.header do
|
3
|
+
table do
|
4
|
+
tbody do
|
5
|
+
tr.navigation do
|
6
|
+
td.previous_month(:colspan => 2) do
|
7
|
+
a(:rel => 'previous', :href => calendar.navigation_url.call(calendar.date.last_month)) do
|
8
|
+
calendar.evaluate_date_format_option(:navigation_label, calendar.date.last_month, calendar.date)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
td.current_month calendar.evaluate_date_format_option(:header_label, calendar.date), :colspan => 3
|
12
|
+
td.next_month(:colspan => 2) do
|
13
|
+
a(:rel => 'next', :href => calendar.navigation_url.call(calendar.date.next_month)) do
|
14
|
+
calendar.evaluate_date_format_option(:navigation_label, calendar.date.next_month, calendar.date)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
tr.labels do
|
19
|
+
calendar.weeks.first.each_with_index do |day, index|
|
20
|
+
day_label = td.day
|
21
|
+
day_label = day_label.send(day.strftime('%A').downcase)
|
22
|
+
day_label = day_label.today if day.cwday == Time.now.to_date.cwday
|
23
|
+
day_label.label calendar.evaluate_date_format_option(:header_day_label, day)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
div.body do
|
30
|
+
calendar.weeks.each do |week|
|
31
|
+
div.week.send("#{calendar.id}_week_#{week.first}_#{week.last}!") do
|
32
|
+
table.send("#{calendar.id}_labels_#{week.first}_#{week.last}!") do
|
33
|
+
tbody do
|
34
|
+
tr.labels do
|
35
|
+
week.each do |day|
|
36
|
+
day_label = td.day
|
37
|
+
day_label = day_label.send(day.month == calendar.date.month ? 'current_month' : day < calendar.date ? 'previous_month' : 'next_month')
|
38
|
+
day_label = day_label.send([6, 7].include?(day.cwday) ? 'weekend' : 'weekday')
|
39
|
+
day_label = day_label.send(day.strftime('%A').downcase)
|
40
|
+
day_label = day_label.send(day.strftime('%B').downcase)
|
41
|
+
day_label = day_label.send("day_#{day.strftime('%d').gsub(/^0/, '')}")
|
42
|
+
day_label = day_label.today if day == Time.now.to_date
|
43
|
+
day_label = day_label.scheduled if calendar.events.any? { |event| event.start_date <= day && event.end_date >= day }
|
44
|
+
day_label.label calendar.evaluate_date_format_option(:day_label, day)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
div.days.send("#{calendar.id}_days_#{week.first}_#{week.last}!") do
|
50
|
+
table.grid.send("#{calendar.id}_grid_#{week.first}_#{week.last}!") do
|
51
|
+
tbody do
|
52
|
+
tr do
|
53
|
+
week.each do |day|
|
54
|
+
day_grid = td.day
|
55
|
+
day_grid = day_grid.send(day.month == calendar.date.month ? 'current_month' : day < calendar.date ? 'previous_month' : 'next_month')
|
56
|
+
day_grid = day_grid.send([6, 7].include?(day.cwday) ? 'weekend' : 'weekday')
|
57
|
+
day_grid = day_grid.send(day.strftime('%A').downcase)
|
58
|
+
day_grid = day_grid.send(day.strftime('%B').downcase)
|
59
|
+
day_grid = day_grid.send("day_#{day.strftime('%d').gsub(/^0/, '')}")
|
60
|
+
day_grid = day_grid.today if day == Time.now.to_date
|
61
|
+
day_grid = day_grid.scheduled if calendar.events.any? { |event| event.start_date <= day && event.end_date >= day }
|
62
|
+
day_grid.send("#{calendar.id}_day_#{day}!") {}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
unless week.events.empty?
|
68
|
+
table.events.send("#{calendar.id}_events_#{week.first}_#{week.last}!") do
|
69
|
+
tbody do
|
70
|
+
tr.grid do
|
71
|
+
week.each { |day| td('') }
|
72
|
+
end
|
73
|
+
week.events.each do |row|
|
74
|
+
tr do
|
75
|
+
row.each do |cell|
|
76
|
+
if cell.empty?
|
77
|
+
td('')
|
78
|
+
else
|
79
|
+
html_options = { :class => 'event' }
|
80
|
+
html_options[:colspan] = cell[:span] unless cell[:span] == 1
|
81
|
+
html_options[:class] << " #{calendar.event_class}" unless calendar.event_class.nil?
|
82
|
+
if cell[:continued]
|
83
|
+
html_options[:class] << ' continuation' if cell[:event].start_date < week.first
|
84
|
+
html_options[:class] << ' continued' if cell[:event].end_date > week.last
|
85
|
+
end
|
86
|
+
event_id = "event_#{cell[:event].id}"
|
87
|
+
begin
|
88
|
+
td.send("#{event_id}!", html_options) do
|
89
|
+
calendar.event_output.call(cell[:event]) + div.fields do
|
90
|
+
calendar.event_fields.each do |field|
|
91
|
+
span(cell[:event].send(field), :title => field)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
rescue Markaby::InvalidXhtmlError
|
96
|
+
event_id = event_id.gsub(/^event_(\d+)(_(\d+))?$/) { |match| "event_#{$1}_#{$3.to_i + 1}" }
|
97
|
+
retry
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Calendar
|
2
|
+
class Week < Array
|
3
|
+
|
4
|
+
extend ActiveSupport::Memoizable
|
5
|
+
|
6
|
+
def initialize(days, events)
|
7
|
+
super days
|
8
|
+
each { |day| day.events = events.select { |event| event.start_date == day || (event.start_date < day && event.end_date >= day && day == first) } }
|
9
|
+
end
|
10
|
+
|
11
|
+
def events
|
12
|
+
events = []
|
13
|
+
until all? { |day| day.events.empty? }
|
14
|
+
row = []
|
15
|
+
each_with_index do |day, index|
|
16
|
+
cell_count = row.inject(0) { |sum, cell| sum += (cell.empty? ? 1 : cell[:span]) }
|
17
|
+
next if cell_count > index || cell_count >= 7
|
18
|
+
|
19
|
+
cell = {}
|
20
|
+
unless day.events.empty?
|
21
|
+
cell[:event] = day.events.shift
|
22
|
+
cell[:span] = cell[:event].days(first, last)
|
23
|
+
cell[:continued] = cell[:event].days != cell[:span]
|
24
|
+
end
|
25
|
+
row << cell
|
26
|
+
end
|
27
|
+
events << row
|
28
|
+
end
|
29
|
+
events
|
30
|
+
end
|
31
|
+
memoize :events
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'activesupport'
|
2
|
+
require 'markaby'
|
3
|
+
require 'haml'
|
4
|
+
require 'calendar/event'
|
5
|
+
require 'calendar/week'
|
6
|
+
|
7
|
+
Date.class_eval { attr_accessor :events }
|
8
|
+
|
9
|
+
class Calendar
|
10
|
+
|
11
|
+
extend ActiveSupport::Memoizable
|
12
|
+
|
13
|
+
undef_method :id
|
14
|
+
|
15
|
+
attr_accessor :events, :month, :options, :year
|
16
|
+
|
17
|
+
def self.default_options
|
18
|
+
@default_options ||= {
|
19
|
+
:id => 'calendar',
|
20
|
+
:beginning_of_week => 0,
|
21
|
+
:day_label => proc { |date| date.strftime('%d').gsub(/^0/, '') },
|
22
|
+
:event_class => nil,
|
23
|
+
:event_id => :id,
|
24
|
+
:event_title => :title,
|
25
|
+
:event_start => :starts_at,
|
26
|
+
:event_end => :ends_at,
|
27
|
+
:event_output => proc { |event| "<a href=\"#\" title=\"#{event.title}\">#{event.title}</a>" },
|
28
|
+
:event_fields => [:id, :title, :start, :end],
|
29
|
+
:events => [],
|
30
|
+
:header_label => '%B %Y',
|
31
|
+
:header_day_label => '%a',
|
32
|
+
:navigation_label => '%B',
|
33
|
+
:navigation_url => proc { |date| '#' },
|
34
|
+
:template => File.join(File.dirname(__FILE__), 'calendar', 'template.mab')
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(year = Time.now.year, month = Time.now.month, options = {})
|
39
|
+
self.year, self.month, self.options = year, month, self.class.default_options.merge(options)
|
40
|
+
self.events = self.options.delete(:events).collect { |event| Event.new(event, self.options) }.sort_by(&:start)
|
41
|
+
yield self if block_given?
|
42
|
+
end
|
43
|
+
|
44
|
+
def date
|
45
|
+
Date.civil(year, month, 1)
|
46
|
+
end
|
47
|
+
memoize :date
|
48
|
+
|
49
|
+
def evaluate_date_format_option(option, *args)
|
50
|
+
value = self.send(option)
|
51
|
+
case value
|
52
|
+
when String
|
53
|
+
args.first.strftime(value)
|
54
|
+
when Symbol
|
55
|
+
args.first.send(value)
|
56
|
+
when Proc
|
57
|
+
value.call(*args)
|
58
|
+
else
|
59
|
+
value
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def method_missing(method, *args)
|
64
|
+
if method.to_s =~ /^([^=]+)(=?)$/ && options.has_key?($1.to_sym)
|
65
|
+
options[$1.to_sym] = args.first unless $2.empty?
|
66
|
+
options[$1.to_sym]
|
67
|
+
else
|
68
|
+
super
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_s
|
73
|
+
date(:reload)
|
74
|
+
weeks(:reload)
|
75
|
+
render
|
76
|
+
end
|
77
|
+
alias_method :to_html, :to_s
|
78
|
+
|
79
|
+
def weeks
|
80
|
+
days_in_month = Time.days_in_month(month, year)
|
81
|
+
starting_day = date.beginning_of_week() -1.day + beginning_of_week.days
|
82
|
+
ending_day = (date + days_in_month).end_of_week() -1.day + beginning_of_week.days
|
83
|
+
(starting_day..ending_day).to_a.in_groups_of(7).collect { |week| Week.new(week, events) }
|
84
|
+
end
|
85
|
+
memoize :weeks
|
86
|
+
|
87
|
+
protected
|
88
|
+
|
89
|
+
def render
|
90
|
+
render_with_markaby
|
91
|
+
end
|
92
|
+
|
93
|
+
def render_with_markaby
|
94
|
+
Markaby::Builder.new(:calendar => self, :template => File.read(template)) { eval(template) }.to_s
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
CALENDAR_ROOT = File.join(File.dirname(__FILE__), '..', '..')
|
4
|
+
ASSETS_ROOT = File.join(CALENDAR_ROOT, 'assets')
|
5
|
+
SANDBOX_ROOT = File.join(CALENDAR_ROOT, 'sandbox')
|
6
|
+
|
7
|
+
namespace :calendar do
|
8
|
+
namespace :generate do
|
9
|
+
desc 'Generates css for the calendar'
|
10
|
+
task :css => :build_css do
|
11
|
+
puts File.read(File.join(ASSETS_ROOT, 'stylesheets', 'calendar.css'))
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'Generates js for the calendar'
|
15
|
+
task :js do
|
16
|
+
puts File.read(File.join(ASSETS_ROOT, 'javascripts', 'calendar.js'))
|
17
|
+
end
|
18
|
+
|
19
|
+
desc 'Creates a sandbox in the gem root for testing'
|
20
|
+
task :sandbox => [:build_css] do
|
21
|
+
$:.unshift(File.join(CALENDAR_ROOT, 'lib'))
|
22
|
+
require 'timecop'
|
23
|
+
require 'active_support'
|
24
|
+
require 'markaby'
|
25
|
+
require 'calendar'
|
26
|
+
|
27
|
+
FileUtils.mkdir(SANDBOX_ROOT) unless File.exists?(SANDBOX_ROOT)
|
28
|
+
FileUtils.cp_r(File.join(ASSETS_ROOT, '.'), SANDBOX_ROOT)
|
29
|
+
|
30
|
+
Event = Struct.new(:title, :starts_at, :ends_at) do
|
31
|
+
def id; object_id; end
|
32
|
+
end
|
33
|
+
|
34
|
+
Timecop.freeze(Date.civil(2009, 10, 6)) do
|
35
|
+
events = [
|
36
|
+
Event.new('Event 1', Time.now, 10.minutes.from_now),
|
37
|
+
Event.new('Event 1a', Time.now, 10.minutes.from_now),
|
38
|
+
Event.new('Event 1b', Time.now, 10.minutes.from_now),
|
39
|
+
Event.new('Event 1c', Time.now, 10.minutes.from_now),
|
40
|
+
Event.new('Event 2', 1.day.from_now, 1.5.days.from_now),
|
41
|
+
Event.new('Event 3', Time.now, 3.days.from_now),
|
42
|
+
Event.new('Event 4 has a longer title', 3.days.from_now, 5.days.from_now),
|
43
|
+
Event.new('Event 5 spans across multiple weeks', 4.days.from_now, 12.days.from_now)
|
44
|
+
]
|
45
|
+
@calendar = Calendar.new(Time.now.year, Time.now.month, :events => events)
|
46
|
+
end
|
47
|
+
|
48
|
+
File.open(File.join(SANDBOX_ROOT, 'index.html'), 'w+') do |file|
|
49
|
+
file.write <<-EOF
|
50
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
51
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
52
|
+
<head>
|
53
|
+
<title>Calendar</title>
|
54
|
+
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
|
55
|
+
<link href="stylesheets/yui-2.7.0.css" media="screen" rel="stylesheet" type="text/css" />
|
56
|
+
<link href="stylesheets/calendar.css" media="screen" rel="stylesheet" type="text/css" />
|
57
|
+
<script src="javascripts/prototype-1.6.1.js" type="text/javascript"></script>
|
58
|
+
<script src="javascripts/calendar.js" type="text/javascript"></script>
|
59
|
+
<script type="text/javascript">
|
60
|
+
//<![CDATA[
|
61
|
+
document.observe('dom:loaded', function() {
|
62
|
+
$$('.calendar').each(function(element) {
|
63
|
+
new Calendar(element);
|
64
|
+
});
|
65
|
+
});
|
66
|
+
//]]>
|
67
|
+
</script>
|
68
|
+
</head>
|
69
|
+
<body>
|
70
|
+
#{@calendar}
|
71
|
+
</body>
|
72
|
+
</html>
|
73
|
+
EOF
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
task :build_css do
|
78
|
+
require 'haml'
|
79
|
+
require 'sass/engine'
|
80
|
+
|
81
|
+
sass = File.join(CALENDAR_ROOT, 'lib', 'calendar', 'stylesheet.sass')
|
82
|
+
File.open(File.join(ASSETS_ROOT, 'stylesheets', 'calendar.css'), 'w+') do |file|
|
83
|
+
file.write Sass::Engine.new(File.read(sass)).to_css
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
require 'timecop'
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
7
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
8
|
+
require 'event_calendar'
|
9
|
+
|
10
|
+
class Test::Unit::TestCase
|
11
|
+
end
|