elevation_event_calendar 1.0.0

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