radiant-event_calendar-extension 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/.gitmodules +0 -0
  2. data/README.md +198 -0
  3. data/Rakefile +139 -0
  4. data/VERSION +1 -0
  5. data/app/controllers/admin/calendars_controller.rb +18 -0
  6. data/app/controllers/admin/event_venues_controller.rb +3 -0
  7. data/app/controllers/admin/events_controller.rb +9 -0
  8. data/app/controllers/admin/icals_controller.rb +26 -0
  9. data/app/controllers/events_controller.rb +201 -0
  10. data/app/models/calendar.rb +57 -0
  11. data/app/models/event.rb +409 -0
  12. data/app/models/event_calendar_page.rb +138 -0
  13. data/app/models/event_occurrence.rb +89 -0
  14. data/app/models/event_recurrence_rule.rb +85 -0
  15. data/app/models/event_venue.rb +17 -0
  16. data/app/models/ical.rb +132 -0
  17. data/app/views/admin/calendars/_actions.html.haml +6 -0
  18. data/app/views/admin/calendars/_form.html.haml +56 -0
  19. data/app/views/admin/calendars/edit.html.haml +11 -0
  20. data/app/views/admin/calendars/help.html.erb +22 -0
  21. data/app/views/admin/calendars/index.html.haml +60 -0
  22. data/app/views/admin/calendars/new.html.haml +10 -0
  23. data/app/views/admin/calendars/show.html.haml +36 -0
  24. data/app/views/admin/event_venues/_event_venue.html.haml +18 -0
  25. data/app/views/admin/event_venues/_form.html.haml +44 -0
  26. data/app/views/admin/event_venues/edit.html.haml +32 -0
  27. data/app/views/admin/event_venues/index.html.haml +33 -0
  28. data/app/views/admin/event_venues/new.html.haml +32 -0
  29. data/app/views/admin/event_venues/remove.html.haml +17 -0
  30. data/app/views/admin/events/_event.html.haml +47 -0
  31. data/app/views/admin/events/_form.html.haml +116 -0
  32. data/app/views/admin/events/_list_head.html.haml +18 -0
  33. data/app/views/admin/events/edit.html.haml +6 -0
  34. data/app/views/admin/events/index.html.haml +23 -0
  35. data/app/views/admin/events/new.html.haml +6 -0
  36. data/app/views/admin/events/remove.html.haml +17 -0
  37. data/app/views/admin/icals/refresh.html.haml +11 -0
  38. data/app/views/admin/icals/refresh_all.html.haml +0 -0
  39. data/app/views/events/_defacet.html.haml +2 -0
  40. data/app/views/events/_event.html.haml +29 -0
  41. data/app/views/events/_event_postscript.html.haml +0 -0
  42. data/app/views/events/_faceting.html.haml +27 -0
  43. data/app/views/events/_minicalendar.html.haml +49 -0
  44. data/app/views/events/_other_page_parts.html.haml +0 -0
  45. data/app/views/events/_views.html.haml +6 -0
  46. data/app/views/events/index.html.haml +56 -0
  47. data/app/views/events/index.ics.erb +7 -0
  48. data/app/views/events/index.rss.builder +20 -0
  49. data/config/routes.rb +13 -0
  50. data/db/migrate/001_create_calendar_and_events.rb +21 -0
  51. data/db/migrate/002_calendar_add_ical_url.rb +9 -0
  52. data/db/migrate/003_add_calendar_category.rb +9 -0
  53. data/db/migrate/004_add_slug.rb +9 -0
  54. data/db/migrate/005_add_subscription_refresh_history.rb +11 -0
  55. data/db/migrate/006_create_icals.rb +13 -0
  56. data/db/migrate/007_move_subscriptions_to_ical.rb +15 -0
  57. data/db/migrate/008_clean_out_calendar.rb +12 -0
  58. data/db/migrate/009_basic_authentication.rb +12 -0
  59. data/db/migrate/010_refresh_interval.rb +9 -0
  60. data/db/migrate/011_more_properties.rb +8 -0
  61. data/db/migrate/20090818133511_simpler_ical_columns.rb +17 -0
  62. data/db/migrate/20090819130919_ownership.rb +17 -0
  63. data/db/migrate/20090820073805_site_scope.rb +13 -0
  64. data/db/migrate/20091118100725_event_status.rb +9 -0
  65. data/db/migrate/20100216080944_more_event_data.rb +31 -0
  66. data/db/migrate/20100218131410_recurrence_parts.rb +19 -0
  67. data/db/migrate/20100219102227_venues_and_categories.rb +23 -0
  68. data/db/migrate/20100221180539_recurrence_rules.rb +34 -0
  69. data/db/migrate/20100222182112_occurrences.rb +13 -0
  70. data/event_calendar_extension.rb +32 -0
  71. data/lib/calendar_period.rb +151 -0
  72. data/lib/event_calendar_admin_ui.rb +78 -0
  73. data/lib/event_calendar_tags.rb +1021 -0
  74. data/lib/event_search.rb +22 -0
  75. data/lib/event_statuses.rb +24 -0
  76. data/lib/tasks/event_calendar_extension_tasks.rake +29 -0
  77. data/pkg/radiant-event_calendar-extension-1.0.0.gem +0 -0
  78. data/public/icals/blank +0 -0
  79. data/public/images/admin/calendar.png +0 -0
  80. data/public/images/event_calendar/calendarlinkbg.png +0 -0
  81. data/public/images/event_calendar/event_shadow.png +0 -0
  82. data/public/images/event_calendar/ical16.png +0 -0
  83. data/public/images/event_calendar/maplinkbg.png +0 -0
  84. data/public/images/event_calendar/one_event.png +0 -0
  85. data/public/images/event_calendar/several_events.png +0 -0
  86. data/public/javascripts/admin/event_calendar.js +240 -0
  87. data/public/stylesheets/sass/admin/event_calendar.sass +266 -0
  88. data/public/stylesheets/sass/admin/modules/_grid.sass +56 -0
  89. data/public/stylesheets/sass/constants.sass +80 -0
  90. data/public/stylesheets/sass/event_calendar.sass +203 -0
  91. data/radiant-event_calendar-extension.gemspec +166 -0
  92. data/spec/datasets/calendar_events_dataset.rb +43 -0
  93. data/spec/datasets/calendar_pages_dataset.rb +8 -0
  94. data/spec/datasets/calendar_sites_dataset.rb +6 -0
  95. data/spec/datasets/calendars_dataset.rb +34 -0
  96. data/spec/datasets/recurrence_dataset.rb +7 -0
  97. data/spec/files/dummy.ics +59 -0
  98. data/spec/files/ny.ics +36 -0
  99. data/spec/lib/event_calendar_page_spec.rb +24 -0
  100. data/spec/models/calendar_spec.rb +11 -0
  101. data/spec/models/event_spec.rb +98 -0
  102. data/spec/models/ical_spec.rb +63 -0
  103. data/spec/models/recurrence_rule_spec.rb +82 -0
  104. data/spec/spec.opts +6 -0
  105. data/spec/spec_helper.rb +36 -0
  106. metadata +238 -0
@@ -0,0 +1,22 @@
1
+ class EventSearch
2
+ attr_accessor :category, :calendars, :slugs, :period
3
+
4
+ def initialize
5
+ Ical.check_refreshments
6
+ @period = CalendarPeriod.new
7
+ @slugs = ["all"]
8
+ end
9
+
10
+ def slugs=(new_slugs)
11
+ unless new_slugs.blank?
12
+ @slugs = new_slugs.split("|").collect { |s| s.downcase }
13
+ end
14
+ end
15
+
16
+ def execute
17
+ search = Event.between(@period.begin_date, @period.end_date)
18
+ search = search.in_calendars(slugs) unless slugs == 'all' || slugs.nil?
19
+ search.find(:all, :include => :calendar, :order => "start_date ASC")
20
+ end
21
+
22
+ end
@@ -0,0 +1,24 @@
1
+ module EventStatuses
2
+ def self.included(base)
3
+ base.class_eval {
4
+ cattr_accessor :statuses
5
+ extend ClassMethods
6
+ include InstanceMethods
7
+ }
8
+
9
+ base.add_status(:id => 5, :name => 'Submitted')
10
+ base.add_status(:id => 200, :name => 'Imported')
11
+ end
12
+
13
+ module ClassMethods
14
+ def add_status(properties)
15
+ statuses.push(Status.new(properties))
16
+ end
17
+ end
18
+
19
+ module InstanceMethods
20
+ def to_s
21
+ id
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,29 @@
1
+ namespace :radiant do
2
+ namespace :extensions do
3
+ namespace :event_calendar do
4
+
5
+ desc "Runs the migration of the Event Calendar extension"
6
+ task :migrate => :environment do
7
+ require 'radiant/extension_migrator'
8
+ if ENV["VERSION"]
9
+ EventCalendarExtension.migrator.migrate(ENV["VERSION"].to_i)
10
+ else
11
+ EventCalendarExtension.migrator.migrate
12
+ end
13
+ end
14
+
15
+ desc "Copies public assets of the Event Calendar to the instance public/ directory."
16
+ task :update => :environment do
17
+ is_svn_or_dir = proc {|path| path =~ /\.svn/ || File.directory?(path) }
18
+ Dir[EventCalendarExtension.root + "/public/**/*"].reject(&is_svn_or_dir).each do |file|
19
+ path = file.sub(EventCalendarExtension.root, '')
20
+ directory = File.dirname(path)
21
+ puts "Copying #{path}..."
22
+ mkdir_p RAILS_ROOT + directory
23
+ cp file, RAILS_ROOT + path
24
+ end
25
+ end
26
+
27
+ end
28
+ end
29
+ end
File without changes
Binary file
@@ -0,0 +1,240 @@
1
+ Toggle.SwapperBehavior = Behavior.create(Toggle.LinkBehavior, {
2
+ initialize: function ($super, options) {
3
+ $super(options);
4
+ this.plan_a = this.toggleWrappers.shift();
5
+ this.plan_b = this.toggleWrappers;
6
+ this.toggle();
7
+ },
8
+ toggle: function() {
9
+ if (this.plan_a.visible()) {
10
+ Toggle.hide(this.plan_a, 'none');
11
+ Toggle.show(this.plan_b, this.effect);
12
+ } else {
13
+ Toggle.hide(this.plan_b, 'none');
14
+ Toggle.show(this.plan_a, this.effect);
15
+ }
16
+ }
17
+ });
18
+
19
+ Toggle.RecurrenceSelect = Behavior.create(Toggle.SelectBehavior, {
20
+ initialize: function($super, options) {
21
+ var options = options || {};
22
+ var optionElements = this.element.select('option');
23
+ this.toggleWrapperIDs = $A();
24
+ this.toggleWrapperIDsFor = {};
25
+
26
+ optionElements.each(function(optionElement) {
27
+ var eid = 'recurrence_' + optionElement.readAttribute('value');
28
+ var elements = [$(eid)];
29
+ var wrapperIDs = elements.map(function(e) { return Toggle.wrapElement(e); }).invoke('identify');
30
+ this.toggleWrapperIDsFor[optionElement.identify()] = wrapperIDs;
31
+ this.toggleWrapperIDs.push(wrapperIDs);
32
+ }.bind(this));
33
+ this.toggleWrapperIDs = this.toggleWrapperIDs.flatten().uniq();
34
+
35
+ this.effect = "none";
36
+ this.toggle();
37
+ this.effect = options.effect || Toggle.DefaultEffect;
38
+ },
39
+ toggle: function($super) {
40
+ var combo = this.element;
41
+ var option = $(combo.options[combo.selectedIndex]);
42
+ var wrapperIDs = this.toggleWrapperIDsFor[option.identify()];
43
+ var partitioned = this.toggleWrapperIDs.partition(function(id) { return wrapperIDs.include(id); });
44
+
45
+ Toggle.hide(partitioned[1], 'none');
46
+ Toggle.show(partitioned[0], this.effect);
47
+ }
48
+ });
49
+
50
+ Toggle.MCE = Behavior.create(Toggle.LinkBehavior, {
51
+ initialize: function($super, options) {
52
+ var elements = Toggle.extractToggleObjects(this.element.readAttribute('rel'));
53
+ this.textarea = elements.shift();
54
+ },
55
+ toggle: function() {
56
+ if (!tinyMCE.get(this.textarea)) {
57
+ tinyMCE.execCommand('mceAddControl', false, this.textarea);
58
+ } else {
59
+ tinyMCE.execCommand('mceRemoveControl', false, this.textarea);
60
+ }
61
+ }
62
+ });
63
+
64
+ // We can't rely on padding-right so hard-code the width of the icon instead
65
+
66
+ DateInputBehavior.addMethods({
67
+ _isOverWidget: function(event) {
68
+ var positionedOverWidget = null;
69
+ if (Prototype.Browser.IE) {
70
+ var widgetLeft = this.element.cumulativeOffset().left;
71
+ var widgetRight = this.element.cumulativeOffset().left + this.element.getDimensions().width;
72
+ positionedOverWidget = (event.pointerX() >= widgetLeft && event.pointerX() <= widgetRight);
73
+ } else {
74
+ var calendarIconWidth = 40;
75
+ var widgetLeft = this.element.cumulativeOffset().left + this.element.getDimensions().width - calendarIconWidth;
76
+ positionedOverWidget = (event.pointerX() >= widgetLeft);
77
+ }
78
+ return positionedOverWidget;
79
+ }
80
+ });
81
+
82
+ // Override the usual Calendar to add a time-picker if the input field also has class 'time'
83
+ // and to tweak the positioning of the calendar
84
+
85
+ var calendar_displacement = {x: 16, y: 16};
86
+
87
+ DateInputBehavior.Calendar.addMethods({
88
+ _setDate: function(source) {
89
+ if (source.innerHTML.strip() != '') {
90
+ this.date.setDate(parseInt(source.innerHTML, 10)); // nb. only sets the day of month.
91
+ $A(this.element.getElementsByClassName('selected')).invoke('removeClassName', 'selected');
92
+ source.parentNode.addClassName('selected');
93
+ if (this.selector.element.hasClassName('time')) {
94
+ this._showTime();
95
+ } else {
96
+ this.selector.setDate(this.date);
97
+ }
98
+ }
99
+ },
100
+ _createTimeChooser : function() {
101
+ this.date_chooser = this.element.select('table.calendar').first();
102
+ var timer = this.element.select('div.clock_control').first() || $div({'class': 'clock_control'});
103
+ this.element.insert(timer);
104
+ timer.clonePosition(this.date_chooser);
105
+ timer.setStyle({position: 'relative', left: 0, top: 0, width: this.date_chooser.getWidth(), height: 'auto'});
106
+ this.time_chooser = new DateInputBehavior.Clock(timer, this);
107
+ },
108
+ _showTime: function () {
109
+ this._createTimeChooser();
110
+ this.date_chooser.hide();
111
+ this.time_chooser.show();
112
+ },
113
+ _hideTime: function () {
114
+ this.time_chooser.hide();
115
+ this.date_chooser.show();
116
+ },
117
+ _setTime: function (h,m) {
118
+ this.date.setHours(h);
119
+ this.date.setMinutes(m);
120
+ this.date.setSeconds(0);
121
+ this.selector.setDate(this.date);
122
+ },
123
+ show: function() {
124
+ DateInputBehavior.Calendar.instances.invoke('hide');
125
+ this.date = this.selector.getDate();
126
+ this.redraw();
127
+ this.element.setStyle({
128
+ 'top': this.getVerticalOffset(this.selector.element) + 'px',
129
+ 'left': this.getHorizontalOffset(this.selector.element) + 'px',
130
+ 'z-index': 10001
131
+ });
132
+ this.element.show();
133
+ this.active = true;
134
+ },
135
+ getVerticalOffset: function(selector){
136
+ var defaultOffset = this.selector.element.cumulativeOffset().top + this.selector.element.getHeight() + 2;
137
+ var height = this.element.getHeight();
138
+ var top = 0;
139
+ if (document.viewport.getHeight() > defaultOffset + height) {
140
+ top = defaultOffset - calendar_displacement.y;
141
+ } else {
142
+ top = (defaultOffset - height - selector.getHeight() - 6 + calendar_displacement.y);
143
+ }
144
+ if (top < document.viewport.getScrollOffsets().top) {
145
+ top = document.viewport.getScrollOffsets().top;
146
+ }
147
+
148
+ return top;
149
+ },
150
+ getHorizontalOffset: function (element) {
151
+ return element.cumulativeOffset().left + element.getWidth() - calendar_displacement.x;
152
+ }
153
+ });
154
+
155
+ Date.prototype.getPaddedMonth = function() {
156
+ var m = (this.getMonth() + 1).toString();
157
+ return (m.length > 1) ? m : "0" + m;
158
+ };
159
+
160
+ Date.prototype.getPaddedDate = function() {
161
+ var d = this.getDate().toString();
162
+ return (d.length > 1) ? d : "0" + d;
163
+ };
164
+
165
+ Date.prototype.getPaddedHours = function() {
166
+ var m = this.getHours().toString();
167
+ return (m.length > 1) ? m : "0" + m;
168
+ };
169
+
170
+ Date.prototype.getPaddedMinutes = function() {
171
+ var m = this.getMinutes().toString();
172
+ return (m.length > 1) ? m : "0" + m;
173
+ };
174
+
175
+ Date.prototype.getPaddedSeconds = function() {
176
+ var s = this.getSeconds().toString();
177
+ return (s.length > 1) ? s : "0" + s;
178
+ };
179
+
180
+ DateInputBehavior.Clock = Behavior.create({
181
+ initialize: function(calendar) {
182
+ this.calendar = calendar;
183
+ this.date = calendar.date;
184
+ this.redraw();
185
+ },
186
+ redraw: function () {
187
+ this.form = new Element('form', {'class': 'clock'});
188
+ this.element.update(this.form);
189
+
190
+ this.h = new Element('input', {'type' : 'text', 'class': 'hours', name: 'hours'}).setValue(this.date.getPaddedHours());
191
+ this.m = new Element('input', {'type' : 'text', 'class': 'minutes', name: 'minutes'}).setValue(this.date.getPaddedMinutes());
192
+ this.button = new Element('input', {'type' : 'button', 'class': 'set_time', name: 'set'}).setValue('set');
193
+ this.cancel = new Element('a', {'href': '#', 'class': 'cancel'}).update('change date');
194
+
195
+ this.form.insert(new Element('h4', {'class': 'date'}).update(this.date.getDate() + ' ' + DateInputBehavior.Calendar.MONTHS[this.date.getMonth()].label + ', ' + this.date.getFullYear() + ' at:'));
196
+ this.form.insert(this.h);
197
+ this.form.insert(new Element('span', {'class': 'colon'}).update(':'));
198
+ this.form.insert(this.m);
199
+ this.form.insert(new Element('br'));
200
+ this.form.insert(this.button);
201
+ this.form.insert(new Element('br'));
202
+ this.form.insert(this.cancel);
203
+ },
204
+ onclick: function (event) {
205
+ event.stop();
206
+ if ($(event.target).hasClassName('set_time')) this.submit();
207
+ if ($(event.target).hasClassName('cancel')) this.goback();
208
+ },
209
+ submit: function () {
210
+ this.calendar._setTime(this.h.getValue(), this.m.getValue());
211
+ },
212
+ goback: function () {
213
+ this.calendar._hideTime();
214
+ },
215
+ show: function () {
216
+ this.element.show();
217
+ },
218
+ hide: function () {
219
+ this.element.hide();
220
+ }
221
+ });
222
+
223
+ DateInputBehavior.DEFAULTS = {
224
+ setter: function(date) {
225
+ return date.getFullYear() + '-' + date.getPaddedMonth() + '-' + date.getPaddedDate() + ' ' + date.getHours() + ':' + date.getPaddedMinutes() + ':' + date.getPaddedSeconds();
226
+ },
227
+ getter: function(value) {
228
+ var p = value.split(/\D+/g);
229
+ if (!p[0]) return null;
230
+ var date = new Date(p[0],p[1]-1,p[2],p[3],p[4],p[5]);
231
+ return date;
232
+ }
233
+ };
234
+
235
+ Event.addBehavior({
236
+ "input.toggle": Toggle.CheckboxBehavior,
237
+ "a.swapper" : Toggle.SwapperBehavior({effect : 'appear'}),
238
+ "select.basis" : Toggle.RecurrenceSelect({effect : 'appear'}),
239
+ "a.toggleMCE" : Toggle.MCE
240
+ });
@@ -0,0 +1,266 @@
1
+ @import modules/rounded.sass
2
+ @import modules/opacity.sass
3
+ @import modules/grid.sass
4
+
5
+ =standard_input
6
+ :width 100%
7
+ :padding 6px 0
8
+ :font-family Monaco,"Courier New",Courier,monospace
9
+ :font-size 95%
10
+
11
+ =quiet
12
+ :font-family helvetica, arial, sans-serif
13
+ :font-weight lighter
14
+ :font-size 85%
15
+ :color #ccc
16
+ :line-height 1.3
17
+ :text-shadow none
18
+
19
+ =flatbottom
20
+ :margin-bottom 0
21
+
22
+ !radiant_red = #c00
23
+
24
+ //calendars index
25
+
26
+ table.index
27
+ td.subscription-title
28
+ :font-size 115%
29
+ :font-weight bold
30
+
31
+ span.calendar_actions
32
+ img
33
+ :opacity 0.5
34
+ a
35
+ img
36
+ :opacity 1
37
+
38
+ //events index
39
+
40
+ table#events
41
+ tr
42
+ td.datemark
43
+ :width 40px
44
+ :vertical-align top
45
+ :text-align center
46
+ :line-height 1
47
+ a
48
+ :text-decoration none
49
+ span.month
50
+ :display block
51
+ :margin-top 5px
52
+ :font-family helvetica, arial, sans-serif
53
+ :font-weight lighter
54
+ :font-size 80%
55
+ :text-transform uppercase
56
+ span.day
57
+ :display block
58
+ :font-family helvetica, arial, sans-serif
59
+ :font-weight bold
60
+ :font-size 160%
61
+ td.event
62
+ :width 40%
63
+ :vertical-align top
64
+ h3
65
+ :margin 0
66
+ :line-height 1.2
67
+ a
68
+ :text-decoration none
69
+ p.description
70
+ :margin 4px 0
71
+ :font-size 80%
72
+ :color #999
73
+ p.keywords
74
+ :margin 4px 0
75
+ :font-size 80%
76
+ td.time, td.location, td.keywords, td.recurrence
77
+ :font-size 80%
78
+ :padding-top 28px
79
+ :vertical-align top
80
+ td.remove
81
+ :padding-top 28px
82
+ :vertical-align top
83
+ span.ineditable
84
+ :color #666
85
+ :font-size 80%
86
+ :background url(/images/admin/minus.png) no-repeat scroll 4px 0
87
+ :padding-left 20px
88
+ :opacity 0.3
89
+ tr.continuing
90
+ td.datemark
91
+ a
92
+ :color #b2aaa4
93
+ td.event
94
+ h3
95
+ a
96
+ :color #b2aaa4
97
+
98
+ // venues index
99
+
100
+ table#event_venues
101
+ td.event_venue
102
+ :width 40%
103
+ :vertical-align top
104
+ h3
105
+ :margin 0
106
+ :line-height 1.2
107
+ a
108
+ :text-decoration none
109
+ p.description
110
+ :margin 4px 0
111
+ :font-size 80%
112
+ :color #999
113
+ td.location, td.url
114
+ :font-size 80%
115
+ :vertical-align top
116
+
117
+ // event and calendar forms
118
+
119
+ #content
120
+ #event_form, #calendar_form, #venue_form
121
+ div
122
+ p
123
+ input.textbox, select.textbox, textarea
124
+ +standard_input
125
+ :margin-top 0.3em
126
+ p.note
127
+ +grid(4)
128
+ :padding-top 1.7em
129
+ div.title
130
+ :clear left
131
+ p.event_title, p.calendar_title, p.keywords, p.venue_title
132
+ :margin-bottom 1em
133
+ input
134
+ :font-family Georgia,Palatino,"Times New Roman",Times,serif
135
+ :font-size 200%
136
+ p.event_title
137
+ +grid(8)
138
+ p.keywords
139
+ +grid(4)
140
+ input
141
+ :color = !radiant_red
142
+ p.calendar_category, p.calendar_slug
143
+ +grid(4)
144
+ div.url
145
+ +grid(12)
146
+ :clear left
147
+
148
+ div.description
149
+ :clear left
150
+ p.description
151
+ +grid(8)
152
+ textarea
153
+ :height 16em
154
+ p.calendar
155
+ +grid(4)
156
+
157
+ div.ical
158
+ :clear left
159
+ p.url
160
+ +grid(8)
161
+ :margin-top 0
162
+ p.username, p.password
163
+ +grid(4)
164
+ p.username
165
+ :margin-right 2%
166
+
167
+ div.date
168
+ :clear left
169
+ p.start_date, p.end_date
170
+ +grid(4)
171
+ :margin-bottom 0
172
+ p.problematic
173
+ :color = !radiant_red
174
+
175
+ div.recurrence
176
+ :clear left
177
+ p.recurrence
178
+ :margin-top 0
179
+ span.recurrence_detail
180
+ :display block
181
+ span#recurrence_interval
182
+ +grid(2)
183
+ span#recurrence_period
184
+ +grid(2)
185
+ span#recurrence_basis
186
+ +grid(2)
187
+ span#recurrence_limit
188
+ +grid(2)
189
+ +flush_right
190
+ span#recurrence_count
191
+ +grid(2)
192
+ +flush_right
193
+
194
+ div.venue, div.newvenue
195
+ :overflow auto
196
+
197
+ div#venue
198
+ :clear left
199
+ p#existing_venue
200
+ +grid(8)
201
+ div#new_venue
202
+ :clear left
203
+ p.venue_title
204
+ +grid(8)
205
+ p.url
206
+ :clear left
207
+ +grid(8)
208
+ p.venue_location
209
+ +grid(8)
210
+ textarea
211
+ :height 5em
212
+ p.venue_postcode
213
+ +grid(4)
214
+ span.note
215
+ :display block
216
+ :margin-top 4px
217
+ p.venue_description
218
+ +grid(12)
219
+ :clear both
220
+ textarea
221
+ :height 5em
222
+ .note, label.minor
223
+ +quiet
224
+ :display inline
225
+ label.inline
226
+ :display inline
227
+ .hidden
228
+ :display none
229
+
230
+
231
+ a.toggleMCE
232
+ :display block
233
+ :width 24px
234
+ :height 24px
235
+ :padding-left 24px
236
+ :float right
237
+
238
+ .calendar_popup
239
+ div.clock_control
240
+ :position relative
241
+ :display block
242
+ :width 300px
243
+ :height 200px
244
+ form
245
+ :text-align center
246
+ h4
247
+ :margin 10px
248
+ :color #999
249
+ :font-weight lighter
250
+ input.hours, input.minutes
251
+ :width 60px
252
+ :border 1px solid #ccc
253
+ :padding 4px
254
+ :font-size 40px
255
+ span.colon
256
+ :padding 4px
257
+ :font-size 40px
258
+ :color #333
259
+ :width 10px
260
+ input.set_time
261
+ :font-size 30px
262
+ :width 114px
263
+ :margin 20px 0
264
+ a.cancel
265
+ :cursor pointer
266
+