calendarize 0.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.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +50 -0
- data/Rakefile +38 -0
- data/app/assets/javascripts/calendarize.js.coffee +84 -0
- data/app/helpers/calendarize_helper.rb +629 -0
- data/lib/calendarize.rb +5 -0
- data/lib/calendarize/engine.rb +11 -0
- data/lib/calendarize/version.rb +3 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +5 -0
- data/test/dummy/app/assets/stylesheets/_variables.css.scss +117 -0
- data/test/dummy/app/assets/stylesheets/application.css +4 -0
- data/test/dummy/app/assets/stylesheets/calendarize.css.scss +48 -0
- data/test/dummy/app/assets/stylesheets/tables.css.scss +282 -0
- data/test/dummy/app/controllers/application_controller.rb +9 -0
- data/test/dummy/app/controllers/events_controller.rb +12 -0
- data/test/dummy/app/controllers/welcome_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/event.rb +5 -0
- data/test/dummy/app/views/layouts/application.html.haml +11 -0
- data/test/dummy/app/views/welcome/index.html.haml +40 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +17 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +17 -0
- data/test/dummy/config/environment.rb +2 -0
- data/test/dummy/config/environments/development.rb +13 -0
- data/test/dummy/config/environments/production.rb +11 -0
- data/test/dummy/config/environments/test.rb +13 -0
- data/test/dummy/config/initializers/secret_token.rb +1 -0
- data/test/dummy/config/initializers/session_store.rb +1 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +7 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +5 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/migrate/20111213185551_create_events.rb +11 -0
- data/test/dummy/db/schema.rb +24 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +23316 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/dummy/tmp/cache/assets/C73/050/sprockets%2Fd16f7f8d96d6856e11493371995e1963 +0 -0
- data/test/dummy/tmp/cache/assets/CBA/5C0/sprockets%2F99a55fcc61a90861078b64623dba7755 +0 -0
- data/test/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
- data/test/dummy/tmp/cache/assets/CEE/890/sprockets%2F3e5c71e7d965ac071af5e389981a9018 +0 -0
- data/test/dummy/tmp/cache/assets/D04/120/sprockets%2Fb90e31ee74700b7b9c651fd266108b0b +0 -0
- data/test/dummy/tmp/cache/assets/D1E/BC0/sprockets%2F080adab657299ba98a417cb866e5596d +0 -0
- data/test/dummy/tmp/cache/assets/D2C/470/sprockets%2Fc20be7f875105eb1090b45b4b45b3af4 +0 -0
- data/test/dummy/tmp/cache/assets/D32/5B0/sprockets%2F38ae1e43e5b0cd2272a36df6d0f09074 +0 -0
- data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/test/dummy/tmp/cache/assets/D33/AD0/sprockets%2F7304efbb5a261ea5d15cc96030594f5a +0 -0
- data/test/dummy/tmp/cache/assets/D37/A50/sprockets%2F20f273b3aae704a026aaf9f8738b395c +0 -0
- data/test/dummy/tmp/cache/assets/D3A/3F0/sprockets%2F8db668af6e983e0f8d5679f1a968627f +0 -0
- data/test/dummy/tmp/cache/assets/D3E/310/sprockets%2F4bda6972b0c877d504d246e2cce4e183 +0 -0
- data/test/dummy/tmp/cache/assets/D3E/A90/sprockets%2F4059ec1ce475df14794877ba32b0cd3b +0 -0
- data/test/dummy/tmp/cache/assets/D44/E30/sprockets%2F139a3719f7f8361f76afefb710b1c5d3 +0 -0
- data/test/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/test/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/test/dummy/tmp/cache/assets/D71/BB0/sprockets%2F33958d26d9cad4b8311fae36cf2ff144 +0 -0
- data/test/dummy/tmp/cache/assets/DD0/480/sprockets%2Ffed1aeed65d2e88cb924a521cc431f68 +0 -0
- data/test/dummy/tmp/cache/assets/DD2/820/sprockets%2Ff62ac2c8d56b4409399c1831fbfabdfe +0 -0
- data/test/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
- metadata +194 -0
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright 2012 JODI GIORDANO
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
== Calendarize
|
|
2
|
+
|
|
3
|
+
A easy-to-use calendar helper for your rails projects. Currently supports a weekly and daily calendar.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
== Getting Started
|
|
7
|
+
|
|
8
|
+
1. Add this gem in your *Gemfile* :
|
|
9
|
+
|
|
10
|
+
gem 'calendarize'
|
|
11
|
+
|
|
12
|
+
2. Add an entry in *app/assets/javascripts/application.js* :
|
|
13
|
+
|
|
14
|
+
//= require calendarize
|
|
15
|
+
|
|
16
|
+
3. Add this class method to any *controller* that will have a calendar in it's views :
|
|
17
|
+
|
|
18
|
+
class MyController < ApplicationController
|
|
19
|
+
calendarize
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
4. Add this class method to any *model* that will represent an event in the calendar
|
|
23
|
+
|
|
24
|
+
class MyModel < ActiveRecord::Base
|
|
25
|
+
acts_as_event
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
== Example
|
|
30
|
+
|
|
31
|
+
Here is an example of how to use the calenderize helpers in your views. If you put *calendarize* in your controller, it gives you the *@calendar* variable. Also, in this example I added *acts_as_event* to my model *Event* so I have the scope *for_day*.
|
|
32
|
+
|
|
33
|
+
= daily_calendar @calendar[:date], Event.for_day(@calendar[:date]), verbose: @calendar[:verbose] do |c|
|
|
34
|
+
= l(c.event.start_time, format: :short)
|
|
35
|
+
= ' - '
|
|
36
|
+
= l(c.event.end_time, format: :short)
|
|
37
|
+
= ' : '
|
|
38
|
+
= c.event.title
|
|
39
|
+
|
|
40
|
+
Which will output:
|
|
41
|
+
http://github.com/ephemeregames/calendarize/raw/master/examples/screenshot1.png
|
|
42
|
+
|
|
43
|
+
And with a little bit of CSS:
|
|
44
|
+
http://github.com/ephemeregames/calendarize/raw/master/examples/screenshot2.png
|
|
45
|
+
http://github.com/ephemeregames/calendarize/raw/master/examples/screenshot3.png
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
== TODO
|
|
49
|
+
|
|
50
|
+
- Monthly calendar
|
data/Rakefile
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env rake
|
|
2
|
+
begin
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
rescue LoadError
|
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
6
|
+
end
|
|
7
|
+
begin
|
|
8
|
+
require 'rdoc/task'
|
|
9
|
+
rescue LoadError
|
|
10
|
+
require 'rdoc/rdoc'
|
|
11
|
+
require 'rake/rdoctask'
|
|
12
|
+
RDoc::Task = Rake::RDocTask
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
17
|
+
rdoc.title = 'Calendarize'
|
|
18
|
+
rdoc.options << '--line-numbers'
|
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
Bundler::GemHelper.install_tasks
|
|
27
|
+
|
|
28
|
+
require 'rake/testtask'
|
|
29
|
+
|
|
30
|
+
Rake::TestTask.new(:test) do |t|
|
|
31
|
+
t.libs << 'lib'
|
|
32
|
+
t.libs << 'test'
|
|
33
|
+
t.pattern = 'test/**/*_test.rb'
|
|
34
|
+
t.verbose = false
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
task :default => :test
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
class @DailyCalendar
|
|
2
|
+
|
|
3
|
+
constructor: (id, opts) ->
|
|
4
|
+
@inner = $('#' + id)
|
|
5
|
+
@not_all_day = $('#not_all_day', @inner)
|
|
6
|
+
@first_row = $('.row_content:first', @not_all_day)
|
|
7
|
+
|
|
8
|
+
@options = {
|
|
9
|
+
height: @first_row.outerHeight(true)
|
|
10
|
+
width: 200
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
$.extend(@options, opts)
|
|
14
|
+
|
|
15
|
+
this.init()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
init: =>
|
|
19
|
+
$('.calendar_event', @inner).each (index, element) =>
|
|
20
|
+
e = $(element)
|
|
21
|
+
e.width(@options['width'])
|
|
22
|
+
e.height(@options['height'] * (parseInt(e.data('row-end')) - parseInt(e.data('row-start'))) - 1)
|
|
23
|
+
console.log $('#row_content_' + e.data('row-start'), @not_all_day)
|
|
24
|
+
|
|
25
|
+
e.position({
|
|
26
|
+
my: 'left top',
|
|
27
|
+
at: 'left top',
|
|
28
|
+
of: $('#row_content_' + e.data('row-start'), @not_all_day),
|
|
29
|
+
offset: e.data('column') * (@options['width'] + 3) + ' 0',
|
|
30
|
+
collision: 'none'
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class @WeeklyCalendar
|
|
35
|
+
|
|
36
|
+
constructor: (id, opts) ->
|
|
37
|
+
@inner = $('#' + id)
|
|
38
|
+
|
|
39
|
+
@options = {
|
|
40
|
+
height: 60
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
$.extend(@options, opts)
|
|
44
|
+
|
|
45
|
+
this.init()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
init: =>
|
|
49
|
+
$('.row_unit', @inner).each (index, element) =>
|
|
50
|
+
e = $(element)
|
|
51
|
+
e.height(e.data('events-count') * @options['height'])
|
|
52
|
+
|
|
53
|
+
$('.calendar_event', @inner).each (index, element) =>
|
|
54
|
+
e = $(element)
|
|
55
|
+
|
|
56
|
+
# find the cell to put the event
|
|
57
|
+
cell = $('.row_' + e.data('row') + '.column_' + e.data('column'), @inner)
|
|
58
|
+
|
|
59
|
+
e.width(cell.outerWidth(true))
|
|
60
|
+
e.height(@options['height'])
|
|
61
|
+
|
|
62
|
+
e.position({
|
|
63
|
+
my: 'left top',
|
|
64
|
+
at: 'left top',
|
|
65
|
+
of: $('.row_' + e.data('row') + '.column_' + e.data('column'), @inner),
|
|
66
|
+
offset: '0 ' + e.data('index') * (@options['height']),
|
|
67
|
+
collision: 'none'
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
$(document).ready ->
|
|
72
|
+
|
|
73
|
+
# Initialize every daily calendar on the page
|
|
74
|
+
daily_calendars = []
|
|
75
|
+
|
|
76
|
+
$('.daily_calendar').each (index, element) =>
|
|
77
|
+
daily_calendars << new DailyCalendar(element.id)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# Initialize every weekly calendar on the page
|
|
81
|
+
weekly_calendars = []
|
|
82
|
+
|
|
83
|
+
$('.weekly_calendar').each (index, element) =>
|
|
84
|
+
weekly_calendars << new WeeklyCalendar(element.id)
|
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
module CalendarizeHelper
|
|
2
|
+
|
|
3
|
+
# Creates a daily calendar for events
|
|
4
|
+
#
|
|
5
|
+
# Usage: daily_calendar day, events, [options] { |c| ... }
|
|
6
|
+
#
|
|
7
|
+
# Params
|
|
8
|
+
# :day, Date, the day to display
|
|
9
|
+
# :events, ?, the events to display. An event must responds to:
|
|
10
|
+
# - start_time: a TimeWithZone object (from ActiveRecord)
|
|
11
|
+
# - end_time: a TimeWithZone object (from ActiveRecord)
|
|
12
|
+
# :block |c|, is yield for every event placed so you can customize it's content. :c is the calendar, which have:
|
|
13
|
+
# - @event: the current event that is rendering
|
|
14
|
+
# - @is_all_day: if the current event is an all-day one
|
|
15
|
+
#
|
|
16
|
+
# Options
|
|
17
|
+
# :unit, integer, the time unit in minutes between two rows, defaults to 60. Must be > 0.
|
|
18
|
+
# :date_format, symbol, the format of the date to find at I18n.l('date.formats.date_format'), defaults to :long
|
|
19
|
+
# :url, the url path to call for some actions (ex: previous day), some params will be appended, defaults to request.path
|
|
20
|
+
# :id, integer, id of the calendar, default to one provided by this helper
|
|
21
|
+
# :day_start, integer, when to start showing events on the calendar, in minutes, defaults to 0
|
|
22
|
+
# :day_end, integer, when to stop showing events on the calendar, in minutes, defaults to 1440 (24 hours)
|
|
23
|
+
# :verbose, boolean, show all the rows or only those with events, defaults to true
|
|
24
|
+
#
|
|
25
|
+
def daily_calendar_for(*args, &block)
|
|
26
|
+
DailyCalendarBuilder.new(self, *args).compute.render(&block)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
alias_method :daily_calendar, :daily_calendar_for
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Creates a weekly calendar for events
|
|
33
|
+
#
|
|
34
|
+
# Usage: weekly_calendar day, events, [options] { |c| ... }
|
|
35
|
+
#
|
|
36
|
+
# Params
|
|
37
|
+
# :day, Date, the day to display. Will display the week that contains that day
|
|
38
|
+
# :events, ?, the events to display. An event must responds to:
|
|
39
|
+
# - start_time: a TimeWithZone object (from ActiveRecord)
|
|
40
|
+
# - end_time: a TimeWithZone object (from ActiveRecord)
|
|
41
|
+
# :block |c|, is yield for every event placed so you can customize it's content. :c is the calendar, which have:
|
|
42
|
+
# - @event: the current event that is rendering
|
|
43
|
+
#
|
|
44
|
+
# Options
|
|
45
|
+
# :unit, integer, the time unit in minutes between two rows, defaults to 60. Must be > 0.
|
|
46
|
+
# :date_format, symbol, the format of the date to find at I18n.l('date.formats.date_format'), defaults to :long
|
|
47
|
+
# :id, integer, id of the calendar, default to one provided by this helper
|
|
48
|
+
# :week_start, Date::DAYS_INTO_WEEK.keys or string, the day to start the week, defaults to :monday
|
|
49
|
+
# :week_end, Date::DAYS_INTO_WEEK.keys or string, the day to end the week, defaults to :sunday
|
|
50
|
+
#
|
|
51
|
+
def weekly_calendar_for(*args, &block)
|
|
52
|
+
WeeklyCalendarBuilder.new(self, *args).compute.render(&block)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
alias_method :weekly_calendar, :weekly_calendar_for
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# Returns the current params for a calendar
|
|
59
|
+
# Used when you want to keep track of the calendar between two requests
|
|
60
|
+
# Can be used with path helpers
|
|
61
|
+
#
|
|
62
|
+
# Usage: resource_path(calendar_current_params)
|
|
63
|
+
#
|
|
64
|
+
def calendar_current_params
|
|
65
|
+
{ calendar: @calendar.clone }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# An enum object that hold the possible scopes for a calendar
|
|
70
|
+
# The scope can be used in a calendar view to know which calendar to show
|
|
71
|
+
#
|
|
72
|
+
class Scopes
|
|
73
|
+
DAILY = 'daily'
|
|
74
|
+
WEEKLY = 'weekly'
|
|
75
|
+
MONTHLY = 'monthly'
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# Methods used in an existing controller to get the params associated to a calendar and pass them to the view
|
|
80
|
+
# Automatically included by the engine as a class helper
|
|
81
|
+
# Right now it only supports one calendar per view but it should not be hard to throw some :uuid in the process
|
|
82
|
+
#
|
|
83
|
+
# Usage:
|
|
84
|
+
#
|
|
85
|
+
# class MyCalendarController < ApplicationController
|
|
86
|
+
# calendarize
|
|
87
|
+
# end
|
|
88
|
+
#
|
|
89
|
+
module Controller
|
|
90
|
+
|
|
91
|
+
def calendarize
|
|
92
|
+
|
|
93
|
+
before_filter lambda {
|
|
94
|
+
@calendar = { }
|
|
95
|
+
|
|
96
|
+
# I hate time handling in RoR...
|
|
97
|
+
# Time.zone.parse is prefered to DateTime.parse because it preserve the timezone
|
|
98
|
+
|
|
99
|
+
if params[:calendar]
|
|
100
|
+
@calendar[:date] = Time.zone.parse(params[:calendar][:date]).to_datetime
|
|
101
|
+
@calendar[:verbose] = params[:calendar][:verbose] == 'true'
|
|
102
|
+
@calendar[:scope] = params[:calendar][:scope]
|
|
103
|
+
else
|
|
104
|
+
@calendar[:date] = DateTime.now.beginning_of_day
|
|
105
|
+
@calendar[:verbose] = true
|
|
106
|
+
@calendar[:scope] = 'daily'
|
|
107
|
+
end
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# Methods used in an ActiveRecord model to make it compatible with the library
|
|
116
|
+
# Note: A model must respond to :start_time and :end_time (:datetime fields)
|
|
117
|
+
#
|
|
118
|
+
# Usage:
|
|
119
|
+
#
|
|
120
|
+
# class MyEventModel < ActiveRecord::Base
|
|
121
|
+
# acts_as_event
|
|
122
|
+
# end
|
|
123
|
+
#
|
|
124
|
+
#
|
|
125
|
+
# class MyEventsMigration < ActiveRecord::Migration
|
|
126
|
+
# def change
|
|
127
|
+
# create_table :my_events do |t|
|
|
128
|
+
# t.datetime :start_time
|
|
129
|
+
# t.datetime :end_time
|
|
130
|
+
# end
|
|
131
|
+
# end
|
|
132
|
+
# end
|
|
133
|
+
#
|
|
134
|
+
# I did not want to dictate how your event model should be; that's why you must
|
|
135
|
+
# create your event model and your migration. However, once this is done, you
|
|
136
|
+
# can use :acts_as_event to ease the process. It will add the scopes needed to
|
|
137
|
+
# send the events to the calendars.
|
|
138
|
+
#
|
|
139
|
+
# Note: You can have an event model that is not persisted in your database.
|
|
140
|
+
# As long as your model responds to the two methods :start_time and :end_time,
|
|
141
|
+
# you're OK. However, :acts_as_event won't help you in that case.
|
|
142
|
+
#
|
|
143
|
+
module Model
|
|
144
|
+
|
|
145
|
+
module ActsAsEvent
|
|
146
|
+
|
|
147
|
+
def acts_as_event
|
|
148
|
+
scope :for_day, lambda { |date = nil| where('start_time >= ? AND start_time <= ?', date ? date : DateTime.now.beginning_of_day, date ? date.end_of_day : DateTime.now.end_of_day) }
|
|
149
|
+
scope :for_week, lambda { |date = nil, week_start = :monday|
|
|
150
|
+
where(
|
|
151
|
+
'start_time >= ? AND start_time < ?',
|
|
152
|
+
date ? date.to_time.beginning_of_week(week_start) : DateTime.now.beginning_of_week(week_start),
|
|
153
|
+
date ? date.to_time.end_of_week(week_start) : DateTime.now.end_of_week(week_start)
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class ActiveRecord::Base
|
|
162
|
+
|
|
163
|
+
extend ActsAsEvent
|
|
164
|
+
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
private
|
|
171
|
+
|
|
172
|
+
class AbstractCalendarBuilder < ActionView::Base
|
|
173
|
+
|
|
174
|
+
@@uuid = 100
|
|
175
|
+
|
|
176
|
+
attr_reader :event, :is_all_day
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def initialize(view_context, *args)
|
|
180
|
+
opts = args.extract_options!
|
|
181
|
+
|
|
182
|
+
@view_context = view_context
|
|
183
|
+
|
|
184
|
+
@options = {
|
|
185
|
+
unit: 60,
|
|
186
|
+
date_format: :long,
|
|
187
|
+
url: @view_context.request.path,
|
|
188
|
+
verbose: true,
|
|
189
|
+
day_start: 0,
|
|
190
|
+
day_end: 1440
|
|
191
|
+
}.merge!(opts)
|
|
192
|
+
|
|
193
|
+
@day = args.shift || Date.today
|
|
194
|
+
@events = args.shift || []
|
|
195
|
+
@event = nil # can be accessed by the passed block
|
|
196
|
+
@is_all_day = false
|
|
197
|
+
|
|
198
|
+
@@uuid += 1
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def compute
|
|
203
|
+
day_time = @day.to_datetime
|
|
204
|
+
|
|
205
|
+
# Get the starting and the ending row of the calendar
|
|
206
|
+
# :starting_row and :ending_row are in minutes so if :starting_row == 60
|
|
207
|
+
# and the :unit == 60, that means one row equals 1 hour and we start the
|
|
208
|
+
# rows at hour 1.
|
|
209
|
+
@_starting_row = @options[:day_start] / @options[:unit]
|
|
210
|
+
@_ending_row = @options[:day_end] / @options[:unit]
|
|
211
|
+
|
|
212
|
+
# Get the rows (in :datetime)
|
|
213
|
+
# The indexes will be used as :ids in the rendering
|
|
214
|
+
@rows = (@_starting_row...@_ending_row).to_a
|
|
215
|
+
@rows_hours = @rows.map { |r| day_time + r * @options[:unit] / 1440.0 }
|
|
216
|
+
|
|
217
|
+
# Sort the events in ascending order of their :start_time
|
|
218
|
+
# Note: not done in-place the first time so the user can re-use it's collection
|
|
219
|
+
@events = @events.sort { |e1, e2| e1.start_time <=> e2.start_time }
|
|
220
|
+
|
|
221
|
+
self
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def render
|
|
226
|
+
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
protected
|
|
231
|
+
|
|
232
|
+
def row(time_with_zone)
|
|
233
|
+
(time_with_zone.to_datetime.hour * 60 + time_with_zone.to_datetime.min)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def row_unit(time_with_zone)
|
|
238
|
+
row(time_with_zone) / @options[:unit]
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def to_query_params(options = {})
|
|
243
|
+
{
|
|
244
|
+
calendar: {
|
|
245
|
+
date: Date.today,
|
|
246
|
+
verbose: @options[:verbose],
|
|
247
|
+
scope: @options[:scope]
|
|
248
|
+
}.merge(options)
|
|
249
|
+
}.to_query
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class DailyCalendarBuilder < AbstractCalendarBuilder
|
|
256
|
+
|
|
257
|
+
def initialize(view_context, *args)
|
|
258
|
+
|
|
259
|
+
opts = {
|
|
260
|
+
id: "daily_calendar_#{@@uuid}",
|
|
261
|
+
scope: 'daily'
|
|
262
|
+
}.merge!(args.extract_options!)
|
|
263
|
+
|
|
264
|
+
args << opts
|
|
265
|
+
|
|
266
|
+
super(view_context, *args)
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def compute
|
|
271
|
+
|
|
272
|
+
super
|
|
273
|
+
|
|
274
|
+
# Partition the events between all-day and :not all-day events
|
|
275
|
+
# Then, for the :not all-day events, put them in rows
|
|
276
|
+
rows_events = {}
|
|
277
|
+
@all_day_events, not_all_day_events = @events.partition { |e| e.end_time.to_date > @day }
|
|
278
|
+
|
|
279
|
+
not_all_day_events.each do |e|
|
|
280
|
+
row_unit = row_unit(e.start_time)
|
|
281
|
+
|
|
282
|
+
if !rows_events.has_key?(row_unit)
|
|
283
|
+
rows_events[row_unit] = [e]
|
|
284
|
+
else
|
|
285
|
+
rows_events[row_unit] << e
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# We remove the events that:
|
|
290
|
+
# - start or end before the :starting_row
|
|
291
|
+
# - start or end after the :ending_row
|
|
292
|
+
rows_events.each_value { |r| r.reject! { |e| row_unit(e.start_time) >= @_ending_row || row_unit(e.start_time) < @_starting_row } }
|
|
293
|
+
rows_events.each_value { |r| r.reject! { |e| row_unit(e.end_time) >= @_ending_row || row_unit(e.end_time) < @_starting_row } }
|
|
294
|
+
|
|
295
|
+
# Sort each row (that now contains events) by the duration of the event
|
|
296
|
+
rows_events.each_value { |r| r.sort! { |e1, e2| e2.end_time - e2.start_time <=> e1.end_time - e1.start_time } }
|
|
297
|
+
|
|
298
|
+
# Put the events in columns, which give a tuple '[[row_start, row_end, column], event]' for the event
|
|
299
|
+
# that will be used to output the calendar. The algorithm works like this:
|
|
300
|
+
# - As we cycle in the rows (which contains events that start at the same :unit time), we create columns and
|
|
301
|
+
# we keep track of what we put in the columns
|
|
302
|
+
# - When to decide in which column to put an event, we start from the column 0 and check the :end_time of the
|
|
303
|
+
# last event inserted in that column. If this :end_time is less than the :start_time of the event we want
|
|
304
|
+
# to insert, we have a candidate!
|
|
305
|
+
columns = {}
|
|
306
|
+
@placed_events = []
|
|
307
|
+
|
|
308
|
+
rows_events.each do |i,v|
|
|
309
|
+
v.each do |e|
|
|
310
|
+
j = 0
|
|
311
|
+
placed = false
|
|
312
|
+
|
|
313
|
+
while (!placed) do
|
|
314
|
+
if !columns.has_key?(j)
|
|
315
|
+
columns[j] = [e]
|
|
316
|
+
@placed_events << [[i, i + ((e.end_time - e.start_time) / (60 * @options[:unit])).ceil, j], e]
|
|
317
|
+
placed = true
|
|
318
|
+
elsif (row(e.start_time) >= row(columns[j].last.end_time))
|
|
319
|
+
columns[j] << e
|
|
320
|
+
@placed_events << [[i, i + ((e.end_time - e.start_time) / (60 * @options[:unit])).ceil, j], e]
|
|
321
|
+
placed = true
|
|
322
|
+
else
|
|
323
|
+
j += 1
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# We make a list of all the rows to render
|
|
330
|
+
# If we set the option :verbose to false, we only show the rows that have events
|
|
331
|
+
occupied_rows = []
|
|
332
|
+
@placed_events.each { |tuple| occupied_rows << (tuple[0][0]...tuple[0][1]).to_a }
|
|
333
|
+
occupied_rows.flatten!
|
|
334
|
+
occupied_rows.uniq!
|
|
335
|
+
@rows_to_render_indexes = @options[:verbose] ? @rows.map.with_index { |x, i| i } : occupied_rows.map { |r| @rows.index(r) }
|
|
336
|
+
|
|
337
|
+
self
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def render(&block)
|
|
342
|
+
content_tag(:div, class: 'daily_calendar', id: @options[:id], style: 'position: relative;') do
|
|
343
|
+
|
|
344
|
+
tables = ''.html_safe
|
|
345
|
+
|
|
346
|
+
# controls
|
|
347
|
+
tables << content_tag(:table, id: 'controls', class: 'styled', style: 'width: 100%;') do
|
|
348
|
+
content_tag(:thead) do
|
|
349
|
+
content_tag(:tr) do
|
|
350
|
+
content = ''.html_safe
|
|
351
|
+
content << content_tag(:th, style: 'width: 33%;') { link_to(@options[:verbose] ? I18n.t('calendarize.daily_calendar.options.verbose', default: 'Compact') : I18n.t('calendarize.daily_calendar.options.not_verbose', default: 'Full'), @options[:url] + '?' + to_query_params({ date: @day.to_date, verbose: !@options[:verbose] })) }
|
|
352
|
+
content << content_tag(:th, style: 'width: 33%;') { I18n.l(@day.to_date, format: @options[:date_format]) }
|
|
353
|
+
content << content_tag(:th, style: 'width: 33%;') do
|
|
354
|
+
options = ''.html_safe
|
|
355
|
+
options << link_to(I18n.t('calendarize.daily_calendar.options.previous_day', default: 'Previous day'), @options[:url] + '?' + to_query_params({ date: @day.yesterday.to_date }))
|
|
356
|
+
options << ' | '
|
|
357
|
+
options << link_to(I18n.t('calendarize.daily_calendar.options.today', default: 'Today'), @options[:url] + '?' + to_query_params)
|
|
358
|
+
options << ' | '
|
|
359
|
+
options << link_to(I18n.t('calendarize.daily_calendar.options.next_day', default: 'Next day'), @options[:url] + '?' + to_query_params({ date: @day.tomorrow.to_date }))
|
|
360
|
+
options
|
|
361
|
+
end
|
|
362
|
+
content
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# all day events
|
|
368
|
+
tables << content_tag(:table, id: 'all_day', class: 'styled', style: 'width: 100%;') do
|
|
369
|
+
content = ''.html_safe
|
|
370
|
+
content << content_tag(:thead) do
|
|
371
|
+
content_tag(:tr) do
|
|
372
|
+
content_tag(:th) { I18n.t('calendarize.daily_calendar.all_day_events', default: 'All day events') }
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
content << content_tag(:tbody) do
|
|
377
|
+
|
|
378
|
+
trs = ''.html_safe
|
|
379
|
+
|
|
380
|
+
@all_day_events.each_index do |i|
|
|
381
|
+
trs << content_tag(:tr) do
|
|
382
|
+
@event = @all_day_events[i]
|
|
383
|
+
@is_all_day = true
|
|
384
|
+
content_tag(:td, class: 'row_content', id: "row_content_#{i}") { @view_context.capture(self, &block) }
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
trs
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
content
|
|
392
|
+
end unless @all_day_events.empty?
|
|
393
|
+
|
|
394
|
+
# normal events
|
|
395
|
+
tables << content_tag(:table, id: 'not_all_day', class: 'styled', style: 'width: 100%;') do
|
|
396
|
+
content = ''.html_safe
|
|
397
|
+
content << content_tag(:thead) do
|
|
398
|
+
content_tag(:tr) do
|
|
399
|
+
header = ''.html_safe
|
|
400
|
+
header << content_tag(:th, style: 'width: 40px;') { I18n.t('calendarize.daily_calendar.hours', default: 'Hours') }
|
|
401
|
+
header << content_tag(:th) { }
|
|
402
|
+
header
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
content << content_tag(:tbody) do
|
|
407
|
+
|
|
408
|
+
trs = ''.html_safe
|
|
409
|
+
|
|
410
|
+
#raise @rows.inspect
|
|
411
|
+
@rows_to_render_indexes.each do |i|
|
|
412
|
+
trs << content_tag(:tr, class: 'row_unit', style: 'height: 50px;') do
|
|
413
|
+
tds = ''.html_safe
|
|
414
|
+
tds << content_tag(:td, class: 'row_header', id: "row_header_#{@rows[i]}", style: 'width: 40px') { @rows_hours[i].strftime('%H:%M') }
|
|
415
|
+
tds << content_tag(:td, class: 'row_content', id: "row_content_#{@rows[i]}") { }
|
|
416
|
+
tds
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
trs
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
content
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
# place the events at the end of the calendar
|
|
427
|
+
# they will be placed at the right place on the calendar with some javascript magic
|
|
428
|
+
@placed_events.each do |e|
|
|
429
|
+
#raise e.inspect
|
|
430
|
+
@event = e[1]
|
|
431
|
+
@is_all_day = false
|
|
432
|
+
tables << content_tag(:div, class: 'calendar_event', data: { row_start: e[0][0], row_end: e[0][1], column: e[0][2] }, style: 'z-index: 1;') do
|
|
433
|
+
content_tag(:div, class: 'content') { @view_context.capture(self, &block) }
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
tables
|
|
438
|
+
end
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
class WeeklyCalendarBuilder < AbstractCalendarBuilder
|
|
445
|
+
|
|
446
|
+
def initialize(view_context, *args)
|
|
447
|
+
|
|
448
|
+
opts = {
|
|
449
|
+
id: "weekly_calendar_#{@@uuid}",
|
|
450
|
+
week_start: :monday,
|
|
451
|
+
week_end: :sunday,
|
|
452
|
+
scope: 'weekly'
|
|
453
|
+
}.merge!(args.extract_options!)
|
|
454
|
+
|
|
455
|
+
opts[:week_start] = opts[:week_start].to_sym
|
|
456
|
+
opts[:week_end] = opts[:week_end].to_sym
|
|
457
|
+
|
|
458
|
+
args << opts
|
|
459
|
+
|
|
460
|
+
super(view_context, *args)
|
|
461
|
+
|
|
462
|
+
# We calculate the number of days between :week_start and :week_end
|
|
463
|
+
ws = Date::DAYS_INTO_WEEK[@options[:week_start]]
|
|
464
|
+
we = Date::DAYS_INTO_WEEK[@options[:week_end]]
|
|
465
|
+
we += 7 if we <= ws
|
|
466
|
+
|
|
467
|
+
@day_start = @day.beginning_of_week(@options[:week_start]).to_date
|
|
468
|
+
@day_end = @day_start + (we - ws)
|
|
469
|
+
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def compute
|
|
474
|
+
|
|
475
|
+
super
|
|
476
|
+
|
|
477
|
+
# We put events in rows
|
|
478
|
+
@_rows_events = {}
|
|
479
|
+
|
|
480
|
+
@events.each do |e|
|
|
481
|
+
row_unit = row_unit(e.start_time)
|
|
482
|
+
|
|
483
|
+
if !@_rows_events.has_key?(row_unit)
|
|
484
|
+
@_rows_events[row_unit] = [e]
|
|
485
|
+
else
|
|
486
|
+
@_rows_events[row_unit] << e
|
|
487
|
+
end
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
# We remove the events that:
|
|
491
|
+
# - start or end before the :starting_row
|
|
492
|
+
# - start or end after the :ending_row
|
|
493
|
+
@_rows_events.each_value { |r| r.reject! { |e| row_unit(e.start_time) >= @_ending_row || row_unit(e.start_time) < @_starting_row } }
|
|
494
|
+
@_rows_events.each_value { |r| r.reject! { |e| row_unit(e.end_time) >= @_ending_row || row_unit(e.end_time) < @_starting_row } }
|
|
495
|
+
|
|
496
|
+
# Sort each row (that now contains events) by the duration of the event
|
|
497
|
+
@_rows_events.each_value { |r| r.sort! { |e1, e2| e2.end_time - e2.start_time <=> e1.end_time - e1.start_time } }
|
|
498
|
+
|
|
499
|
+
# We remove the events that are outside the days watched
|
|
500
|
+
@_rows_events.each_value { |r| r.reject! { |e| e.start_time.to_date < @day_start || e.start_time.to_date > @day_end } }
|
|
501
|
+
|
|
502
|
+
@days_shown = (@day_start..@day_end).to_a
|
|
503
|
+
|
|
504
|
+
# Put the events in columns, which give a tuple '[[row, column, index], event]' for the event
|
|
505
|
+
# that will be used to output the calendar. The :index is used to identify an event on a same
|
|
506
|
+
# :row and :column
|
|
507
|
+
@placed_events = []
|
|
508
|
+
|
|
509
|
+
@_rows_events.each do |i, v|
|
|
510
|
+
columns = {}
|
|
511
|
+
v.each { |e| columns[column(e.start_time)] = 0 }
|
|
512
|
+
|
|
513
|
+
v.each do |e|
|
|
514
|
+
column = column(e.start_time)
|
|
515
|
+
|
|
516
|
+
@placed_events << [[i, column, columns[column]], e]
|
|
517
|
+
|
|
518
|
+
columns[column] += 1
|
|
519
|
+
end
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
# We make a list of all the rows to render
|
|
523
|
+
# If we set the option :verbose to false, we only show the rows that have events
|
|
524
|
+
occupied_rows = []
|
|
525
|
+
@placed_events.each { |tuple| occupied_rows << tuple[0][0] }
|
|
526
|
+
occupied_rows.uniq!
|
|
527
|
+
|
|
528
|
+
@rows_to_render_indexes = @options[:verbose] ? @rows.map.with_index { |x, i| i } : occupied_rows.map { |r| @rows.index(r) }
|
|
529
|
+
|
|
530
|
+
self
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
def render(&block)
|
|
535
|
+
content_tag(:div, class: 'weekly_calendar', id: @options[:id], style: 'position: relative;') do
|
|
536
|
+
|
|
537
|
+
tables = ''.html_safe
|
|
538
|
+
|
|
539
|
+
# controls
|
|
540
|
+
tables << content_tag(:table, id: 'controls', class: 'styled', style: 'width: 100%;') do
|
|
541
|
+
content_tag(:thead) do
|
|
542
|
+
content_tag(:tr) do
|
|
543
|
+
content = ''.html_safe
|
|
544
|
+
content << content_tag(:th, style: 'width: 33%;') { link_to(@options[:verbose] ? I18n.t('calendarize.weekly_calendar.options.verbose', default: 'Compact') : I18n.t('calendarize.weekly_calendar.options.not_verbose', default: 'Full'), @options[:url] + '?' + to_query_params({ date: @day.to_date, verbose: !@options[:verbose] })) }
|
|
545
|
+
content << content_tag(:th, style: 'width: 33%;') { "#{I18n.t('calendarize.weekly_calendar.week_of', default: 'Week of')} #{ I18n.l(@day_start.to_date, format: @options[:date_format]) }" }
|
|
546
|
+
content << content_tag(:th, style: 'width: 33%;') do
|
|
547
|
+
options = ''.html_safe
|
|
548
|
+
options << link_to(I18n.t('calendarize.weekly_calendar.options.previous_week', default: 'Previous week'), @options[:url] + '?' + to_query_params({ date: @day_start.prev_week(@options[:week_start]).to_date }))
|
|
549
|
+
options << ' | '
|
|
550
|
+
options << link_to(I18n.t('calendarize.weekly_calendar.options.current_week', default: 'This week'), @options[:url] + '?' + to_query_params)
|
|
551
|
+
options << ' | '
|
|
552
|
+
options << link_to(I18n.t('calendarize.weekly_calendar.options.next_week', default: 'Next week'), @options[:url] + '?' + to_query_params({ date: @day_end.next_week(@options[:week_start]).to_date }))
|
|
553
|
+
options
|
|
554
|
+
end
|
|
555
|
+
content
|
|
556
|
+
end
|
|
557
|
+
end
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
# normal events
|
|
561
|
+
tables << content_tag(:table, id: 'not_all_day', class: 'styled', style: 'width: 100%;') do
|
|
562
|
+
content = ''.html_safe
|
|
563
|
+
content << content_tag(:thead) do
|
|
564
|
+
content_tag(:tr) do
|
|
565
|
+
header = ''.html_safe
|
|
566
|
+
|
|
567
|
+
header << content_tag(:th, style: 'width: 40px;') { I18n.t('calendarize.weekly_calendar.hours', default: 'Hours') }
|
|
568
|
+
|
|
569
|
+
@days_shown.each do |d|
|
|
570
|
+
header << content_tag(:th) {
|
|
571
|
+
link_to(I18n.l(d, format: @options[:date_format]), @options[:url] + '?' + to_query_params({ date: d, verbose: @options[:verbose], scope: CalendarizeHelper::Scopes::DAILY }))
|
|
572
|
+
}
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
header
|
|
576
|
+
end
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
content << content_tag(:tbody) do
|
|
580
|
+
|
|
581
|
+
trs = ''.html_safe
|
|
582
|
+
|
|
583
|
+
@rows_to_render_indexes.each do |i|
|
|
584
|
+
trs << content_tag(:tr, class: 'row_unit', data: { events_count: @_rows_events.include?(@rows[i]) ? @_rows_events[@rows[i]].map{ |e| column(e.start_time) }.group_by{ |i| i }.map{ |k, v| v.count }.max : 0 }) do
|
|
585
|
+
tds = ''.html_safe
|
|
586
|
+
|
|
587
|
+
tds << content_tag(:td, class: 'row_header', id: "row_header_#{@rows[i]}", style: 'width: 40px') { @rows_hours[i].strftime('%H:%M') }
|
|
588
|
+
|
|
589
|
+
@days_shown.each_index do |j|
|
|
590
|
+
tds << content_tag(:td, class: ["row_#{@rows[i]}", "column_#{j}"]) { }
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
tds
|
|
594
|
+
end
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
trs
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
content
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
# place the events at the end of the calendar
|
|
604
|
+
# they will be placed at the right place on the calendar with some javascript magic
|
|
605
|
+
@placed_events.each do |e|
|
|
606
|
+
@event = e[1]
|
|
607
|
+
@is_all_day = false
|
|
608
|
+
tables << content_tag(:div, class: 'calendar_event', data: { row: e[0][0], column: e[0][1], index: e[0][2] }, style: 'z-index: 1;') do
|
|
609
|
+
content_tag(:div, class: 'content') { @view_context.capture(self, &block) }
|
|
610
|
+
end
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
tables
|
|
614
|
+
end
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
private
|
|
619
|
+
|
|
620
|
+
def column(time_with_zone)
|
|
621
|
+
ws = Date::DAYS_INTO_WEEK[@options[:week_start]]
|
|
622
|
+
we = time_with_zone.strftime('%u').to_i - 1
|
|
623
|
+
we += 7 if we <= ws
|
|
624
|
+
(we - ws) % 7
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
end
|