event-calendar 2.3.1

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.
Files changed (38) hide show
  1. data/CHANGELOG.rdoc +38 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +274 -0
  4. data/Rakefile +37 -0
  5. data/VERSION +1 -0
  6. data/generators/event_calendar/USAGE +20 -0
  7. data/generators/event_calendar/event_calendar_generator.rb +50 -0
  8. data/generators/event_calendar/lib/insert_routes.rb +55 -0
  9. data/generators/event_calendar/templates/controller.rb.erb +12 -0
  10. data/generators/event_calendar/templates/helper.rb.erb +35 -0
  11. data/generators/event_calendar/templates/javascript.js +49 -0
  12. data/generators/event_calendar/templates/jq_javascript.js +35 -0
  13. data/generators/event_calendar/templates/migration.rb.erb +18 -0
  14. data/generators/event_calendar/templates/model.rb.erb +4 -0
  15. data/generators/event_calendar/templates/stylesheet.css +233 -0
  16. data/generators/event_calendar/templates/view.html.erb +6 -0
  17. data/init.rb +2 -0
  18. data/install.rb +1 -0
  19. data/lib/event_calendar.rb +189 -0
  20. data/lib/event_calendar/calendar_helper.rb +366 -0
  21. data/lib/event_calendar/railtie.rb +12 -0
  22. data/lib/generators/event_calendar/USAGE +20 -0
  23. data/lib/generators/event_calendar/event_calendar_generator.rb +92 -0
  24. data/lib/generators/event_calendar/templates/controller.rb.erb +12 -0
  25. data/lib/generators/event_calendar/templates/helper.rb.erb +35 -0
  26. data/lib/generators/event_calendar/templates/javascript.js +49 -0
  27. data/lib/generators/event_calendar/templates/jq_javascript.js +35 -0
  28. data/lib/generators/event_calendar/templates/migration.rb.erb +21 -0
  29. data/lib/generators/event_calendar/templates/model.rb.erb +3 -0
  30. data/lib/generators/event_calendar/templates/stylesheet.css +233 -0
  31. data/lib/generators/event_calendar/templates/view.html.erb +6 -0
  32. data/lib/tasks/event_calendar_tasks.rake +4 -0
  33. data/spec/event_calendar_spec.rb +78 -0
  34. data/spec/fixtures/models.rb +6 -0
  35. data/spec/spec.opts +3 -0
  36. data/spec/spec_helper.rb +30 -0
  37. data/uninstall.rb +1 -0
  38. metadata +105 -0
@@ -0,0 +1,6 @@
1
+ <!-- Probably move the stylesheet to you layout. Also make sure you include the javascript. -->
2
+ <%%= stylesheet_link_tag "event_calendar" %>
3
+
4
+ <h1>Calendar</h1>
5
+
6
+ <%%= event_calendar %>
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ ActiveRecord::Base.send :include, EventCalendar
2
+ ActionView::Base.send :include, EventCalendar::CalendarHelper
data/install.rb ADDED
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,189 @@
1
+ require 'event_calendar/railtie' if defined?(::Rails::Railtie)
2
+ module EventCalendar
3
+
4
+ def self.included(base)
5
+ base.send :extend, ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+
10
+ def has_event_calendar(options={})
11
+ cattr_accessor :start_at_field, :end_at_field
12
+ self.start_at_field = ( options[:start_at_field] ||= :start_at).to_s
13
+ self.end_at_field = ( options[:end_at_field] ||= :end_at ).to_s
14
+ alias_attribute :start_at, start_at_field unless start_at_field == 'start_at'
15
+ alias_attribute :end_at, end_at_field unless end_at_field == 'end_at'
16
+ before_save :adjust_all_day_dates
17
+ send :include, InstanceMethods
18
+ end
19
+
20
+ # For the given month, find the start and end dates
21
+ # Find all the events within this range, and create event strips for them
22
+ def event_strips_for_month(shown_date, first_day_of_week=0, find_options = {})
23
+ if first_day_of_week.is_a?(Hash)
24
+ find_options.merge!(first_day_of_week)
25
+ first_day_of_week = 0
26
+ end
27
+ strip_start, strip_end = get_start_and_end_dates(shown_date, first_day_of_week)
28
+ events = events_for_date_range(strip_start, strip_end, find_options)
29
+ event_strips = create_event_strips(strip_start, strip_end, events)
30
+ event_strips
31
+ end
32
+
33
+ # Expand start and end dates to show the previous month and next month's days,
34
+ # that overlap with the shown months display
35
+ def get_start_and_end_dates(shown_date, first_day_of_week=0)
36
+ # start with the first day of the given month
37
+ start_of_month = Date.civil(shown_date.year, shown_date.month, 1)
38
+ # the end of last month
39
+ strip_start = beginning_of_week(start_of_month, first_day_of_week)
40
+ # the beginning of next month, unless this month ended evenly on the last day of the week
41
+ if start_of_month.next_month == beginning_of_week(start_of_month.next_month, first_day_of_week)
42
+ # last day of the month is also the last day of the week
43
+ strip_end = start_of_month.next_month
44
+ else
45
+ # add the extra days from next month
46
+ strip_end = beginning_of_week(start_of_month.next_month + 7, first_day_of_week)
47
+ end
48
+ [strip_start, strip_end]
49
+ end
50
+
51
+ # Get the events overlapping the given start and end dates
52
+ def events_for_date_range(start_d, end_d, find_options = {})
53
+ self.scoped(find_options).find(
54
+ :all,
55
+ :conditions => [ "(? <= #{self.end_at_field}) AND (#{self.start_at_field}< ?)", start_d.to_time.utc, end_d.to_time.utc ],
56
+ :order => "#{self.start_at_field} ASC"
57
+ )
58
+ end
59
+
60
+ # Create the various strips that show evetns
61
+ def create_event_strips(strip_start, strip_end, events)
62
+ # create an inital event strip, with a nil entry for every day of the displayed days
63
+ event_strips = [[nil] * (strip_end - strip_start + 1)]
64
+
65
+ events.each do |event|
66
+ cur_date = event.start_at.to_date
67
+ end_date = event.end_at.to_date
68
+ cur_date, end_date = event.clip_range(strip_start, strip_end)
69
+ start_range = (cur_date - strip_start).to_i
70
+ end_range = (end_date - strip_start).to_i
71
+
72
+ # make sure the event is within our viewing range
73
+ if (start_range <= end_range) and (end_range >= 0)
74
+ range = start_range..end_range
75
+
76
+ open_strip = space_in_current_strips?(event_strips, range)
77
+
78
+ if open_strip.nil?
79
+ # no strips open, make a new one
80
+ new_strip = [nil] * (strip_end - strip_start + 1)
81
+ range.each {|r| new_strip[r] = event}
82
+ event_strips << new_strip
83
+ else
84
+ # found an open strip, add this event to it
85
+ range.each {|r| open_strip[r] = event}
86
+ end
87
+ end
88
+ end
89
+ event_strips
90
+ end
91
+
92
+ def space_in_current_strips?(event_strips, range)
93
+ open_strip = nil
94
+ for strip in event_strips
95
+ strip_is_open = true
96
+ range.each do |r|
97
+ # overlapping events on this strip
98
+ if !strip[r].nil?
99
+ strip_is_open = false
100
+ break
101
+ end
102
+ end
103
+
104
+ if strip_is_open
105
+ open_strip = strip
106
+ break
107
+ end
108
+ end
109
+ open_strip
110
+ end
111
+
112
+ def days_between(first, second)
113
+ if first > second
114
+ second + (7 - first)
115
+ else
116
+ second - first
117
+ end
118
+ end
119
+
120
+ def beginning_of_week(date, start = 0)
121
+ days_to_beg = days_between(start, date.wday)
122
+ date - days_to_beg
123
+ end
124
+
125
+ end
126
+
127
+ # Instance Methods
128
+ # Override in your model as needed
129
+ module InstanceMethods
130
+ def year
131
+ date.year
132
+ end
133
+
134
+ def month
135
+ date.month
136
+ end
137
+
138
+ def day
139
+ date.day
140
+ end
141
+
142
+ def all_day
143
+ self[:all_day]
144
+ end
145
+
146
+ def color
147
+ self[:color] || '#9aa4ad'
148
+ end
149
+
150
+ def days
151
+ end_at.to_date - start_at.to_date
152
+ end
153
+
154
+ # start_d - start of the month, or start of the week
155
+ # end_d - end of the month, or end of the week
156
+ def clip_range(start_d, end_d)
157
+ # make sure we are comparing date objects to date objects,
158
+ # otherwise timezones can cause problems
159
+ start_at_d = start_at.to_date
160
+ end_at_d = end_at.to_date
161
+ # Clip start date, make sure it also ends on or after the start range
162
+ if (start_at_d < start_d and end_at_d >= start_d)
163
+ clipped_start = start_d
164
+ else
165
+ clipped_start = start_at_d
166
+ end
167
+
168
+ # Clip end date
169
+ if (end_at_d > end_d)
170
+ clipped_end = end_d
171
+ else
172
+ clipped_end = end_at_d
173
+ end
174
+ [clipped_start, clipped_end]
175
+ end
176
+
177
+ def adjust_all_day_dates
178
+ if self[:all_day]
179
+ self[:start_at] = self[:start_at].beginning_of_day
180
+ if self[:end_at]
181
+ self[:end_at] = self[:end_at].beginning_of_day + 1.day - 1.second
182
+ else
183
+ self[:end_at] = self[:start_at].beginning_of_day + 1.day - 1.second
184
+ end
185
+ end
186
+ end
187
+
188
+ end
189
+ end
@@ -0,0 +1,366 @@
1
+ module EventCalendar
2
+ module CalendarHelper
3
+
4
+ # Returns an HTML calendar which can show multiple, overlapping events across calendar days and rows.
5
+ # Customize using CSS, the below options, and by passing in a code block.
6
+ #
7
+ # The following are optional, available for customizing the default behaviour:
8
+ # :month => Time.now.month # The month to show the calendar for. Defaults to current month.
9
+ # :year => Time.now.year # The year to show the calendar for. Defaults to current year.
10
+ # :dates => (start_date .. end_date) # Show specific range of days. Defaults to :year, :month.
11
+ # :abbrev => true # Abbreviate day names. Reads from the abbr_day_names key in the localization file.
12
+ # :first_day_of_week => 0 # Renders calendar starting on Sunday. Use 1 for Monday, and so on.
13
+ # :show_today => true # Highlights today on the calendar using CSS class.
14
+ # :month_name_text => nil # Displayed center in header row.
15
+ # Defaults to current month name from Date::MONTHNAMES hash.
16
+ # :previous_month_text => nil # Displayed left of the month name if set
17
+ # :next_month_text => nil # Displayed right of the month name if set
18
+ # :event_strips => [] # An array of arrays, encapsulating the event rows on the calendar
19
+ #
20
+ # :width => nil # Width of the calendar, if none is set then it will stretch the container's width
21
+ # :height => 500 # Approx minimum total height of the calendar (excluding the header).
22
+ # Height could get added if a day has too many event's to fit.
23
+ # :day_names_height => 18 # Height of the day names table (included in the above 'height' option)
24
+ # :day_nums_height => 18 # Height of the day numbers tables (included in the 'height' option)
25
+ # :event_height => 18 # Height of an individual event row
26
+ # :event_margin => 1 # Spacing of the event rows
27
+ # :event_padding_top => 1 # Padding on the top of the event rows (increase to move text down)
28
+ #
29
+ # :use_all_day => false # If set to true, will check for an 'all_day' boolean field when displaying an event.
30
+ # If it is an all day event, or the event is multiple days, then it will display as usual.
31
+ # Otherwise it will display without a background color bar.
32
+ # :use_javascript => true # Outputs HTML with inline javascript so events spanning multiple days will be highlighted.
33
+ # If this option is false, cleaner HTML will be output, but events spanning multiple days will
34
+ # not be highlighted correctly on hover, so it is only really useful if you know your calendar
35
+ # will only have single-day events. Defaults to true.
36
+ # :link_to_day_action => false # If controller action is passed,
37
+ # the day number will be a link. Override the day_link method for greater customization.
38
+ #
39
+ # For more customization, you can pass a code block to this method
40
+ # The varibles you have to work with in this block are passed in an agruments hash:
41
+ # :event => The event to be displayed.
42
+ # :day => The day the event is displayed on. Usually the first day of the event, or the first day of the week,
43
+ # if the event spans a calendar row.
44
+ # :options => All the calendar options in use. (User defined and defaults merged.)
45
+ #
46
+ # For example usage, see README.
47
+ #
48
+ def calendar(options = {}, &block)
49
+ block ||= Proc.new {|d| nil}
50
+
51
+ defaults = {
52
+ :year => (Time.zone || Time).now.year,
53
+ :month => (Time.zone || Time).now.month,
54
+ :abbrev => true,
55
+ :first_day_of_week => 0,
56
+ :show_today => true,
57
+ :month_name_text => (Time.zone || Time).now.strftime("%B %Y"),
58
+ :previous_month_text => nil,
59
+ :next_month_text => nil,
60
+ :event_strips => [],
61
+
62
+ # it would be nice to have these in the CSS file
63
+ # but they are needed to perform height calculations
64
+ :width => nil,
65
+ :height => 500,
66
+ :day_names_height => 18,
67
+ :day_nums_height => 18,
68
+ :event_height => 18,
69
+ :event_margin => 1,
70
+ :event_padding_top => 2,
71
+
72
+ :use_all_day => false,
73
+ :use_javascript => true,
74
+ :link_to_day_action => false
75
+ }
76
+ options = defaults.merge options
77
+
78
+ # default month name for the given number
79
+ options[:month_name_text] ||= I18n.translate(:'date.month_names')[options[:month]]
80
+
81
+ # make the height calculations
82
+ # tricky since multiple events in a day could force an increase in the set height
83
+ height = options[:day_names_height]
84
+ row_heights = cal_row_heights(options)
85
+ row_heights.each do |row_height|
86
+ height += row_height
87
+ end
88
+
89
+ # the first and last days of this calendar month
90
+ if options[:dates].is_a?(Range)
91
+ first = options[:dates].begin
92
+ last = options[:dates].end
93
+ else
94
+ first = Date.civil(options[:year], options[:month], 1)
95
+ last = Date.civil(options[:year], options[:month], -1)
96
+ end
97
+
98
+ # create the day names array [Sunday, Monday, etc...]
99
+ day_names = []
100
+ if options[:abbrev]
101
+ day_names.concat(I18n.translate(:'date.abbr_day_names'))
102
+ else
103
+ day_names.concat(I18n.translate(:'date.day_names'))
104
+ end
105
+ options[:first_day_of_week].times do
106
+ day_names.push(day_names.shift)
107
+ end
108
+
109
+ # Build the HTML string
110
+ cal = ""
111
+
112
+ # outer calendar container
113
+ cal << %(<div class="ec-calendar")
114
+ cal << %(style="width: #{options[:width]}px;") if options[:width]
115
+ cal << %(>)
116
+
117
+ # table header, including the monthname and links to prev & next month
118
+ cal << %(<table class="ec-calendar-header" cellpadding="0" cellspacing="0">)
119
+ cal << %(<thead><tr>)
120
+ if options[:previous_month_text] or options[:next_month_text]
121
+ cal << %(<th colspan="2" class="ec-month-nav ec-previous-month">#{options[:previous_month_text]}</th>)
122
+ colspan = 3
123
+ else
124
+ colspan = 7
125
+ end
126
+ cal << %(<th colspan="#{colspan}" class="ec-month-name">#{options[:month_name_text]}</th>)
127
+ if options[:next_month_text]
128
+ cal << %(<th colspan="2" class="ec-month-nav ec-next-month">#{options[:next_month_text]}</th>)
129
+ end
130
+ cal << %(</tr></thead></table>)
131
+
132
+ # body container (holds day names and the calendar rows)
133
+ cal << %(<div class="ec-body" style="height: #{height}px;">)
134
+
135
+ # day names
136
+ cal << %(<table class="ec-day-names" style="height: #{options[:day_names_height]}px;" cellpadding="0" cellspacing="0">)
137
+ cal << %(<tbody><tr>)
138
+ day_names.each do |day_name|
139
+ cal << %(<th class="ec-day-name" title="#{day_name}">#{day_name}</th>)
140
+ end
141
+ cal << %(</tr></tbody></table>)
142
+
143
+ # container for all the calendar rows
144
+ cal << %(<div class="ec-rows" style="top: #{options[:day_names_height]}px; )
145
+ cal << %(height: #{height - options[:day_names_height]}px;">)
146
+
147
+ # initialize loop variables
148
+ first_day_of_week = beginning_of_week(first, options[:first_day_of_week])
149
+ last_day_of_week = end_of_week(first, options[:first_day_of_week])
150
+ last_day_of_cal = end_of_week(last, options[:first_day_of_week])
151
+ row_num = 0
152
+ top = 0
153
+
154
+ # go through a week at a time, until we reach the end of the month
155
+ while(last_day_of_week <= last_day_of_cal)
156
+ cal << %(<div class="ec-row" style="top: #{top}px; height: #{row_heights[row_num]}px;">)
157
+ top += row_heights[row_num]
158
+
159
+ # this weeks background table
160
+ cal << %(<table class="ec-row-bg" cellpadding="0" cellspacing="0">)
161
+ cal << %(<tbody><tr>)
162
+ first_day_of_week.upto(first_day_of_week+6) do |day|
163
+ today_class = (day == Date.today) ? "ec-today-bg" : ""
164
+ cal << %(<td class="ec-day-bg #{today_class}">&nbsp;</td>)
165
+ end
166
+ cal << %(</tr></tbody></table>)
167
+
168
+ # calendar row
169
+ cal << %(<table class="ec-row-table" cellpadding="0" cellspacing="0">)
170
+ cal << %(<tbody>)
171
+
172
+ # day numbers row
173
+ cal << %(<tr>)
174
+ first_day_of_week.upto(last_day_of_week) do |day|
175
+ cal << %(<td class="ec-day-header )
176
+ cal << %(ec-today-header ) if options[:show_today] and (day == Date.today)
177
+ cal << %(ec-other-month-header ) if (day < first) || (day > last)
178
+ cal << %(ec-weekend-day-header) if weekend?(day)
179
+ cal << %(" style="height: #{options[:day_nums_height]}px;">)
180
+ if options[:link_to_day_action]
181
+ cal << day_link(day.day, day, options[:link_to_day_action])
182
+ else
183
+ cal << %(#{day.day})
184
+ end
185
+ cal << %(</td>)
186
+ end
187
+ cal << %(</tr>)
188
+
189
+ # event rows for this day
190
+ # for each event strip, create a new table row
191
+ options[:event_strips].each do |strip|
192
+ cal << %(<tr>)
193
+ # go through through the strip, for the entries that correspond to the days of this week
194
+ strip[row_num*7, 7].each_with_index do |event, index|
195
+ day = first_day_of_week + index
196
+
197
+ if event
198
+ # get the dates of this event that fit into this week
199
+ dates = event.clip_range(first_day_of_week, last_day_of_week)
200
+ # if the event (after it has been clipped) starts on this date,
201
+ # then create a new cell that spans the number of days
202
+ if dates[0] == day.to_date
203
+ # check if we should display the bg color or not
204
+ no_bg = no_event_bg?(event, options)
205
+
206
+ cal << %(<td class="ec-event-cell" colspan="#{(dates[1]-dates[0]).to_i + 1}" )
207
+ cal << %(style="padding-top: #{options[:event_margin]}px;">)
208
+ cal << %(<div class="ec-event ec-event-#{event.id} )
209
+ if no_bg
210
+ cal << %(ec-event-no-bg" )
211
+ cal << %(style="color: #{event.color}; )
212
+ else
213
+ cal << %(ec-event-bg" )
214
+ cal << %(style="background-color: #{event.color}; )
215
+ end
216
+ cal << %(padding-top: #{options[:event_padding_top]}px; )
217
+ cal << %(height: #{options[:event_height] - options[:event_padding_top]}px;" )
218
+ if options[:use_javascript]
219
+ # custom attributes needed for javascript event highlighting
220
+ cal << %(data-event-id="#{event.id}" data-color="#{event.color}" )
221
+ end
222
+ cal << %(>)
223
+
224
+ # add a left arrow if event is clipped at the beginning
225
+ if event.start_at.to_date < dates[0]
226
+ cal << %(<div class="ec-left-arrow"></div>)
227
+ end
228
+ # add a right arrow if event is clipped at the end
229
+ if event.end_at.to_date > dates[1]
230
+ cal << %(<div class="ec-right-arrow"></div>)
231
+ end
232
+
233
+ if no_bg
234
+ cal << %(<div class="ec-bullet" style="background-color: #{event.color};"></div>)
235
+ # make sure anchor text is the event color
236
+ # here b/c CSS 'inherit' color doesn't work in all browsers
237
+ cal << %(<style type="text/css">.ec-event-#{event.id} a { color: #{event.color}; }</style>)
238
+ end
239
+
240
+ if block_given?
241
+ # add the additional html that was passed as a block to this helper
242
+ cal << block.call({:event => event, :day => day.to_date, :options => options})
243
+ else
244
+ # default content in case nothing is passed in
245
+ cal << %(<a href="/events/#{event.id}" title="#{h(event.name)}">#{h(event.name)}</a>)
246
+ end
247
+
248
+ cal << %(</div></td>)
249
+ end
250
+
251
+ else
252
+ # there wasn't an event, so create an empty cell and container
253
+ cal << %(<td class="ec-event-cell ec-no-event-cell" )
254
+ cal << %(style="padding-top: #{options[:event_margin]}px;">)
255
+ cal << %(<div class="ec-event" )
256
+ cal << %(style="padding-top: #{options[:event_padding_top]}px; )
257
+ cal << %(height: #{options[:event_height] - options[:event_padding_top]}px;" )
258
+ cal << %(>)
259
+ cal << %(&nbsp;</div></td>)
260
+ end
261
+ end
262
+ cal << %(</tr>)
263
+ end
264
+
265
+ cal << %(</tbody></table>)
266
+ cal << %(</div>)
267
+
268
+ # increment the calendar row we are on, and the week
269
+ row_num += 1
270
+ first_day_of_week += 7
271
+ last_day_of_week += 7
272
+ end
273
+
274
+ cal << %(</div>)
275
+ cal << %(</div>)
276
+ cal << %(</div>)
277
+ end
278
+
279
+ # override this in your own helper for greater control
280
+ def day_link(text, date, day_action)
281
+ link_to(text, params.merge(:action => day_action, :year => date.year, :month => date.month, :day => date.day), :class => 'ec-day-link')
282
+ end
283
+
284
+ # check if we should display without a background color
285
+ def no_event_bg?(event, options)
286
+ options[:use_all_day] && !event.all_day && event.days == 0
287
+ end
288
+
289
+ # default html for displaying an event's time
290
+ # to customize: override, or do something similar, in your helper
291
+ def display_event_time(event, day)
292
+ time = event.start_at
293
+ if !event.all_day and time.to_date == day
294
+ # try to make it display as short as possible
295
+ fmt = (time.min == 0) ? "%l" : "%l:%M"
296
+ t = time.strftime(fmt)
297
+ am_pm = time.strftime("%p") == "PM" ? "p" : ""
298
+ t += am_pm
299
+ %(<span class="ec-event-time">#{t}</span>)
300
+ else
301
+ ""
302
+ end
303
+ end
304
+
305
+ private
306
+
307
+ # calculate the height of each row
308
+ # by default, it will be the height option minus the day names height,
309
+ # divided by the total number of calendar rows
310
+ # this gets tricky, however, if there are too many event rows to fit into the row's height
311
+ # then we need to add additional height
312
+ def cal_row_heights(options)
313
+ # number of rows is the number of days in the event strips divided by 7
314
+ num_cal_rows = options[:event_strips].first.size / 7
315
+ # the row will be at least this big
316
+ min_height = (options[:height] - options[:day_names_height]) / num_cal_rows
317
+ row_heights = []
318
+ num_event_rows = 0
319
+ # for every day in the event strip...
320
+ 1.upto(options[:event_strips].first.size+1) do |index|
321
+ num_events = 0
322
+ # get the largest event strip that has an event on this day
323
+ options[:event_strips].each_with_index do |strip, strip_num|
324
+ num_events = strip_num + 1 unless strip[index-1].blank?
325
+ end
326
+ # get the most event rows for this week
327
+ num_event_rows = [num_event_rows, num_events].max
328
+ # if we reached the end of the week, calculate this row's height
329
+ if index % 7 == 0
330
+ total_event_height = options[:event_height] + options[:event_margin]
331
+ calc_row_height = (num_event_rows * total_event_height) + options[:day_nums_height] + options[:event_margin]
332
+ row_height = [min_height, calc_row_height].max
333
+ row_heights << row_height
334
+ num_event_rows = 0
335
+ end
336
+ end
337
+ row_heights
338
+ end
339
+
340
+ #
341
+ # helper methods for working with a calendar week
342
+ #
343
+
344
+ def days_between(first, second)
345
+ if first > second
346
+ second + (7 - first)
347
+ else
348
+ second - first
349
+ end
350
+ end
351
+
352
+ def beginning_of_week(date, start = 0)
353
+ days_to_beg = days_between(start, date.wday)
354
+ date - days_to_beg
355
+ end
356
+
357
+ def end_of_week(date, start = 0)
358
+ beg = beginning_of_week(date, start)
359
+ beg + 6
360
+ end
361
+
362
+ def weekend?(date)
363
+ [0, 6].include?(date.wday)
364
+ end
365
+ end
366
+ end