Module: EventCalendar::CalendarHelper

Defined in:
lib/event_calendar/calendar_helper.rb

Instance Method Summary collapse

Instance Method Details

#calendar(options = {}, &block) ⇒ Object

Returns an HTML calendar which can show multiple, overlapping events across calendar days and rows. Customize using CSS, the below options, and by passing in a code block.

The following are optional, available for customizing the default behaviour: :month => Time.now.month # The month to show the calendar for. Defaults to current month. :year => Time.now.year # The year to show the calendar for. Defaults to current year. :dates => (start_date .. end_date) # Show specific range of days. Defaults to :year, :month. :abbrev => true # Abbreviate day names. Reads from the abbr_day_names key in the localization file. :first_day_of_week => 0 # Renders calendar starting on Sunday. Use 1 for Monday, and so on. :show_today => true # Highlights today on the calendar using CSS class. :show_header => true # Show the calendar’s header. (month name, next, & previous links) :month_name_text => nil # Displayed center in header row.

Defaults to current month name from Date::MONTHNAMES hash.

:previous_month_text => nil # Displayed left of the month name if set :next_month_text => nil # Displayed right of the month name if set :event_strips => [] # An array of arrays, encapsulating the event rows on the calendar

:width => nil # Width of the calendar, if none is set then it will stretch the container’s width :height => 500 # Approx minimum total height of the calendar (excluding the header).

Height could get added if a day has too many event's to fit.

:day_names_height => 18 # Height of the day names table (included in the above ‘height’ option) :day_nums_height => 18 # Height of the day numbers tables (included in the ‘height’ option) :event_height => 18 # Height of an individual event row :event_margin => 1 # Spacing of the event rows :event_padding_top => 1 # Padding on the top of the event rows (increase to move text down)

:use_all_day => false # If set to true, will check for an ‘all_day’ boolean field when displaying an event.

If it is an all day event, or the event is multiple days, then it will display as usual.
Otherwise it will display without a background color bar.

:use_javascript => true # Outputs HTML with inline javascript so events spanning multiple days will be highlighted.

If this option is false, cleaner HTML will be output, but events spanning multiple days will
not be highlighted correctly on hover, so it is only really useful if you know your calendar
will only have single-day events. Defaults to true.

:link_to_day_action => false # If controller action is passed,

the day number will be a link. Override the day_link method for greater customization.

For more customization, you can pass a code block to this method The varibles you have to work with in this block are passed in an agruments hash: :event => The event to be displayed. :day => The day the event is displayed on. Usually the first day of the event, or the first day of the week,

if the event spans a calendar row.

:options => All the calendar options in use. (User defined and defaults merged.)

For example usage, see README.



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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/event_calendar/calendar_helper.rb', line 49

def calendar(options = {}, &block)
  block ||= Proc.new {|d| nil}

  defaults = {
    :year => (Time.zone || Time).now.year,
    :month => (Time.zone || Time).now.month,
    :abbrev => true,
    :first_day_of_week => 0,
    :show_today => true,
    :show_header => true,
    :month_name_text => (Time.zone || Time).now.strftime("%B %Y"),
    :previous_month_text => nil,
    :next_month_text => nil,
    :event_strips => [],

    # it would be nice to have these in the CSS file
    # but they are needed to perform height calculations
    :width => nil,
    :height => 500,
    :day_names_height => 18,
    :day_nums_height => 18,
    :event_height => 18,
    :event_margin => 1,
    :event_padding_top => 2,

    :use_all_day => false,
    :use_javascript => true,
    :link_to_day_action => false
  }
  options = defaults.merge options

  # default month name for the given number
  if options[:show_header]
    options[:month_name_text] ||= I18n.translate(:'date.month_names')[options[:month]]
  end

  # make the height calculations
  # tricky since multiple events in a day could force an increase in the set height
  height = options[:day_names_height]
  row_heights = cal_row_heights(options)
  row_heights.each do |row_height|
    height += row_height
  end

  # the first and last days of this calendar month
  if options[:dates].is_a?(Range)
    first = options[:dates].begin
    last = options[:dates].end
  else
    first = Date.civil(options[:year], options[:month], 1)
    last = Date.civil(options[:year], options[:month], -1)
  end

  # create the day names array [Sunday, Monday, etc...]
  day_names = []
  if options[:abbrev]
    day_names.concat(I18n.translate(:'date.abbr_day_names'))
  else
    day_names.concat(I18n.translate(:'date.day_names'))
  end
  options[:first_day_of_week].times do
    day_names.push(day_names.shift)
  end

  # Build the HTML string
  cal = ""

  # outer calendar container
  cal << %(<div class="ec-calendar")
  cal << %(style="width: #{options[:width]}px;") if options[:width]
  cal << %(>)

  # table header, including the monthname and links to prev & next month
  if options[:show_header]
    cal << %(<table class="ec-calendar-header" cellpadding="0" cellspacing="0">)
    cal << %(<thead><tr>)
    if options[:previous_month_text] or options[:next_month_text]
      cal << %(<th colspan="2" class="ec-month-nav ec-previous-month">#{options[:previous_month_text]}</th>)
      colspan = 3
    else
      colspan = 7
    end
    cal << %(<th colspan="#{colspan}" class="ec-month-name">#{options[:month_name_text]}</th>)
    if options[:next_month_text]
      cal << %(<th colspan="2" class="ec-month-nav ec-next-month">#{options[:next_month_text]}</th>)
    end
    cal << %(</tr></thead></table>)
  end

  # body container (holds day names and the calendar rows)
  cal << %(<div class="ec-body" style="height: #{height}px;">)

  # day names
  cal << %(<table class="ec-day-names" style="height: #{options[:day_names_height]}px;" cellpadding="0" cellspacing="0">)
  cal << %(<tbody><tr>)
  day_names.each do |day_name|
    cal << %(<th class="ec-day-name" title="#{day_name}">#{day_name}</th>)
  end
  cal << %(</tr></tbody></table>)

  # container for all the calendar rows
  cal << %(<div class="ec-rows" style="top: #{options[:day_names_height]}px; )
  cal << %(height: #{height - options[:day_names_height]}px;">)

  # initialize loop variables
  first_day_of_week = beginning_of_week(first, options[:first_day_of_week])
  last_day_of_week = end_of_week(first, options[:first_day_of_week])
  last_day_of_cal = end_of_week(last, options[:first_day_of_week])
  row_num = 0
  top = 0

  # go through a week at a time, until we reach the end of the month
  while(last_day_of_week <= last_day_of_cal)
    cal << %(<div class="ec-row" style="top: #{top}px; height: #{row_heights[row_num]}px;">)
    top += row_heights[row_num]

    # this weeks background table
    cal << %(<table class="ec-row-bg" cellpadding="0" cellspacing="0">)
    cal << %(<tbody><tr>)
    first_day_of_week.upto(first_day_of_week+6) do |day|
      today_class = (day == Date.today) ? "ec-today-bg" : ""
      other_month_class = (day < first) || (day > last) ? 'ec-other-month-bg' : ''
      cal << %(<td class="ec-day-bg #{today_class} #{other_month_class}">&nbsp;</td>)
    end
    cal << %(</tr></tbody></table>)

    # calendar row
    cal << %(<table class="ec-row-table" cellpadding="0" cellspacing="0">)
    cal << %(<tbody>)

    # day numbers row
    cal << %(<tr>)
    first_day_of_week.upto(last_day_of_week) do |day|
      cal << %(<td class="ec-day-header )
      cal << %(ec-today-header ) if options[:show_today] and (day == Date.today)
      cal << %(ec-other-month-header ) if (day < first) || (day > last)
      cal << %(ec-weekend-day-header) if weekend?(day)
      cal << %(" style="height: #{options[:day_nums_height]}px;">)
      if options[:link_to_day_action]
        cal << day_link(day.day, day, options[:link_to_day_action])
      else
        cal << %(#{day.day})
      end
      cal << %(</td>)
    end
    cal << %(</tr>)

    # event rows for this day
    # for each event strip, create a new table row
    options[:event_strips].each do |strip|
      cal << %(<tr>)
      # go through through the strip, for the entries that correspond to the days of this week
      strip[row_num*7, 7].each_with_index do |event, index|
        day = first_day_of_week + index

        if event
          # get the dates of this event that fit into this week
          dates = event.clip_range(first_day_of_week, last_day_of_week)
          # if the event (after it has been clipped) starts on this date,
          # then create a new cell that spans the number of days
          if dates[0] == day.to_date
            # check if we should display the bg color or not
            no_bg = no_event_bg?(event, options)
            class_name = event.class.name.tableize.singularize

            cal << %(<td class="ec-event-cell" colspan="#{(dates[1]-dates[0]).to_i + 1}" )
            cal << %(style="padding-top: #{options[:event_margin]}px;">)
            cal << %(<div class="ec-event ec-#{class_name}-#{event.id} )
            if no_bg
              cal << %(ec-event-no-bg" )
              cal << %(style="color: #{event.color}; )
            else
              cal << %(ec-event-bg" )
              cal << %(style="background-color: #{event.color}; )
            end

            cal << %(padding-top: #{options[:event_padding_top]}px; )
            cal << %(height: #{options[:event_height] - options[:event_padding_top]}px;" )
            if options[:use_javascript]
              # custom attributes needed for javascript event highlighting
              cal << %(data-event-id="#{event.id}" data-event-class="#{class_name}" data-color="#{event.color}" )
            end
            cal << %(>)

            # add a left arrow if event is clipped at the beginning
            if event.start_at.to_date < dates[0]
              cal << %(<div class="ec-left-arrow"></div>)
            end
            # add a right arrow if event is clipped at the end
            if event.end_at.to_date > dates[1]
              cal << %(<div class="ec-right-arrow"></div>)
            end

            if no_bg
              cal << %(<div class="ec-bullet" style="background-color: #{event.color};"></div>)
              # make sure anchor text is the event color
              # here b/c CSS 'inherit' color doesn't work in all browsers
              cal << %(<style type="text/css">.ec-#{class_name}-#{event.id} a { color: #{event.color}; }</style>)
            end

            if block_given?
              # add the additional html that was passed as a block to this helper
              cal << block.call({:event => event, :day => day.to_date, :options => options})
            else
              # default content in case nothing is passed in
              cal << %(<a href="/#{class_name.pluralize}/#{event.id}" title="#{h(event.name)}">#{h(event.name)}</a>)
            end

            cal << %(</div></td>)
          end

        else
          # there wasn't an event, so create an empty cell and container
          cal << %(<td class="ec-event-cell ec-no-event-cell" )
          cal << %(style="padding-top: #{options[:event_margin]}px;">)
          cal << %(<div class="ec-event" )
          cal << %(style="padding-top: #{options[:event_padding_top]}px; )
          cal << %(height: #{options[:event_height] - options[:event_padding_top]}px;" )
          cal << %(>)
          cal << %(&nbsp;</div></td>)
        end
      end
      cal << %(</tr>)
    end

    cal << %(</tbody></table>)
    cal << %(</div>)

    # increment the calendar row we are on, and the week
    row_num += 1
    first_day_of_week += 7
    last_day_of_week += 7
  end

  cal << %(</div>)
  cal << %(</div>)
  cal << %(</div>)
end

override this in your own helper for greater control



289
290
291
# File 'lib/event_calendar/calendar_helper.rb', line 289

def day_link(text, date, day_action)
  link_to(text, params.merge(:action => day_action, :year => date.year, :month => date.month, :day => date.day), :class => 'ec-day-link')
end

#display_event_time(event, day) ⇒ Object

default html for displaying an event’s time to customize: override, or do something similar, in your helper for instance, you may want to add localization



301
302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/event_calendar/calendar_helper.rb', line 301

def display_event_time(event, day)
  time = event.start_at
  if !event.all_day and time.to_date == day
    # try to make it display as short as possible
    format = (time.min == 0) ? "%l" : "%l:%M"
    t = time.strftime(format)
    am_pm = time.strftime("%p") == "PM" ? "p" : ""
    t += am_pm
    %(<span class="ec-event-time">#{t}</span>)
  else
    ""
  end
end

#no_event_bg?(event, options) ⇒ Boolean

check if we should display without a background color

Returns:

  • (Boolean)


294
295
296
# File 'lib/event_calendar/calendar_helper.rb', line 294

def no_event_bg?(event, options)
  options[:use_all_day] && !event.all_day && event.days == 0
end