Event Organiser - Version 2.0.2

Version Description

  • Prevent new row being created on single event save
Download this release

Release Info

Developer stephenharris
Plugin Icon 128x128 Event Organiser
Version 2.0.2
Comparing to
See all releases

Version 2.0.2

Files changed (79) hide show
  1. .gitignore +10 -0
  2. classes/class-eo-agenda-widget.php +112 -0
  3. classes/class-eo-calendar-widget.php +278 -0
  4. classes/class-eo-event-list-widget.php +217 -0
  5. classes/class-eo-venue-list-table.php +193 -0
  6. classes/class-eo-widget-categories.php +103 -0
  7. classes/class-eo-widget-venues.php +98 -0
  8. classes/class-eventorganiser-admin-page.php +65 -0
  9. classes/class-eventorganiser-shortcodes.php +407 -0
  10. css/eventorganiser-admin-classic.css +576 -0
  11. css/eventorganiser-admin-fresh.css +591 -0
  12. css/eventorganiser-admin-style.css +364 -0
  13. css/eventorganiser-front-end.css +251 -0
  14. css/fullcalendar.css +208 -0
  15. css/images/eo-pro-event-search.png +0 -0
  16. css/images/eo-pro-ticket-picker.png +0 -0
  17. css/images/eo-pro-venue-cf.png +0 -0
  18. css/images/eobadge.png +0 -0
  19. css/images/eoicon-16.png +0 -0
  20. css/images/eoicon-32.png +0 -0
  21. css/images/eoicon-32.xcf +0 -0
  22. css/images/eoicon-64.png +0 -0
  23. css/images/help-14.png +0 -0
  24. css/images/info-14.png +0 -0
  25. css/images/loading-image.gif +0 -0
  26. css/images/ui-bg_flat_100_f5f5f5_40x100.png +0 -0
  27. css/images/ui-bg_flat_100_ffffff_40x100.png +0 -0
  28. css/images/ui-bg_flat_10_ffebe8_40x100.png +0 -0
  29. css/images/ui-bg_flat_10_ffffff_40x100.png +0 -0
  30. css/images/ui-bg_flat_15_f9f9f9_40x100.png +0 -0
  31. css/images/ui-bg_flat_15_ffffe0_40x100.png +0 -0
  32. css/images/ui-bg_flat_20_000000_40x100.png +0 -0
  33. css/images/ui-bg_flat_70_000000_40x100.png +0 -0
  34. css/images/ui-bg_highlight-soft_100_ececec_1x100.png +0 -0
  35. css/images/ui-icons_21759b_256x240.png +0 -0
  36. css/images/ui-icons_222222_256x240.png +0 -0
  37. css/images/ui-icons_333333_256x240.png +0 -0
  38. css/images/ui-icons_3572ac_256x240.png +0 -0
  39. css/images/ui-icons_999999_256x240.png +0 -0
  40. css/images/ui-icons_cc0000_256x240.png +0 -0
  41. css/images/ui-icons_d54e21_256x240.png +0 -0
  42. event-organiser-calendar.php +306 -0
  43. event-organiser-debug.php +387 -0
  44. event-organiser-edit.php +389 -0
  45. event-organiser-go-pro.php +115 -0
  46. event-organiser-manage.php +240 -0
  47. event-organiser-settings.php +562 -0
  48. event-organiser-venues.php +433 -0
  49. event-organiser.php +234 -0
  50. includes/class-event-organiser-im-export.php +852 -0
  51. includes/debug.php +57 -0
  52. includes/deprecated.php +151 -0
  53. includes/event-organiser-ajax.php +596 -0
  54. includes/event-organiser-archives.php +517 -0
  55. includes/event-organiser-cpt.php +1057 -0
  56. includes/event-organiser-event-functions.php +1231 -0
  57. includes/event-organiser-install.php +388 -0
  58. includes/event-organiser-register.php +780 -0
  59. includes/event-organiser-templates.php +269 -0
  60. includes/event-organiser-utility-functions.php +910 -0
  61. includes/event-organiser-venue-functions.php +867 -0
  62. includes/event.php +715 -0
  63. js/admin-calendar.js +1199 -0
  64. js/admin-calendar.min.js +45 -0
  65. js/event-manager.js +234 -0
  66. js/event-manager.min.js +5 -0
  67. js/event.js +2219 -0
  68. js/event.min.js +73 -0
  69. js/eventorganiser-pointer.js +16 -0
  70. js/frontend.js +467 -0
  71. js/frontend.min.js +19 -0
  72. js/fullcalendar.js +5236 -0
  73. js/fullcalendar.min.js +117 -0
  74. js/inline-help.js +25 -0
  75. js/qtip2.js +2 -0
  76. js/venues.js +100 -0
  77. js/venues.min.js +7 -0
  78. languages/eventorganiser-da_DK.mo +0 -0
  79. languages/eventorganiser-da_DK.po +866 -0
.gitignore ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ *~
2
+ .project
3
+ .settings
4
+ .buildpath
5
+ .metadata
6
+ compiler.jar
7
+ dist
8
+ externs.js
9
+ externs-venue.js
10
+ Makefile
classes/class-eo-agenda-widget.php ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class used to create the event calendar widget
4
+ */
5
+ class EO_Events_Agenda_Widget extends WP_Widget{
6
+ var $w_arg = array(
7
+ 'title'=> '',
8
+ 'mode'=> 'day',
9
+ 'group_format'=>'l, jS F',
10
+ 'item_format'=>'g:i a',
11
+ 'add_to_google' => 1,
12
+ );
13
+
14
+ static $agendas=array();
15
+
16
+ function __construct() {
17
+ $widget_ops = array('classname' => 'widget_events', 'description' => __('Displays a list of events, grouped by date','eventorganiser'));
18
+ parent::__construct('EO_Events_Agenda_Widget', __('Events Agenda','eventorganiser'), $widget_ops);
19
+ }
20
+
21
+
22
+ function form($instance) {
23
+ $instance = wp_parse_args( (array) $instance, $this->w_arg );
24
+ ?>
25
+ <p>
26
+ <label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title', 'eventorganiser'); ?>: </label>
27
+ <input id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo esc_attr($instance['title']);?>" />
28
+ </p>
29
+ <p>
30
+ <label for="<?php echo $this->get_field_id('mode'); ?>"><?php _e('Group by', 'eventorganiser'); ?>: </label>
31
+ <select id="<?php echo $this->get_field_id('mode'); ?>" name="<?php echo $this->get_field_name('mode'); ?>" type="text">
32
+ <option value="day" <?php selected($instance['mode'], ''); ?>><?php _e('Day','eventorganiser'); ?> </option>
33
+ <option value="week" <?php selected($instance['mode'], 'week'); ?>><?php _e('Week','eventorganiser'); ?> </option>
34
+ <option value="month" <?php selected($instance['mode'], 'month'); ?>><?php _e('Month','eventorganiser'); ?> </option>
35
+ </select>
36
+ </p>
37
+ <p>
38
+ <label for="<?php echo $this->get_field_id('group_format'); ?>"><?php _e('Group date format', 'eventorganiser'); ?>: </label>
39
+ <input id="<?php echo $this->get_field_id('group_format'); ?>" name="<?php echo $this->get_field_name('group_format'); ?>" type="text" value="<?php echo esc_attr($instance['group_format']);?>" />
40
+ </p>
41
+ <p>
42
+ <label for="<?php echo $this->get_field_id('item_format'); ?>"><?php _e('Event date/time format', 'eventorganiser'); ?>: </label>
43
+ <input id="<?php echo $this->get_field_id('item_format'); ?>" name="<?php echo $this->get_field_name('item_format'); ?>" type="text" value="<?php echo esc_attr($instance['item_format']);?>" />
44
+ </p>
45
+ <p>
46
+ <label for="<?php echo $this->get_field_id('add_to_google'); ?>"><?php _e('Include \'Add To Google\' link','eventorganiser'); ?>: </label>
47
+ <input id="<?php echo $this->get_field_id('add_to_google'); ?>" name="<?php echo $this->get_field_name('add_to_google'); ?>" type="checkbox" value="1" <?php checked($instance['add_to_google'],1);?>" />
48
+ </p>
49
+
50
+ <?php
51
+ }
52
+
53
+
54
+ function update($new_instance, $old_instance){
55
+ $validated=array();
56
+ delete_transient('eo_widget_agenda');
57
+ $validated['title'] = sanitize_text_field( $new_instance['title'] );
58
+ $validated['mode'] = sanitize_text_field( $new_instance['mode'] );
59
+ $validated['group_format'] = sanitize_text_field( $new_instance['group_format'] );
60
+ $validated['item_format'] = sanitize_text_field( $new_instance['item_format'] );
61
+ $validated['add_to_google'] = intval( $new_instance['add_to_google']);
62
+ return $validated;
63
+ }
64
+
65
+
66
+
67
+ function widget($args, $instance){
68
+ global $wp_locale;
69
+ wp_enqueue_script( 'eo_front');
70
+ wp_enqueue_style( 'eo_front');
71
+ extract($args, EXTR_SKIP);
72
+
73
+ add_action('wp_footer', array(__CLASS__, 'add_options_to_script'));
74
+ $id = esc_attr($args['widget_id']).'_container';
75
+ self::$agendas[$id] = array(
76
+ 'id'=>esc_attr($args['widget_id']),
77
+ 'number'=>$this->number,
78
+ 'mode'=> isset($instance['mode']) ? $instance['mode'] : 'day',
79
+ 'add_to_google'=>$instance['add_to_google']
80
+ );
81
+
82
+ //Echo widget
83
+ echo $before_widget;
84
+
85
+ $widget_title = apply_filters('widget_title', $instance['title'], $instance, $this->id_base);
86
+
87
+ if ( $widget_title )
88
+ echo $before_title.esc_html($widget_title).$after_title;
89
+
90
+ echo "<div style='width:100%' id='{$id}' class='eo-agenda-widget'>";
91
+ ?>
92
+ <div class='agenda-nav'>
93
+ <span class="next button ui-button ui-widget ui-state-default ui-corner-all ui-button-icon-only" role="button" title="">
94
+ <span class="ui-button-icon-primary ui-icon ui-icon-carat-1-e"></span><span class="ui-button-text"></span>
95
+ </span>
96
+ <span class="prev button ui-button ui-widget ui-state-default ui-corner-all ui-button-icon-only" role="button" title="">
97
+ <span class="ui-button-icon-primary ui-icon ui-icon-carat-1-w"></span><span class="ui-button-text"></span>
98
+ </span>
99
+ </div>
100
+ <?php
101
+ echo "<ul class='dates'>";
102
+ echo '</ul>';//End dates
103
+ echo "</div>";
104
+ echo $after_widget;
105
+ }
106
+
107
+ function add_options_to_script() {
108
+ if(!empty(self::$agendas))
109
+ wp_localize_script( 'eo_front', 'eo_widget_agenda', self::$agendas);
110
+ }
111
+
112
+ }
classes/class-eo-calendar-widget.php ADDED
@@ -0,0 +1,278 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class used to create the event calendar widget
4
+ */
5
+ class EO_Calendar_Widget extends WP_Widget
6
+ {
7
+
8
+ var $w_arg = array(
9
+ 'title'=> '',
10
+ 'showpastevents'=>1,
11
+ 'event-category'=>'',
12
+ 'event-venue'=>'',
13
+ );
14
+ static $widget_cal =array();
15
+
16
+ function __construct() {
17
+ $widget_ops = array('classname' => 'widget_calendar eo_widget_calendar', 'description' => __('Displays a calendar of your events','eventorganiser') );
18
+ parent::__construct('EO_Calendar_Widget', __('Events Calendar','eventorganiser'), $widget_ops);
19
+ }
20
+
21
+ function form($instance) {
22
+
23
+ $instance = wp_parse_args( (array) $instance, $this->w_arg );
24
+
25
+ printf('<p>
26
+ <label for="%1$s"> %2$s: </label>
27
+ <input type="text" id="%1$s" name="%3$s" value="%4$s">
28
+ </p>',
29
+ esc_attr( $this->get_field_id('title') ),
30
+ esc_html__('Title', 'eventorganiser'),
31
+ esc_attr( $this->get_field_name('title') ),
32
+ esc_attr($instance['title'])
33
+ );
34
+
35
+ printf('<p>
36
+ <label for="%1$s"> %2$s: </label>
37
+ <input type="checkbox" id="%1$s" name="%3$s" value="1" %4$s>
38
+ </p>',
39
+ esc_attr( $this->get_field_id('showpastevents') ),
40
+ esc_html__('Include past events', 'eventorganiser'),
41
+ esc_attr( $this->get_field_name('showpastevents') ),
42
+ checked($instance['showpastevents'], 1, false)
43
+ );
44
+
45
+ printf('<p>
46
+ <label for="%1$s"> %2$s: </label>
47
+ <input type="text" id="%1$s" name="%3$s" value="%4$s" class="widefat">
48
+ <em> %5$s </em>
49
+ </p>',
50
+ esc_attr( $this->get_field_id('event-category') ),
51
+ esc_html__('Event categories', 'eventorganiser'),
52
+ esc_attr( $this->get_field_name('event-category') ),
53
+ esc_attr($instance['event-category']),
54
+ esc_html__('List category slug(s), seperate by comma. Leave blank for all', 'eventorganiser')
55
+ );
56
+
57
+ printf('<p>
58
+ <label for="%1$s"> %2$s: </label>
59
+ %3$s
60
+ </p>',
61
+ esc_attr( $this->get_field_id('event-venue') ),
62
+ esc_html__('Event venue', 'eventorganiser'),
63
+ eo_event_venue_dropdown(array('echo'=>0,'show_option_all'=>esc_html__('All Venues','eventorganiser'),'id'=>$this->get_field_id('event-venue'),'selected'=>$instance['event-venue'], 'name'=>$this->get_field_name('event-venue'),'hide_empty'=>false))
64
+ );
65
+
66
+ }
67
+
68
+
69
+ function update($new_instance, $old_instance){
70
+ $validated=array();
71
+ $validated['title'] = sanitize_text_field( $new_instance['title'] );
72
+ $validated['event-category'] = sanitize_text_field( $new_instance['event-category'] );
73
+ $validated['event-venue'] = sanitize_text_field( $new_instance['event-venue'] );
74
+ $validated['showpastevents'] = ( !empty($new_instance['showpastevents']) ? 1: 0);
75
+ delete_transient( 'eo_widget_calendar' );
76
+ return $validated;
77
+ }
78
+
79
+
80
+
81
+ function widget($args, $instance){
82
+ wp_enqueue_script( 'eo_front');
83
+ extract($args, EXTR_SKIP);
84
+
85
+ //Set the month to display (DateTime must be 1st of that month)
86
+ $tz = eo_get_blog_timezone();
87
+ $date = get_query_var('ondate') ? str_replace('/','-',get_query_var('ondate')) : 'now';
88
+ try{
89
+ $month = new DateTime($date,$tz);
90
+ }catch( Exception $e){
91
+ $month = new DateTime('now',$tz);
92
+ }
93
+ $month = date_create($month->format('Y-m-1'),$tz);
94
+
95
+ /* Set up the event query */
96
+ $calendar = array(
97
+ 'showpastevents'=> (empty($instance['showpastevents']) ? 0 : 1),
98
+ 'event-venue'=> (!empty($instance['event-venue']) ? $instance['event-venue'] : 0),
99
+ 'event-category'=> (!empty($instance['event-category']) ? $instance['event-category'] : 0),
100
+ );
101
+
102
+ add_action('wp_footer', array(__CLASS__, 'add_options_to_script'));
103
+
104
+ $id = esc_attr($args['widget_id']);
105
+ self::$widget_cal[$id] = $calendar;
106
+
107
+ //Echo widget
108
+ echo $before_widget;
109
+
110
+ $widget_title = apply_filters('widget_title', $instance['title'], $instance, $this->id_base);
111
+
112
+ if ( $widget_title )
113
+ echo $before_title.esc_html($widget_title).$after_title;
114
+
115
+ echo "<div id='{$id}_content' >";
116
+ echo $this->generate_output($month,$calendar);
117
+
118
+ echo "</div>";
119
+ echo $after_widget;
120
+ }
121
+
122
+ function add_options_to_script() {
123
+ wp_enqueue_script( 'eo_front');
124
+ if(!empty(self::$widget_cal))
125
+ wp_localize_script( 'eo_front', 'eo_widget_cal', self::$widget_cal);
126
+ }
127
+
128
+ /**
129
+ * Generates widget / shortcode calendar html
130
+ *
131
+ * param $month - DateTime object for first day of the month (in blog timezone)
132
+ */
133
+ function generate_output( $month, $args=array() ){
134
+
135
+ //Translations
136
+ global $wp_locale;
137
+
138
+ $today = new DateTime('now',eo_get_blog_timezone());
139
+
140
+ $key= $month->format('YM').serialize($args).get_locale().$today->format('Y-m-d');
141
+ $calendar = get_transient('eo_widget_calendar');
142
+ if( ( !defined( 'WP_DEBUG' ) || !WP_DEBUG ) && $calendar && is_array( $calendar ) && isset( $calendar[$key] ) ){
143
+ return $calendar[$key];
144
+ }
145
+
146
+ //Month details
147
+ $first_day_of_month= intval($month->format('N')); //0=sun,...,6=sat
148
+ $days_in_month= intval($month->format('t')); // 28-31
149
+ $last_month = clone $month;
150
+ $last_month->modify('last month');
151
+ $next_month = clone $month;
152
+ $next_month->modify('next month');
153
+
154
+ //Retrieve the start day of the week from the options.
155
+ $start_day=intval(get_option('start_of_week'));//0=sun,...,6=sat
156
+
157
+ //How many blank cells before inserting dates
158
+ $offset = ($first_day_of_month-$start_day +7)%7;
159
+
160
+ //Number of weeks to show in Calendar
161
+ $totalweeks = ceil(($offset + $days_in_month)/7);
162
+
163
+ //Get events for this month
164
+ $start = $month->format('Y-m-d');
165
+ $end = $month->format('Y-m-t');
166
+
167
+ //Query events
168
+ $required = array( 'numberposts'=>-1, 'showrepeats'=>1, 'event_start_before'=>$end, 'event_start_after'=>$start );
169
+ $events= eo_get_events(array_merge($args,$required));
170
+
171
+ //Populate events array
172
+ $calendar_events =array();
173
+ foreach($events as $event):
174
+ $date = eo_get_the_start('Y-m-d', $event->ID, null, $event->occurrence_id);
175
+ $calendar_events[$date][]= $event;
176
+ endforeach;
177
+
178
+ $before = "<table id='wp-calendar'>";
179
+
180
+ $title = sprintf("<caption> %s </caption>", esc_html(eo_format_datetime($month, 'F Y')) );
181
+
182
+ $head="<thead><tr>";
183
+ for ($d=0; $d <= 6; $d++):
184
+ $day = $wp_locale->get_weekday( ($d+$start_day)%7 );
185
+ $day_abbrev =$wp_locale->get_weekday_initial( $day );
186
+ $head .= sprintf("<th title='%s' scope='col'>%s</th>", esc_attr($day), esc_html($day_abbrev) );
187
+ endfor;
188
+ $head.="</tr></thead>";
189
+
190
+ $foot = sprintf( "<tfoot><tr>
191
+ <td id='eo-widget-prev-month' colspan='3'><a title='%s' href='%s'>&laquo; %s</a></td>
192
+ <td class='pad'>&nbsp;</td>
193
+ <td id='eo-widget-next-month' colspan='3'><a title='%s' href='%s'> %s &raquo; </a></td>
194
+ </tr></tfoot>",
195
+ esc_html__('Previous month','eventorganiser'),
196
+ add_query_arg('eo_month',$last_month->format('Y-m')),
197
+ esc_html(eo_format_datetime($last_month, 'M')),
198
+ esc_html__('Next month','eventorganiser'),
199
+ add_query_arg('eo_month',$next_month->format('Y-m')),
200
+ esc_html(eo_format_datetime($next_month, 'M'))
201
+ );
202
+
203
+
204
+ $body ="<tbody>";
205
+ $current_date = clone $month;
206
+ $event_archive_link = get_post_type_archive_link('event');
207
+
208
+ //Foreach week in calendar
209
+ for( $w = 0; $w <= $totalweeks-1; $w++ ):
210
+ $body .="<tr>";
211
+
212
+ //For each cell in this week
213
+ for( $cell = $w*7 +1; $cell <= ($w+1)*7; $cell++ ):
214
+
215
+ $formated_date = $current_date->format('Y-m-d');
216
+
217
+ if( $cell<=$offset || $cell-$offset > $days_in_month ){
218
+ $body .="<td class='pad' colspan='1'>&nbsp;</td>";
219
+
220
+ }else{
221
+ $class=array();
222
+
223
+ //Is the date 'today'?
224
+ if( $formated_date == $today->format('Y-m-d') )
225
+ $class[] ='today';
226
+
227
+ //Does the date have any events
228
+ if( isset($calendar_events[$formated_date]) ){
229
+ $class[] ='event';
230
+ $events = $calendar_events[$formated_date];
231
+
232
+ $link = esc_url(eo_get_event_archive_link($current_date->format('Y'),$current_date->format('m'),$current_date->format('d')));
233
+
234
+ /**
235
+ * Filters the the link of a date on the events widget calendar
236
+ *@param string $link The link
237
+ *@param datetime $current_date The date being filtered
238
+ *@param array $events Array of events starting on this day
239
+ */
240
+ $link = apply_filters('eventorganiser_widget_calendar_date_link', $link, $current_date, $events );
241
+
242
+ $classes = implode(' ',$class);
243
+ $titles = implode(', ',wp_list_pluck( $events, 'post_title') );
244
+
245
+ $body .= sprintf("<td class='%s'> <a title='%s' href='%s'> %s </a></td>",
246
+ esc_attr($classes),
247
+ esc_attr($titles),
248
+ $link,
249
+ ($cell-$offset)
250
+ );
251
+ }else{
252
+ $classes = implode(' ',$class);
253
+ $body .= sprintf("<td class='%s'> %s </td>", esc_attr($classes), ($cell-$offset) );
254
+ }
255
+
256
+ //Proceed to next day
257
+ $current_date->modify('+1 day');
258
+ }
259
+
260
+ endfor;//Endfor each day in week
261
+ $body .="</tr>";
262
+
263
+ endfor; //End for each week
264
+
265
+ $body .="</tbody>";
266
+ $after = "</table>";
267
+
268
+ if( !$calendar || !is_array($calendar) )
269
+ $calendar = array();
270
+
271
+ $calendar[$key] = $before.$title.$head.$foot.$body.$after;
272
+
273
+ set_transient('eo_widget_calendar',$calendar, 60*60*24);
274
+ return $calendar[$key];
275
+ }
276
+ }
277
+
278
+ ?>
classes/class-eo-event-list-widget.php ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class used to create the event list widget
4
+ */
5
+ class EO_Event_List_Widget extends WP_Widget{
6
+
7
+ var $w_arg = array(
8
+ 'title'=> 'Events',
9
+ 'numberposts'=> 5,
10
+ 'event-category'=> '',
11
+ 'venue_id'=> NULL,
12
+ 'venue'=> '',
13
+ 'orderby'=> 'eventstart',
14
+ 'showpastevents'=> 0,
15
+ 'group_events_by'=>'',
16
+ 'order'=> 'ASC',
17
+ 'template'=>'',
18
+ 'no_events'=>'No Events'
19
+ );
20
+
21
+ function __construct() {
22
+ $widget_ops = array('classname' => 'EO_Event_List_Widget', 'description' => __('Displays a list of events','eventorganiser') );
23
+ parent::__construct('EO_Event_List_Widget', __('Events','eventorganiser'), $widget_ops);
24
+ }
25
+
26
+
27
+ function form($instance){
28
+ $instance = wp_parse_args( (array) $instance, $this->w_arg );
29
+ ?>
30
+ <p>
31
+ <label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title', 'eventorganiser'); ?>: </label>
32
+ <input type="text" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" value="<?php echo esc_attr($instance['title']); ?>" />
33
+ </p>
34
+ <p>
35
+ <label for="<?php echo $this->get_field_id('numberposts'); ?>"><?php _e('Number of events','eventorganiser');?>: </label>
36
+ <input id="<?php echo $this->get_field_id('numberposts'); ?>" name="<?php echo $this->get_field_name('numberposts'); ?>" type="number" size="3" value="<?php echo intval($instance['numberposts']);?>" />
37
+ </p>
38
+ <p>
39
+ <label for="<?php echo $this->get_field_id('event-category'); ?>"><?php _e('Event categories', 'eventorganiser'); ?>: </label>
40
+ <input id="<?php echo $this->get_field_id('event-category'); ?>" class="widefat" name="<?php echo $this->get_field_name('event-category'); ?>" type="text" value="<?php echo esc_attr($instance['event-category']);?>" />
41
+ <em><?php _e('List category slug(s), seperate by comma. Leave blank for all', 'eventorganiser'); ?> </em>
42
+ </p>
43
+ <p>
44
+ <label for="<?php echo $this->get_field_id('venue'); ?>"><?php _e('Venue', 'eventorganiser'); ?>: </label>
45
+ <?php $venues = get_terms('event-venue', array('hide_empty'=>false));?>
46
+ <select id="<?php echo $this->get_field_id('venue'); ?>" name="<?php echo $this->get_field_name('venue'); ?>" type="text">
47
+ <option value="" <?php selected($instance['venue'], ''); ?>><?php _e('All Venues','eventorganiser'); ?> </option>
48
+ <?php foreach ($venues as $venue):?>
49
+ <option <?php selected($instance['venue'],$venue->slug);?> value="<?php echo esc_attr($venue->slug);?>"><?php echo esc_html($venue->name); ?></option>
50
+ <?php endforeach;?>
51
+ </select>
52
+ </p>
53
+
54
+ <p>
55
+ <label for="<?php echo $this->get_field_id('orderby'); ?>"><?php _e('Order by', 'eventorganiser'); ?></label>
56
+ <select id="<?php echo $this->get_field_id('orderby'); ?>" name="<?php echo $this->get_field_name('orderby'); ?>" type="text">
57
+ <option value="eventstart" <?php selected($instance['orderby'], 'eventstart'); ?>><?php _e('Start date', 'eventorganiser'); ?></option>
58
+ <option value="title" <?php selected($instance['orderby'], 'title');?>><?php _e('Title', 'eventorganiser'); ?> </option>
59
+ </select>
60
+ <select id="<?php echo $this->get_field_id('order'); ?>" name="<?php echo $this->get_field_name('order'); ?>" type="text">
61
+ <option value="asc" <?php selected($instance['order'], 'asc'); ?>><?php _e('ASC', 'eventorganiser'); ?> </option>
62
+ <option value="desc" <?php selected($instance['order'], 'desc');?>><?php _e('DESC', 'eventorganiser'); ?> </option>
63
+ </select>
64
+ </p>
65
+ <p>
66
+ <label for="<?php echo $this->get_field_id('showpastevents'); ?>"><?php _e('Include past events', 'eventorganiser'); ?> </label>
67
+ <input type="checkbox" id="<?php echo $this->get_field_id('showpastevents'); ?>" name="<?php echo $this->get_field_name('showpastevents'); ?>" <?php checked($instance['showpastevents'],1);?> value="1" />
68
+ </p>
69
+ <p>
70
+ <label for="<?php echo $this->get_field_id('group_events_by'); ?>"><?php _e('Group occurrences', 'eventorganiser'); ?> </label>
71
+ <input type="checkbox" id="<?php echo $this->get_field_id('group_events_by'); ?>" value="series" name="<?php echo $this->get_field_name('group_events_by'); ?>" <?php checked($instance['group_events_by'],'series');?> />
72
+ </p>
73
+ <p>
74
+ <label for="<?php echo $this->get_field_id('template'); ?>">
75
+ <?php
76
+ _e('Template (leave blank for default)', 'eventorganiser');
77
+ echo eventorganiser_inline_help(
78
+ __( 'Event list widget placeholders', 'eventorganiser' ),
79
+ sprintf(
80
+ __( 'You can use specified tags as placeholders for event information which you want to appear in the widget. <a href="%s" target="_blank"> Find out more</a>.', 'eventorganiser' ),
81
+ 'http://wp-event-organiser.com/documentation/widgets/#whatistemplate'
82
+ )
83
+ );
84
+ ?>
85
+ </label>
86
+ <input id="<?php echo $this->get_field_id('template'); ?>" class="widefat" name="<?php echo $this->get_field_name('template'); ?>" type="text" value="<?php echo esc_attr($instance['template']);?>" />
87
+
88
+ </p>
89
+ <p>
90
+ <label for="<?php echo $this->get_field_id('no_events'); ?>"><?php _e("'No events' message", 'eventorganiser'); ?> </label>
91
+ <input id="<?php echo $this->get_field_id('no_events'); ?>" class="widefat" name="<?php echo $this->get_field_name('no_events'); ?>" type="text" value="<?php echo esc_attr($instance['no_events']);?>" />
92
+ </p>
93
+
94
+ <?php
95
+ }
96
+
97
+ function update($new_instance, $old_instance){
98
+ $validated=array();
99
+ $validated['title'] = sanitize_text_field( $new_instance['title'] );
100
+ $validated['numberposts'] = intval($new_instance['numberposts']);
101
+ $event_cats = array_map('sanitize_text_field', explode(',',$new_instance['event-category']));
102
+ $validated['event-category'] = implode(',',$event_cats);
103
+ $validated['venue'] = sanitize_text_field( $new_instance['venue'] );
104
+ $validated['order'] = ($new_instance['order'] == 'asc' ? 'asc' : 'desc');
105
+ $validated['orderby'] = ($new_instance['order'] == 'title' ? 'title' : 'eventstart');
106
+ $validated['showpastevents'] = ( !empty($new_instance['showpastevents']) ? 1: 0);
107
+ $validated['group_events_by'] = ( isset($new_instance['group_events_by']) && $new_instance['group_events_by']=='series' ? 'series': '');
108
+ $validated['template'] = $new_instance['template'];
109
+ $validated['no_events'] = $new_instance['no_events'];
110
+ return $validated;
111
+ }
112
+
113
+
114
+ function widget($args, $instance){
115
+ extract($args, EXTR_SKIP);
116
+
117
+ $template = $instance['template'];
118
+ $no_events = isset($instance['no_events']) ? $instance['no_events'] :'';
119
+ unset($instance['template']);
120
+ unset($instance['no_events']);
121
+
122
+ echo $before_widget;
123
+
124
+ $widget_title = apply_filters('widget_title', $instance['title'], $instance, $this->id_base);
125
+
126
+ if ( $widget_title )
127
+ echo $before_title.esc_html($widget_title).$after_title;
128
+
129
+ eventorganiser_list_events($instance, array('type'=>'widget','class'=>'eo-events eo-events-widget','template'=>$template, 'no_events'=>$no_events));
130
+
131
+ echo $after_widget;
132
+ }
133
+
134
+ }
135
+
136
+ function eventorganiser_list_events( $query, $args=array(), $echo=1 ){
137
+
138
+ $args = array_merge(array(
139
+ 'id'=>'',
140
+ 'class'=>'eo-event-list',
141
+ 'type'=>'shortcode',
142
+ 'no_events' => '',
143
+ ),$args);
144
+
145
+ /* Pass these defaults - backwards compat with using eo_get_events()*/
146
+ $query = wp_parse_args($query, array(
147
+ 'posts_per_page'=>-1,
148
+ 'post_type'=>'event',
149
+ 'supress_filters'=>false,
150
+ 'orderby'=> 'eventstart',
151
+ 'order'=> 'ASC',
152
+ 'showrepeats'=>1,
153
+ 'group_events_by'=>'',
154
+ 'showpastevents'=>true,
155
+ ));
156
+
157
+ //Make sure false and 'False' etc actually get parsed as 0/false (input from shortcodes, for instance, can be varied).
158
+ //This maybe moved to the shortcode handler if this function is made public.
159
+ if( strtolower($query['showpastevents']) === 'false' )
160
+ $query['showpastevents'] = 0;
161
+
162
+ if( !empty($query['numberposts']) ){
163
+ $query['posts_per_page'] = (int) $query['numberposts'];
164
+ }
165
+
166
+ $template = isset($args['template']) ? $args['template'] :'';
167
+
168
+ global $eo_event_loop,$eo_event_loop_args;
169
+ $eo_event_loop_args = $args;
170
+ $eo_event_loop = new WP_Query($query);
171
+
172
+ /* Try to find template - backwards compat. Don't use this filter. Will be removed! */
173
+ $template_file = locate_template(apply_filters('eventorganiser_event_list_loop',false));
174
+ if( $template_file || empty($template) ){
175
+ ob_start();
176
+ if( empty($template_file) )
177
+ $template_file = eo_locate_template( array($eo_event_loop_args['type'].'-event-list.php', 'event-list.php'), true, false );
178
+ else
179
+ require( $template_file );
180
+
181
+ $html = ob_get_contents();
182
+ ob_end_clean();
183
+
184
+
185
+ }else{
186
+ //Using the 'placeholder' template
187
+ $no_events = isset($args['no_events']) ? $args['no_events'] :'';
188
+
189
+
190
+ $line_wrap = '<li class="%2$s">%1$s</li>';
191
+ $id = (!empty($args['id']) ? 'id="'.esc_attr($args['id']).'"' : '');
192
+ $container = '<ul '.$id.' class="%2$s">%1$s</ul>';
193
+
194
+ $html='';
195
+ if( $eo_event_loop->have_posts() ):
196
+ while( $eo_event_loop->have_posts() ): $eo_event_loop->the_post();
197
+
198
+ $event_classes = eo_get_event_classes();
199
+ $html .= sprintf($line_wrap, EventOrganiser_Shortcodes::read_template($template), esc_attr(implode(' ',$event_classes)) );
200
+
201
+ endwhile;
202
+
203
+ elseif( $no_events ):
204
+ $html .= sprintf($line_wrap, $no_events, 'eo-no-events');
205
+ endif;
206
+
207
+ $html = sprintf($container, $html, esc_attr($args['class']) );
208
+ }
209
+
210
+ wp_reset_postdata();
211
+
212
+ if( $echo )
213
+ echo $html;
214
+
215
+ return $html;
216
+ }
217
+ ?>
classes/class-eo-venue-list-table.php ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class used for displaying venue table and handling interations
4
+ */
5
+
6
+ /*
7
+ *The WP_List_Table class isn't automatically available to plugins, so we need
8
+ * to check if it's available and load it if necessary.
9
+ */
10
+ if(!class_exists('WP_List_Table')){
11
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
12
+ }
13
+
14
+ class EO_Venue_List_Table extends WP_List_Table {
15
+
16
+ /*
17
+ * Constructor. Set some default configs.
18
+ */
19
+ function __construct(){
20
+ global $status, $page;
21
+ //Set parent defaults
22
+ parent::__construct( array(
23
+ 'singular' => __('venue','eventorganiser'), //singular name of the listed records
24
+ 'plural' => __('venues','eventorganiser'), //plural name of the listed records
25
+ 'ajax' => true //does this table support ajax?
26
+ ) );
27
+ }
28
+
29
+ /*
30
+ * For more detailed insight into how columns are handled, take a look at
31
+ * WP_List_Table::single_row_columns()
32
+ *
33
+ * @param array $item A singular item (one full row's worth of data)
34
+ * @param array $column_name The name/slug of the column to be processed
35
+ * @return string Text or HTML to be placed inside the column <td>
36
+ */
37
+ function column_default($item, $column_name){
38
+ $term_id = (int) $item->term_id;
39
+ $address = eo_get_venue_address($term_id);
40
+
41
+ switch($column_name){
42
+ case 'venue_slug':
43
+ return esc_html($item->slug);
44
+ case 'posts':
45
+ return intval($item->count);
46
+ default:
47
+ $address_keys = array_keys($address);
48
+ foreach( $address_keys as $key ){
49
+ if( 'venue_'.$key == $column_name ){
50
+ return esc_html($address[$key]);
51
+ }
52
+ }
53
+ //TODO Hook for extra columns?
54
+ return print_r($item,true); //Show the whole array for troubleshooting purposes
55
+ }
56
+ }
57
+
58
+
59
+ /*
60
+ * @see WP_List_Table::::single_row_columns()
61
+ * @param array $item A singular item (one full row's worth of data)
62
+ * @return string Text to be placed inside the column <td>
63
+ */
64
+ function column_name($item){
65
+ $term_id = (int) $item->term_id;
66
+
67
+ //Build row actions
68
+ $actions = array(
69
+ 'edit' => sprintf('<a href="?post_type=event&page=%s&action=%s&event-venue=%s">'.__('Edit').'</a>',$_REQUEST['page'],'edit',$item->slug),
70
+ 'delete' => '<a href="'.wp_nonce_url(sprintf('?post_type=event&page=%s&action=%s&event-venue=%s',$_REQUEST['page'],'delete',$item->slug), 'eventorganiser_delete_venue_'.$item->slug).'">'.__('Delete').' </a>',
71
+ 'view' => sprintf('<a href="%s">'.__('View').'</a>', eo_get_venue_link($term_id)),
72
+ );
73
+
74
+ //Return the title contents
75
+ return sprintf('<a href="?post_type=event&page=%1$s&action=%2$s&event-venue=%3$s" class="row-title">%4$s</a>%5$s',
76
+ /*$1%s*/ $_REQUEST['page'],
77
+ /*$2%s*/ 'edit',
78
+ /*$3%s*/ $item->slug,
79
+ /*$4%s*/ $item->name,
80
+ /*$5%s*/ $this->row_actions($actions)
81
+ );
82
+ }
83
+
84
+ /*
85
+ * Checkbox column for Bulk Actions.
86
+ *
87
+ * @see WP_List_Table::::single_row_columns()
88
+ * @param array $item A singular item (one full row's worth of data)
89
+ * @return string Text to be placed inside the column <td> (movie title only)
90
+ */
91
+ function column_cb($item){
92
+ return sprintf(
93
+ '<input type="checkbox" name="%1$s[]" value="%2$s" />',
94
+ /*$1%s*/ 'event-venue',
95
+ /*$2%s*/ $item->slug //The value of the checkbox should be the record's id
96
+ );
97
+ }
98
+
99
+
100
+ /*
101
+ * Set columns sortable
102
+ *
103
+ * @return array An associative array containing all the columns that should be sortable: 'slugs'=>array('data_values',bool)
104
+ */
105
+ function get_sortable_columns() {
106
+ $sortable_columns = array(
107
+ 'name' => array('name',true), //true means its sorted by default
108
+ 'venue_address' => array('address',false),
109
+ 'venue_city' => array('city',false),
110
+ 'venue_state' => array('state',false),
111
+ 'venue_postcode' => array('postcode',false),
112
+ 'venue_country' => array('country',false),
113
+ 'venue_slug' => array('slug',false),
114
+ 'posts' => array('count',false),
115
+ );
116
+ return $sortable_columns;
117
+ }
118
+
119
+
120
+ /*
121
+ * Set bulk actions
122
+ *
123
+ * @return array An associative array containing all the bulk actions: 'slugs'=>'Visible Titles'
124
+ */
125
+ function get_bulk_actions() {
126
+ $actions = array(
127
+ 'delete' => __('Delete')
128
+ );
129
+ return $actions;
130
+ }
131
+
132
+
133
+ /*
134
+ * Echos the row, after assigning it an ID based ont eh venue being shown. Assign appropriate class to alternate rows.
135
+ */
136
+ function single_row( $item ) {
137
+ static $row_class = '';
138
+ $row_id = 'id="venue-'.$item->term_id.'"';
139
+ $row_class = ( $row_class == '' ? ' class="alternate"' : '' );
140
+ echo '<tr' .$row_class.' '.$row_id.'>';
141
+ echo $this->single_row_columns( $item );
142
+ echo '</tr>';
143
+ }
144
+
145
+ /*
146
+ * Prepare venues for display
147
+ *
148
+ * @uses $this->_column_headers
149
+ * @uses $this->items
150
+ * @uses $this->get_columns()
151
+ * @uses $this->get_sortable_columns()
152
+ * @uses $this->get_pagenum()
153
+ * @uses $this->set_pagination_args()
154
+ */
155
+ function prepare_items() {
156
+
157
+ //Retrieve page number for pagination
158
+ $current_page = (int) $this->get_pagenum();
159
+
160
+ //First, lets decide how many records per page to show
161
+ $screen = get_current_screen();
162
+ $per_page = $this->get_items_per_page( 'edit_event_venue_per_page' );
163
+
164
+ //Get the columns, the hidden columns an sortable columns
165
+ $columns = get_column_headers('event_page_venues');
166
+ $hidden = get_hidden_columns('event_page_venues');
167
+ $sortable = $this->get_sortable_columns();
168
+ $this->_column_headers = array($columns, $hidden, $sortable);
169
+ $taxonomy ='event-venue';
170
+
171
+ $search = (!empty( $_REQUEST['s'] ) ? trim( stripslashes( $_REQUEST['s'] ) ) : '');
172
+ $orderby =( !empty( $_REQUEST['orderby'] ) ? trim( stripslashes($_REQUEST['orderby'])) : '');
173
+ $order =( !empty( $_REQUEST['order'] ) ? trim( stripslashes($_REQUEST['order'])) : '');
174
+
175
+ //Display result
176
+ $this->items = get_terms('event-venue',array(
177
+ 'hide_empty'=>false,
178
+ 'search'=>$search,
179
+ 'offset'=> ($current_page-1)*$per_page,
180
+ 'number'=>$per_page,
181
+ 'orderby'=>$orderby,
182
+ 'order'=>$order
183
+ )
184
+ );
185
+
186
+ $this->set_pagination_args( array(
187
+ 'total_items' => wp_count_terms('event-venue', compact( 'search', 'orderby' ) ),
188
+ 'per_page' => $per_page,
189
+ ) );
190
+
191
+ }
192
+
193
+ }?>
classes/class-eo-widget-categories.php ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Event categories widget class
4
+ *
5
+ * @since 1.8
6
+ */
7
+ class EO_Widget_Categories extends WP_Widget {
8
+
9
+ function __construct() {
10
+ $widget_ops = array( 'classname' => 'eo__event_categories', 'description' => __( "A list or dropdown of event categories" ) );
11
+ parent::__construct('eo-event-categories', __( 'Event Categories', 'eventorganiser' ), $widget_ops);
12
+ }
13
+
14
+ function widget( $args, $instance ) {
15
+ extract( $args );
16
+
17
+ $taxonomy = 'event-category';
18
+
19
+ $title = apply_filters('widget_title', empty( $instance['title'] ) ? __( 'Categories' ) : $instance['title'], $instance, $this->id_base);
20
+ $h = ! empty( $instance['hierarchical'] ) ? '1' : '0';
21
+ $d = ! empty( $instance['dropdown'] ) ? '1' : '0';
22
+
23
+ echo $before_widget;
24
+ if ( $title )
25
+ echo $before_title . $title . $after_title;
26
+
27
+ //Select current category by default
28
+ if( is_tax( $taxonomy ) ){
29
+ $term = get_term( get_queried_object_id() , $taxonomy );
30
+ $selected = ( $term && !is_wp_error( $term ) ? $term->slug : false );
31
+ }else{
32
+ $selected = false;
33
+ }
34
+
35
+ $cat_args = array(
36
+ 'orderby' => 'name',
37
+ 'hierarchical' => false,
38
+ 'taxonomy' => $taxonomy,
39
+ 'id' => 'eo-event-venue',
40
+ 'selected' => $selected
41
+ );
42
+ if ( $d ) {
43
+ $cat_args['walker'] = new EO_Walker_TaxonomyDropdown();
44
+ $cat_args['value'] = 'slug';
45
+ $cat_args['show_option_none'] = __('Select Category');
46
+ wp_dropdown_categories(apply_filters('eventorganiser_widget_event_venues_dropdown_args', $cat_args));
47
+ ?>
48
+
49
+ <script type='text/javascript'>
50
+ /* <![CDATA[ */
51
+ var event_dropdown = document.getElementById("eo-event-cat");
52
+ function eventorganiserDropdownChange() {
53
+ if ( event_dropdown.options[event_dropdown.selectedIndex].value != -1 ) {
54
+ location.href = "<?php echo home_url().'/'.$taxonomy.'=';?>"+event_dropdown.options[event_dropdown.selectedIndex].value;
55
+ }
56
+ }
57
+ event_dropdown.onchange = eventorganiserDropdownChange;
58
+ /* ]]> */
59
+ </script>
60
+
61
+ <?php
62
+ } else {
63
+ ?>
64
+ <ul>
65
+ <?php
66
+ $cat_args['title_li'] = '';
67
+ wp_list_categories(apply_filters('eventorganiser_widget_event_categories_args', $cat_args));
68
+ ?>
69
+ </ul>
70
+ <?php
71
+ }
72
+
73
+ echo $after_widget;
74
+ }
75
+
76
+ function update( $new_instance, $old_instance ) {
77
+ $instance = $old_instance;
78
+ $instance['title'] = strip_tags($new_instance['title']);
79
+ $instance['hierarchical'] = !empty($new_instance['hierarchical']) ? 1 : 0;
80
+ $instance['dropdown'] = !empty($new_instance['dropdown']) ? 1 : 0;
81
+
82
+ return $instance;
83
+ }
84
+
85
+ function form( $instance ) {
86
+ //Defaults
87
+ $instance = wp_parse_args( (array) $instance, array( 'title' => '') );
88
+ $title = esc_attr( $instance['title'] );
89
+ $hierarchical = isset( $instance['hierarchical'] ) ? (bool) $instance['hierarchical'] : false;
90
+ $dropdown = isset( $instance['dropdown'] ) ? (bool) $instance['dropdown'] : false;
91
+ ?>
92
+ <p><label for="<?php echo $this->get_field_id('title'); ?>"><?php _e( 'Title:' ); ?></label>
93
+ <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo $title; ?>" /></p>
94
+
95
+ <p><input type="checkbox" class="checkbox" id="<?php echo $this->get_field_id('dropdown'); ?>" name="<?php echo $this->get_field_name('dropdown'); ?>"<?php checked( $dropdown ); ?> />
96
+ <label for="<?php echo $this->get_field_id('dropdown'); ?>"><?php _e( 'Display as dropdown' ); ?></label><br />
97
+
98
+ <input type="checkbox" class="checkbox" id="<?php echo $this->get_field_id('hierarchical'); ?>" name="<?php echo $this->get_field_name('hierarchical'); ?>"<?php checked( $hierarchical ); ?> />
99
+ <label for="<?php echo $this->get_field_id('hierarchical'); ?>"><?php _e( 'Show hierarchy' ); ?></label></p>
100
+ <?php
101
+ }
102
+
103
+ }
classes/class-eo-widget-venues.php ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Event venues widget class
4
+ *
5
+ * @since 1.8
6
+ */
7
+ class EO_Widget_Venues extends WP_Widget {
8
+
9
+ function __construct() {
10
+ $widget_ops = array( 'classname' => 'eo__event_venues', 'description' => __( "A list or dropdown of event venues" ) );
11
+ parent::__construct('eo-event-venues', __( 'Event Venues', 'eventorganiser' ), $widget_ops);
12
+ }
13
+
14
+ function widget( $args, $instance ) {
15
+ extract( $args );
16
+
17
+ $taxonomy = 'event-venue';
18
+
19
+ $title = apply_filters('widget_title', empty( $instance['title'] ) ? __( 'Venues' ) : $instance['title'], $instance, $this->id_base);
20
+ $d = ! empty( $instance['dropdown'] ) ? '1' : '0';
21
+
22
+ echo $before_widget;
23
+ if ( $title )
24
+ echo $before_title . $title . $after_title;
25
+
26
+ //Select current category by default
27
+ if( is_tax( $taxonomy ) ){
28
+ $term = get_term( get_queried_object_id() , $taxonomy );
29
+ $selected = ( $term && !is_wp_error( $term ) ? $term->slug : false );
30
+ }else{
31
+ $selected = false;
32
+ }
33
+
34
+ $cat_args = array(
35
+ 'orderby' => 'name',
36
+ 'hierarchical' => false,
37
+ 'taxonomy' => $taxonomy,
38
+ 'id' => 'eo-event-venue',
39
+ 'selected' => $selected
40
+ );
41
+ if ( $d ) {
42
+ $cat_args['walker'] = new EO_Walker_TaxonomyDropdown();
43
+ $cat_args['value'] = 'slug';
44
+ $cat_args['show_option_none'] = __('Select Category');
45
+ wp_dropdown_categories(apply_filters('eventorganiser_widget_event_venues_dropdown_args', $cat_args));
46
+ ?>
47
+
48
+ <script type='text/javascript'>
49
+ /* <![CDATA[ */
50
+ var event_venue_dropdown = document.getElementById("eo-event-venue");
51
+ function eventorganiserVenueDropdownChange() {
52
+ console.log( event_venue_dropdown.options[event_venue_dropdown.selectedIndex].value);
53
+ if ( event_venue_dropdown.options[event_venue_dropdown.selectedIndex].value != -1 ) {
54
+ location.href = "<?php echo home_url().'/?'.$taxonomy.'=';?>"+event_venue_dropdown.options[event_venue_dropdown.selectedIndex].value;
55
+ }
56
+ }
57
+ event_venue_dropdown.onchange = eventorganiserVenueDropdownChange;
58
+ /* ]]> */
59
+ </script>
60
+
61
+ <?php
62
+ } else {
63
+ ?>
64
+ <ul>
65
+ <?php
66
+ $cat_args['title_li'] = '';
67
+ wp_list_categories(apply_filters('eventorganiser_widget_event_venues_args', $cat_args));
68
+ ?>
69
+ </ul>
70
+ <?php
71
+ }
72
+
73
+ echo $after_widget;
74
+ }
75
+
76
+ function update( $new_instance, $old_instance ) {
77
+ $instance = $old_instance;
78
+ $instance['title'] = strip_tags($new_instance['title']);
79
+ $instance['dropdown'] = !empty($new_instance['dropdown']) ? 1 : 0;
80
+
81
+ return $instance;
82
+ }
83
+
84
+ function form( $instance ) {
85
+ //Defaults
86
+ $instance = wp_parse_args( (array) $instance, array( 'title' => '') );
87
+ $title = esc_attr( $instance['title'] );
88
+ $dropdown = isset( $instance['dropdown'] ) ? (bool) $instance['dropdown'] : false;
89
+ ?>
90
+ <p><label for="<?php echo $this->get_field_id('title'); ?>"><?php _e( 'Title:' ); ?></label>
91
+ <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo $title; ?>" /></p>
92
+
93
+ <p><input type="checkbox" class="checkbox" id="<?php echo $this->get_field_id('dropdown'); ?>" name="<?php echo $this->get_field_name('dropdown'); ?>"<?php checked( $dropdown ); ?> />
94
+ <label for="<?php echo $this->get_field_id('dropdown'); ?>"><?php _e( 'Display as dropdown' ); ?></label><br />
95
+ <?php
96
+ }
97
+
98
+ }
classes/class-eventorganiser-admin-page.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class EventOrganiser_Admin_Page{
3
+
4
+ static $hook;
5
+ static $title;
6
+ static $menu;
7
+ static $permissions;
8
+ static $slug;
9
+ static $page;
10
+
11
+ function __construct() {
12
+ add_action('admin_init', array($this,'admin_init_actions'));
13
+ add_action('admin_menu', array($this,'add_page'));
14
+ add_action('init', array($this,'set_constants'));
15
+ }
16
+
17
+ function set_constants(){
18
+ }
19
+
20
+ function add_page(){
21
+ self::$page = add_submenu_page($this->hook,$this->title, $this->menu, $this->permissions,$this->slug, array($this,'render_page'),10);
22
+ add_action('load-'.self::$page, array($this,'page_actions'),9);
23
+ add_action('admin_print_scripts-'.self::$page, array($this,'page_styles'),10);
24
+ add_action('admin_print_styles-'.self::$page, array($this,'page_scripts'),10);
25
+ add_action("admin_footer-".self::$page,array($this,'footer_scripts'));
26
+ }
27
+ function footer_scripts(){
28
+ }
29
+
30
+ function page_scripts(){
31
+ }
32
+ /*
33
+ * Actions to be taken prior to page loading. This is after headers have been set.
34
+ * @uses load-$hook
35
+ */
36
+ function page_actions(){
37
+ }
38
+
39
+ function page_styles(){
40
+ }
41
+
42
+ function admin_init_actions(){
43
+ }
44
+
45
+ function current_action(){
46
+
47
+ if ( isset( $_REQUEST['action'] ) && -1 != $_REQUEST['action'] )
48
+ return $_REQUEST['action'];
49
+
50
+ if ( isset( $_REQUEST['action2'] ) && -1 != $_REQUEST['action2'] )
51
+ return $_REQUEST['action2'];
52
+
53
+ return false;
54
+ }
55
+
56
+ function init(){
57
+ }
58
+
59
+ function render_page(){
60
+ $this->init();
61
+ $this->display();
62
+ }
63
+ function display(){
64
+ }
65
+ }
classes/class-eventorganiser-shortcodes.php ADDED
@@ -0,0 +1,407 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class used to create the event calendar shortcode
4
+ *
5
+ *@uses EO_Calendar Widget class to generate calendar html
6
+ */
7
+ class EventOrganiser_Shortcodes {
8
+ static $add_script;
9
+ static $calendars =array();
10
+ static $widget_calendars =array();
11
+ static $map = array();
12
+ static $event;
13
+
14
+ function init() {
15
+ add_shortcode('eo_calendar', array(__CLASS__, 'handle_calendar_shortcode'));
16
+ add_shortcode('eo_fullcalendar', array(__CLASS__, 'handle_fullcalendar_shortcode'));
17
+ add_shortcode('eo_venue_map', array(__CLASS__, 'handle_venuemap_shortcode'));
18
+ add_shortcode('eo_events', array(__CLASS__, 'handle_eventlist_shortcode'));
19
+ add_shortcode('eo_subscribe', array(__CLASS__, 'handle_subscription_shortcode'));
20
+ add_action('wp_footer', array(__CLASS__, 'print_script'));
21
+ }
22
+
23
+ function handle_calendar_shortcode($atts=array()) {
24
+ global $post;
25
+
26
+ /* Shortcodes don't accept hyphens, so convert taxonomy names */
27
+ $taxs = array('category','tag','venue');
28
+ foreach ($taxs as $tax){
29
+ if(isset($atts['event_'.$tax])){
30
+ $atts['event-'.$tax]= $atts['event_'.$tax];
31
+ unset($atts['event_'.$tax]);
32
+ }
33
+ }
34
+
35
+ /* Backwards compatibility */
36
+ $atts = wp_parse_args($atts,array(
37
+ 'showpastevents'=>1,
38
+ ));
39
+
40
+ self::$add_script = true;
41
+
42
+ $id = count(self::$widget_calendars);
43
+ self::$widget_calendars['eo_shortcode_calendar_'.$id] = $atts;
44
+
45
+ $tz = eo_get_blog_timezone();
46
+ $date = isset($_GET['eo_month']) ? $_GET['eo_month'].'-01' : 'now';
47
+ $month = new DateTime($date,$tz);
48
+ $month = date_create($month->format('Y-m-1'),$tz);
49
+
50
+ $html = '<div class="widget_calendar eo-calendar eo-calendar-shortcode eo_widget_calendar" id="eo_shortcode_calendar_'.$id.'">';
51
+ $html .= '<div id="eo_shortcode_calendar_'.$id.'_content">'.EO_Calendar_Widget::generate_output($month,$atts).'</div>';
52
+ $html .= '</div>';
53
+
54
+ return $html;
55
+ }
56
+
57
+ function handle_subscription_shortcode($atts, $content=null) {
58
+ extract( shortcode_atts( array(
59
+ 'title' => 'Subscribe to calendar',
60
+ 'type' => 'google',
61
+ 'class' => '',
62
+ 'id' => '',
63
+ ), $atts ) );
64
+
65
+ $url = eo_get_events_feed();
66
+
67
+ $class = esc_attr($class);
68
+ $title = esc_attr($title);
69
+ $id = esc_attr($id);
70
+
71
+ if(strtolower($type)=='webcal'):
72
+ $url = str_replace( 'http://', 'webcal://',$url);
73
+ elseif( strtolower($type)=='ical' ):
74
+ //Do nothing
75
+ else:
76
+ $url = add_query_arg('cid',urlencode($url),'http://www.google.com/calendar/render');
77
+ endif;
78
+
79
+ $html = '<a href="'.$url.'" target="_blank" class="'.$class.'" title="'.$title.'" id="'.$id.'">'.$content.'</a>';
80
+ return $html;
81
+ }
82
+
83
+ function handle_fullcalendar_shortcode($atts=array()) {
84
+
85
+ /* Handle Boolean attributes - this will be passed as strings, we want them as boolean */
86
+ $bool_atts = array(
87
+ 'key'=>'false',
88
+ 'tooltip'=>'true',
89
+ 'weekends'=>'true',
90
+ 'alldayslot'=>'true',
91
+ );
92
+ $atts = wp_parse_args( $atts, $bool_atts );
93
+
94
+ foreach( $bool_atts as $att => $value )
95
+ $atts[$att] = ( strtolower( $atts[$att] ) == 'true' ? true : false );
96
+
97
+ if( isset($atts['venue']) && !isset( $atts['event_venue'] ) )
98
+ $atts['event_venue'] = $atts['venue'];
99
+ if( isset($atts['category']) && !isset( $atts['event_category'] ) )
100
+ $atts['event_category'] = $atts['category'];
101
+
102
+ return eo_get_event_fullcalendar( $atts );
103
+ }
104
+
105
+ function handle_venuemap_shortcode($atts) {
106
+ global $post;
107
+
108
+ if( !empty($atts['event_venue']) )
109
+ $atts['venue'] = $atts['event_venue'];
110
+
111
+ //If venue is not set get from the venue being quiered or the post being viewed
112
+ if( empty($atts['venue']) ){
113
+ if( eo_is_venue() ){
114
+ $atts['venue']= esc_attr(get_query_var('term'));
115
+ }else{
116
+ $atts['venue'] = eo_get_venue_slug(get_the_ID());
117
+ }
118
+ }
119
+
120
+ $venue_slugs = explode(',',$atts['venue']);
121
+
122
+ $args = shortcode_atts( array(
123
+ 'zoom' => 15, 'scrollwheel'=>'true','zoomcontrol'=>'true',
124
+ 'rotatecontrol'=>'true','pancontrol'=>'true','overviewmapcontrol'=>'true',
125
+ 'streetviewcontrol'=>'true','maptypecontrol'=>'true','draggable'=>'true',
126
+ 'maptypeid' => 'ROADMAP',
127
+ 'width' => '100%','height' => '200px','class' => '',
128
+ 'tooltip'=>'false'
129
+ ), $atts );
130
+
131
+ //Cast options as boolean:
132
+ $bool_options = array('tooltip','scrollwheel','zoomcontrol','rotatecontrol','pancontrol','overviewmapcontrol','streetviewcontrol','draggable','maptypecontrol');
133
+ foreach( $bool_options as $option ){
134
+ $args[$option] = ( $args[$option] == 'false' ? false : true );
135
+ }
136
+
137
+ return eo_get_venue_map($venue_slugs, $args);
138
+ }
139
+
140
+
141
+
142
+ function handle_eventlist_shortcode($atts=array(),$content=null) {
143
+ $taxs = array('category','tag','venue');
144
+ foreach ($taxs as $tax){
145
+ if(isset($atts['event_'.$tax])){
146
+ $atts['event-'.$tax]= $atts['event_'.$tax];
147
+ unset($atts['event_'.$tax]);
148
+ }
149
+ }
150
+
151
+ if((isset($atts['venue']) &&$atts['venue']=='%this%') ||( isset($atts['event-venue']) && $atts['event-venue']=='%this%' )){
152
+ if( eo_get_venue_slug() ){
153
+ $atts['event-venue']= eo_get_venue_slug();
154
+ }else{
155
+ unset($atts['venue']);
156
+ unset($atts['event-venue']);
157
+ }
158
+ }
159
+
160
+ $args = array(
161
+ 'class'=>'eo-events eo-events-shortcode',
162
+ 'template'=>$content,
163
+ 'no_events'=>'',
164
+ 'type'=>'shortcode',
165
+ );
166
+
167
+ return eventorganiser_list_events( $atts,$args, 0);
168
+ }
169
+
170
+
171
+ function read_template($template){
172
+ $patterns = array(
173
+ '/%(event_title)%/',
174
+ '/%(start)({([^{}]*)}{([^{}]*)}|{[^{}]*})?%/',
175
+ '/%(end)({([^{}]*)}{([^{}]*)}|{[^{}]*})?%/',
176
+ '/%(event_venue)%/',
177
+ '/%(event_venue_url)%/',
178
+ '/%(event_cats)%/',
179
+ '/%(event_tags)%/',
180
+ '/%(event_venue_address)%/',
181
+ '/%(event_venue_postcode)%/',
182
+ '/%(event_venue_city)%/',
183
+ '/%(event_venue_country)%/',
184
+ '/%(event_venue_state)%/',
185
+ '/%(event_venue_city)%/',
186
+ '/%(schedule_start)({([^{}]*)}{([^{}]*)}|{[^{}]*})?%/',
187
+ '/%(schedule_last)({([^{}]*)}{([^{}]*)}|{[^{}]*})?%/',
188
+ '/%(schedule_end)({([^{}]*)}{([^{}]*)}|{[^{}]*})?%/',
189
+ '/%(event_thumbnail)(?:{([^{}]+)})?(?:{([^{}]+)})?%/',
190
+ '/%(event_url)%/',
191
+ '/%(event_custom_field){([^{}]+)}%/',
192
+ '/%(event_venue_map)({[^{}]+})?%/',
193
+ '/%(event_excerpt)(?:{(\d+)})?%/',
194
+ '/%(cat_color)%/',
195
+ '/%(event_title_attr)%/',
196
+ '/%(event_duration){([^{}]+)}%/',
197
+ '/%(event_content)%/',
198
+ );
199
+ $template = preg_replace_callback($patterns, array(__CLASS__,'parse_template'), $template);
200
+ return $template;
201
+ }
202
+
203
+ function parse_template($matches){
204
+ global $post;
205
+ $replacement='';
206
+
207
+ switch($matches[1]):
208
+ case 'event_title':
209
+ $replacement = get_the_title();
210
+ break;
211
+
212
+ case 'start':
213
+ case 'end':
214
+ case 'schedule_start':
215
+ case 'schedule_last':
216
+ case 'schedule_end':
217
+ switch(count($matches)):
218
+ case 2:
219
+ $dateFormat = get_option('date_format');
220
+ $dateTime = get_option('time_format');
221
+ break;
222
+ case 3:
223
+ $dateFormat = self::eo_clean_input($matches[2]);
224
+ $dateTime='';
225
+ break;
226
+ case 5:
227
+ $dateFormat = self::eo_clean_input($matches[3]);
228
+ $dateTime = self::eo_clean_input($matches[4]);
229
+ break;
230
+ endswitch;
231
+
232
+ $format = eo_is_all_day(get_the_ID()) ? $dateFormat : $dateFormat . $dateTime;
233
+
234
+ switch( $matches[1] ):
235
+ case 'start':
236
+ $replacement = eo_get_the_start( $format );
237
+ break;
238
+ case 'end':
239
+ $replacement = eo_get_the_end( $format );
240
+ break;
241
+ case 'schedule_start':
242
+ $replacement = eo_get_schedule_start( $format );
243
+ break;
244
+ case 'schedule_last':
245
+ case 'schedule_end':
246
+ $replacement = eo_get_schedule_end( $format );
247
+ break;
248
+ endswitch;
249
+
250
+ break;
251
+ case 'event_duration':
252
+ $start = eo_get_the_start(DATETIMEOBJ);
253
+ $end = eo_get_the_end(DATETIMEOBJ);
254
+ if( eo_is_all_day() )
255
+ $end->modify('+1 minute');
256
+
257
+ if( !function_exists('date_diff') ){
258
+ $duration = date_diff($start,$end);
259
+ $replacement = $duration->format($matches[2]);
260
+ }else{
261
+ $replacement = eo_date_interval($start,$end,$matches[2]);
262
+ }
263
+ break;
264
+
265
+ case 'event_tags':
266
+ $replacement = get_the_term_list( get_the_ID(), 'event-tag', '', ', ','');
267
+ break;
268
+
269
+ case 'event_cats':
270
+ $replacement = get_the_term_list( get_the_ID(), 'event-category', '', ', ','');
271
+ break;
272
+
273
+ case 'event_venue':
274
+ $replacement =eo_get_venue_name();
275
+ break;
276
+
277
+ case 'event_venue_map':
278
+ if(eo_get_venue()){
279
+ $class = (isset($matches[2]) ? self::eo_clean_input($matches[2]) : '');
280
+ $class = (!empty($class) ? 'class='.$class : '');
281
+ $replacement = eo_get_venue_map( eo_get_venue(), compact('class') );
282
+ }
283
+ break;
284
+
285
+ case 'event_venue_url':
286
+ $venue_link =eo_get_venue_link();
287
+ $replacement = ( !is_wp_error($venue_link) ? $venue_link : '');
288
+ break;
289
+ case 'event_venue_address':
290
+ $address = eo_get_venue_address();
291
+ $replacement =$address['address'];
292
+ break;
293
+ case 'event_venue_postcode':
294
+ $address = eo_get_venue_address();
295
+ $replacement =$address['postcode'];
296
+ break;
297
+ case 'event_venue_city':
298
+ $address = eo_get_venue_address();
299
+ $replacement =$address['city'];
300
+ break;
301
+ case 'event_venue_country':
302
+ $address = eo_get_venue_address();
303
+ $replacement =$address['country'];
304
+ break;
305
+ case 'event_venue_state':
306
+ $address = eo_get_venue_address();
307
+ $replacement =$address['state'];
308
+ break;
309
+ case 'event_venue_city':
310
+ $address = eo_get_venue_address();
311
+ $replacement =$address['city'];
312
+ break;
313
+ case 'event_thumbnail':
314
+ $size = (isset($matches[2]) ? self::eo_clean_input($matches[2]) : '');
315
+ $size = (!empty($size) ? $size : 'thumbnail');
316
+ $attr = (isset($matches[3]) ? self::eo_clean_input($matches[3]) : '');
317
+
318
+ //Decode HTML entities as shortcode encodes them
319
+ $attr = html_entity_decode($attr);
320
+ $replacement = get_the_post_thumbnail(get_the_ID(),$size, $attr);
321
+ break;
322
+ case 'event_url':
323
+ $replacement =get_permalink();
324
+ break;
325
+ case 'event_custom_field':
326
+ $field = $matches[2];
327
+ $meta = get_post_meta(get_the_ID(), $field);
328
+ $replacement = implode($meta);
329
+ break;
330
+ case 'event_excerpt':
331
+ $length = ( isset($matches[2]) ? intval($matches[2]) : 55 );
332
+ //Using get_the_excerpt adds a link....
333
+ if ( post_password_required($post) ) {
334
+ $output = __('There is no excerpt because this is a protected post.');
335
+ }else{
336
+ $output = $post->post_excerpt;
337
+ }
338
+ $replacement = eventorganiser_trim_excerpt( $output, $length);
339
+ break;
340
+ case 'event_content':
341
+ $replacement = get_the_content();
342
+ break;
343
+ case 'cat_color':
344
+ $replacement = eo_get_event_color();
345
+ break;
346
+ case 'event_title_attr':
347
+ $replacement = get_the_title();
348
+ break;
349
+
350
+ endswitch;
351
+ return $replacement;
352
+ }
353
+
354
+ function eo_clean_input($input){
355
+ $input = trim($input,"{}"); //remove { }
356
+ $input = str_replace(array("'",'"',"&#8221;","&#8216;", "&#8217;"),'',$input); //remove quotations
357
+ return $input;
358
+ }
359
+
360
+ function print_script() {
361
+ global $wp_locale;
362
+ if ( ! self::$add_script ) return;
363
+ $fullcal = (empty(self::$calendars) ? array() : array(
364
+ 'firstDay'=>intval(get_option('start_of_week')),
365
+ 'venues' => get_terms( 'event-venue', array('hide_empty' => 0)),
366
+ 'categories' => get_terms( 'event-category', array('hide_empty' => 0)),
367
+ ));
368
+ wp_localize_script( 'eo_front', 'EOAjax',
369
+ array(
370
+ 'ajaxurl' => admin_url( 'admin-ajax.php'),
371
+ 'calendars' => self::$calendars,
372
+ 'widget_calendars' => self::$widget_calendars,
373
+ 'fullcal' => $fullcal,
374
+ 'map' => self::$map,
375
+ ));
376
+
377
+ if( !empty(self::$calendars) || !empty(self::$map) || !empty(self::$widget_calendars) ):
378
+ wp_enqueue_script( 'eo_qtip2');
379
+ wp_enqueue_style('eo_calendar-style');
380
+ wp_enqueue_style('eo_front');
381
+ wp_enqueue_script( 'eo_front');
382
+ endif;
383
+
384
+ if( !empty( self::$map ) )
385
+ wp_enqueue_script( 'eo_GoogleMap' );
386
+ }
387
+ }
388
+
389
+ EventOrganiser_Shortcodes::init();
390
+
391
+ function eventorganiser_category_key($args=array(),$id=1){
392
+ $args['taxonomy'] ='event-category';
393
+
394
+ $html ='<div class="eo-fullcalendar-key" id="eo_fullcalendar_key'.$id.'">';
395
+ $terms = get_terms( 'event-category', $args );
396
+ $html.= "<ul class='eo_fullcalendar_key'>";
397
+ foreach ($terms as $term):
398
+ $slug = esc_attr($term->slug);
399
+ $color = esc_attr($term->color);
400
+ $class = "class='eo_fullcalendar_key_cat eo_fullcalendar_key_cat_{$slug}'";
401
+ $html.= "<li {$class}><span class='eo_fullcalendar_key_colour' style='background:{$color}'>&nbsp;</span>".esc_attr($term->name)."</li>";
402
+ endforeach;
403
+ $html.='</ul></div>';
404
+
405
+ return $html;
406
+ }
407
+ ?>
css/eventorganiser-admin-classic.css ADDED
@@ -0,0 +1,576 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * jQuery UI CSS Framework 1.8.16
3
+ *
4
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
5
+ * Dual licensed under the MIT or GPL Version 2 licenses.
6
+ * http://jquery.org/license
7
+ *
8
+ * http://docs.jquery.com/UI/Theming/API
9
+ */
10
+
11
+ /* Layout helpers
12
+ ----------------------------------*/
13
+ .ui-helper-hidden { display: none; }
14
+ .ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
15
+ .ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
16
+ .ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
17
+ .ui-helper-clearfix { display: inline-block; }
18
+ /* required comment for clearfix to work in Opera \*/
19
+ * html .ui-helper-clearfix { height:1%; }
20
+ .ui-helper-clearfix { display:block; }
21
+ /* end clearfix */
22
+ .ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
23
+
24
+
25
+ /* Interaction Cues
26
+ ----------------------------------*/
27
+ .ui-state-disabled { cursor: default !important; }
28
+
29
+
30
+ /* Icons
31
+ ----------------------------------*/
32
+
33
+ /* states and images */
34
+ .ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
35
+
36
+
37
+ /* Misc visuals
38
+ ----------------------------------*/
39
+
40
+ /* Overlays */
41
+ .ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
42
+
43
+
44
+ /*
45
+ * jQuery UI CSS Framework 1.8.16
46
+ *
47
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
48
+ * Dual licensed under the MIT or GPL Version 2 licenses.
49
+ * http://jquery.org/license
50
+ *
51
+ * http://docs.jquery.com/UI/Theming/API
52
+ *
53
+ * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=sans-serif&fwDefault=normal&fsDefault=12px&cornerRadius=3px&bgColorHeader=eff8ff&bgTextureHeader=04_highlight_hard.png&bgImgOpacityHeader=75&borderColorHeader=d1e5ee&fcHeader=333333&iconColorHeader=21759b&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=d1e5ee&fcContent=333333&iconColorContent=333333&bgColorDefault=eff8ff&bgTextureDefault=04_highlight_hard.png&bgImgOpacityDefault=75&borderColorDefault=d1e5ee&fcDefault=333333&iconColorDefault=333333&bgColorHover=f7fcfe&bgTextureHover=06_inset_hard.png&bgImgOpacityHover=75&borderColorHover=b8d7e5&fcHover=000000&iconColorHover=333333&bgColorActive=ffffff&bgTextureActive=01_flat.png&bgImgOpacityActive=65&borderColorActive=d1e5ee&fcActive=333333&iconColorActive=333333&bgColorHighlight=ffffe0&bgTextureHighlight=01_flat.png&bgImgOpacityHighlight=55&borderColorHighlight=e6db55&fcHighlight=333333&iconColorHighlight=21759b&bgColorError=ffebe8&bgTextureError=01_flat.png&bgImgOpacityError=95&borderColorError=cc0000&fcError=cc0000&iconColorError=cc0000&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=3px
54
+ */
55
+
56
+
57
+ /* Component containers
58
+ ----------------------------------*/
59
+ .ui-widget { font-family: sans-serif; font-size: 12px; }
60
+ .ui-widget .ui-widget { font-size: 1em; }
61
+ .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: sans-serif; font-size: 1em; }
62
+ .ui-widget-content { border: 1px solid #d1e5ee; background: #ffffff; color: #333333; }
63
+ .ui-widget-header { border: 1px solid #d1e5ee; background-color: #f5fafd; background-image: -ms-linear-gradient(top, #f7fcfe, #eff8ff); background-image: -moz-linear-gradient(top, #f7fcfe, #eff8ff); background-image: -o-linear-gradient(top, #f7fcfe, #eff8ff); background-image: -webkit-gradient(linear, left top, left bottom, from(#f7fcfe), to(#eff8ff)); background-image: -webkit-linear-gradient(top, #f7fcfe, #eff8ff); background-image: linear-gradient(top, #f7fcfe, #eff8ff); color: #333333; font-weight: bold; }
64
+ .ui-widget-header a { color: #333333; }
65
+
66
+ /* Interaction states
67
+ ----------------------------------*/
68
+ .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d1e5ee; background-color: #f5fafd; background-image: -ms-linear-gradient(top, #f7fcfe, #eff8ff); background-image: -moz-linear-gradient(top, #f7fcfe, #eff8ff); background-image: -o-linear-gradient(top, #f7fcfe, #eff8ff); background-image: -webkit-gradient(linear, left top, left bottom, from(#f7fcfe), to(#eff8ff)); background-image: -webkit-linear-gradient(top, #f7fcfe, #eff8ff); background-image: linear-gradient(top, #f7fcfe, #eff8ff); font-weight: normal; color: #333333; }
69
+ .ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #21759b; text-decoration: none; }
70
+ .ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #b8d7e5; background-color: #f7fcfe; background-image: -ms-linear-gradient(top, #eff8ff, #f7fcfe); background-image: -moz-linear-gradient(top, #eff8ff, #f7fcfe); background-image: -o-linear-gradient(top, #eff8ff, #f7fcfe); background-image: -webkit-gradient(linear, left top, left bottom, from(#eff8ff), to(#f7fcfe)); background-image: -webkit-linear-gradient(top, #eff8ff, #f7fcfe); background-image: linear-gradient(top, #eff8ff, #f7fcfe);; font-weight: normal; color: #000000; }
71
+ .ui-state-hover a, .ui-state-hover a:hover { color: #d54e21; text-decoration: none; }
72
+ .ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #d1e5ee; background: #ffffff; font-weight: normal; color: #333333; }
73
+ .ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #333333; text-decoration: none; }
74
+ .ui-widget :active { outline: none; }
75
+
76
+ /* Interaction Cues
77
+ ----------------------------------*/
78
+ .ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #e6db55; background: #ffffe0; color: #333333; }
79
+ .ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #333333; }
80
+ .ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cc0000; background: #ffebe8; color: #cc0000; }
81
+ .ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cc0000; }
82
+ .ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cc0000; }
83
+ .ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
84
+ .ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
85
+ .ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
86
+
87
+ /* Icons
88
+ ----------------------------------*/
89
+
90
+ /* states and images */
91
+ .ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_21759b_256x240.png); }
92
+ .ui-widget-content .ui-icon {background-image: url(images/ui-icons_21759b_256x240.png); }
93
+ .ui-widget-header .ui-icon {background-image: url(images/ui-icons_21759b_256x240.png); }
94
+ .ui-state-default .ui-icon { background-image: url(images/ui-icons_21759b_256x240.png); }
95
+ .ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_d54e21_256x240.png); }
96
+ .ui-state-active .ui-icon {background-image: url(images/ui-icons_333333_256x240.png); }
97
+ .ui-state-highlight .ui-icon {background-image: url(images/ui-icons_21759b_256x240.png); }
98
+ .ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cc0000_256x240.png); }
99
+
100
+
101
+ /* positioning */
102
+ .ui-icon-carat-1-n { background-position: 0 0; }
103
+ .ui-icon-carat-1-ne { background-position: -16px 0; }
104
+ .ui-icon-carat-1-e { background-position: -32px 0; }
105
+ .ui-icon-carat-1-se { background-position: -48px 0; }
106
+ .ui-icon-carat-1-s { background-position: -64px 0; }
107
+ .ui-icon-carat-1-sw { background-position: -80px 0; }
108
+ .ui-icon-carat-1-w { background-position: -96px 0; }
109
+ .ui-icon-carat-1-nw { background-position: -112px 0; }
110
+ .ui-icon-carat-2-n-s { background-position: -128px 0; }
111
+ .ui-icon-carat-2-e-w { background-position: -144px 0; }
112
+ .ui-icon-triangle-1-n { background-position: 0 -16px; }
113
+ .ui-icon-triangle-1-ne { background-position: -16px -16px; }
114
+ .ui-icon-triangle-1-e { background-position: -32px -16px; }
115
+ .ui-icon-triangle-1-se { background-position: -48px -16px; }
116
+ .ui-icon-triangle-1-s { background-position: -64px -16px; }
117
+ .ui-icon-triangle-1-sw { background-position: -80px -16px; }
118
+ .ui-icon-triangle-1-w { background-position: -96px -16px; }
119
+ .ui-icon-triangle-1-nw { background-position: -112px -16px; }
120
+ .ui-icon-triangle-2-n-s { background-position: -128px -16px; }
121
+ .ui-icon-triangle-2-e-w { background-position: -144px -16px; }
122
+ .ui-icon-arrow-1-n { background-position: 0 -32px; }
123
+ .ui-icon-arrow-1-ne { background-position: -16px -32px; }
124
+ .ui-icon-arrow-1-e { background-position: -32px -32px; }
125
+ .ui-icon-arrow-1-se { background-position: -48px -32px; }
126
+ .ui-icon-arrow-1-s { background-position: -64px -32px; }
127
+ .ui-icon-arrow-1-sw { background-position: -80px -32px; }
128
+ .ui-icon-arrow-1-w { background-position: -96px -32px; }
129
+ .ui-icon-arrow-1-nw { background-position: -112px -32px; }
130
+ .ui-icon-arrow-2-n-s { background-position: -128px -32px; }
131
+ .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
132
+ .ui-icon-arrow-2-e-w { background-position: -160px -32px; }
133
+ .ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
134
+ .ui-icon-arrowstop-1-n { background-position: -192px -32px; }
135
+ .ui-icon-arrowstop-1-e { background-position: -208px -32px; }
136
+ .ui-icon-arrowstop-1-s { background-position: -224px -32px; }
137
+ .ui-icon-arrowstop-1-w { background-position: -240px -32px; }
138
+ .ui-icon-arrowthick-1-n { background-position: 0 -48px; }
139
+ .ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
140
+ .ui-icon-arrowthick-1-e { background-position: -32px -48px; }
141
+ .ui-icon-arrowthick-1-se { background-position: -48px -48px; }
142
+ .ui-icon-arrowthick-1-s { background-position: -64px -48px; }
143
+ .ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
144
+ .ui-icon-arrowthick-1-w { background-position: -96px -48px; }
145
+ .ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
146
+ .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
147
+ .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
148
+ .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
149
+ .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
150
+ .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
151
+ .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
152
+ .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
153
+ .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
154
+ .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
155
+ .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
156
+ .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
157
+ .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
158
+ .ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
159
+ .ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
160
+ .ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
161
+ .ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
162
+ .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
163
+ .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
164
+ .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
165
+ .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
166
+ .ui-icon-arrow-4 { background-position: 0 -80px; }
167
+ .ui-icon-arrow-4-diag { background-position: -16px -80px; }
168
+ .ui-icon-extlink { background-position: -32px -80px; }
169
+ .ui-icon-newwin { background-position: -48px -80px; }
170
+ .ui-icon-refresh { background-position: -64px -80px; }
171
+ .ui-icon-shuffle { background-position: -80px -80px; }
172
+ .ui-icon-transfer-e-w { background-position: -96px -80px; }
173
+ .ui-icon-transferthick-e-w { background-position: -112px -80px; }
174
+ .ui-icon-folder-collapsed { background-position: 0 -96px; }
175
+ .ui-icon-folder-open { background-position: -16px -96px; }
176
+ .ui-icon-document { background-position: -32px -96px; }
177
+ .ui-icon-document-b { background-position: -48px -96px; }
178
+ .ui-icon-note { background-position: -64px -96px; }
179
+ .ui-icon-mail-closed { background-position: -80px -96px; }
180
+ .ui-icon-mail-open { background-position: -96px -96px; }
181
+ .ui-icon-suitcase { background-position: -112px -96px; }
182
+ .ui-icon-comment { background-position: -128px -96px; }
183
+ .ui-icon-person { background-position: -144px -96px; }
184
+ .ui-icon-print { background-position: -160px -96px; }
185
+ .ui-icon-trash { background-position: -176px -96px; }
186
+ .ui-icon-locked { background-position: -192px -96px; }
187
+ .ui-icon-unlocked { background-position: -208px -96px; }
188
+ .ui-icon-bookmark { background-position: -224px -96px; }
189
+ .ui-icon-tag { background-position: -240px -96px; }
190
+ .ui-icon-home { background-position: 0 -112px; }
191
+ .ui-icon-flag { background-position: -16px -112px; }
192
+ .ui-icon-calendar { background-position: -32px -112px; }
193
+ .ui-icon-cart { background-position: -48px -112px; }
194
+ .ui-icon-pencil { background-position: -64px -112px; }
195
+ .ui-icon-clock { background-position: -80px -112px; }
196
+ .ui-icon-disk { background-position: -96px -112px; }
197
+ .ui-icon-calculator { background-position: -112px -112px; }
198
+ .ui-icon-zoomin { background-position: -128px -112px; }
199
+ .ui-icon-zoomout { background-position: -144px -112px; }
200
+ .ui-icon-search { background-position: -160px -112px; }
201
+ .ui-icon-wrench { background-position: -176px -112px; }
202
+ .ui-icon-gear { background-position: -192px -112px; }
203
+ .ui-icon-heart { background-position: -208px -112px; }
204
+ .ui-icon-star { background-position: -224px -112px; }
205
+ .ui-icon-link { background-position: -240px -112px; }
206
+ .ui-icon-cancel { background-position: 0 -128px; }
207
+ .ui-icon-plus { background-position: -16px -128px; }
208
+ .ui-icon-plusthick { background-position: -32px -128px; }
209
+ .ui-icon-minus { background-position: -48px -128px; }
210
+ .ui-icon-minusthick { background-position: -64px -128px; }
211
+ .ui-icon-close { background-position: -80px -128px; }
212
+ .ui-icon-closethick { background-position: -96px -128px; }
213
+ .ui-icon-key { background-position: -112px -128px; }
214
+ .ui-icon-lightbulb { background-position: -128px -128px; }
215
+ .ui-icon-scissors { background-position: -144px -128px; }
216
+ .ui-icon-clipboard { background-position: -160px -128px; }
217
+ .ui-icon-copy { background-position: -176px -128px; }
218
+ .ui-icon-contact { background-position: -192px -128px; }
219
+ .ui-icon-image { background-position: -208px -128px; }
220
+ .ui-icon-video { background-position: -224px -128px; }
221
+ .ui-icon-script { background-position: -240px -128px; }
222
+ .ui-icon-alert { background-position: 0 -144px; }
223
+ .ui-icon-info { background-position: -16px -144px; }
224
+ .ui-icon-notice { background-position: -32px -144px; }
225
+ .ui-icon-help { background-position: -48px -144px; }
226
+ .ui-icon-check { background-position: -64px -144px; }
227
+ .ui-icon-bullet { background-position: -80px -144px; }
228
+ .ui-icon-radio-off { background-position: -96px -144px; }
229
+ .ui-icon-radio-on { background-position: -112px -144px; }
230
+ .ui-icon-pin-w { background-position: -128px -144px; }
231
+ .ui-icon-pin-s { background-position: -144px -144px; }
232
+ .ui-icon-play { background-position: 0 -160px; }
233
+ .ui-icon-pause { background-position: -16px -160px; }
234
+ .ui-icon-seek-next { background-position: -32px -160px; }
235
+ .ui-icon-seek-prev { background-position: -48px -160px; }
236
+ .ui-icon-seek-end { background-position: -64px -160px; }
237
+ .ui-icon-seek-start { background-position: -80px -160px; }
238
+ /* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
239
+ .ui-icon-seek-first { background-position: -80px -160px; }
240
+ .ui-icon-stop { background-position: -96px -160px; }
241
+ .ui-icon-eject { background-position: -112px -160px; }
242
+ .ui-icon-volume-off { background-position: -128px -160px; }
243
+ .ui-icon-volume-on { background-position: -144px -160px; }
244
+ .ui-icon-power { background-position: 0 -176px; }
245
+ .ui-icon-signal-diag { background-position: -16px -176px; }
246
+ .ui-icon-signal { background-position: -32px -176px; }
247
+ .ui-icon-battery-0 { background-position: -48px -176px; }
248
+ .ui-icon-battery-1 { background-position: -64px -176px; }
249
+ .ui-icon-battery-2 { background-position: -80px -176px; }
250
+ .ui-icon-battery-3 { background-position: -96px -176px; }
251
+ .ui-icon-circle-plus { background-position: 0 -192px; }
252
+ .ui-icon-circle-minus { background-position: -16px -192px; }
253
+ .ui-icon-circle-close { background-position: -32px -192px; }
254
+ .ui-icon-circle-triangle-e { background-position: -48px -192px; }
255
+ .ui-icon-circle-triangle-s { background-position: -64px -192px; }
256
+ .ui-icon-circle-triangle-w { background-position: -80px -192px; }
257
+ .ui-icon-circle-triangle-n { background-position: -96px -192px; }
258
+ .ui-icon-circle-arrow-e { background-position: -112px -192px; }
259
+ .ui-icon-circle-arrow-s { background-position: -128px -192px; }
260
+ .ui-icon-circle-arrow-w { background-position: -144px -192px; }
261
+ .ui-icon-circle-arrow-n { background-position: -160px -192px; }
262
+ .ui-icon-circle-zoomin { background-position: -176px -192px; }
263
+ .ui-icon-circle-zoomout { background-position: -192px -192px; }
264
+ .ui-icon-circle-check { background-position: -208px -192px; }
265
+ .ui-icon-circlesmall-plus { background-position: 0 -208px; }
266
+ .ui-icon-circlesmall-minus { background-position: -16px -208px; }
267
+ .ui-icon-circlesmall-close { background-position: -32px -208px; }
268
+ .ui-icon-squaresmall-plus { background-position: -48px -208px; }
269
+ .ui-icon-squaresmall-minus { background-position: -64px -208px; }
270
+ .ui-icon-squaresmall-close { background-position: -80px -208px; }
271
+ .ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
272
+ .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
273
+ .ui-icon-grip-solid-vertical { background-position: -32px -224px; }
274
+ .ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
275
+ .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
276
+ .ui-icon-grip-diagonal-se { background-position: -80px -224px; }
277
+
278
+
279
+ /* Misc visuals
280
+ ----------------------------------*/
281
+
282
+ /* Corner radius */
283
+ .ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 3px; -webkit-border-top-left-radius: 3px; -khtml-border-top-left-radius: 3px; border-top-left-radius: 3px; }
284
+ .ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 3px; -webkit-border-top-right-radius: 3px; -khtml-border-top-right-radius: 3px; border-top-right-radius: 3px; }
285
+ .ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 3px; -webkit-border-bottom-left-radius: 3px; -khtml-border-bottom-left-radius: 3px; border-bottom-left-radius: 3px; }
286
+ .ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 3px; -webkit-border-bottom-right-radius: 3px; -khtml-border-bottom-right-radius: 3px; border-bottom-right-radius: 3px; }
287
+
288
+ /* Overlays */
289
+ .ui-widget-overlay { background: #000000; opacity: .6;filter:Alpha(Opacity=60); }
290
+ .ui-widget-shadow { box-shadow: 0 0 16px rgba(0, 0, 0, 0.3); }/*
291
+ * jQuery UI Resizable 1.8.16
292
+ *
293
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
294
+ * Dual licensed under the MIT or GPL Version 2 licenses.
295
+ * http://jquery.org/license
296
+ *
297
+ * http://docs.jquery.com/UI/Resizable#theming
298
+ */
299
+ .ui-resizable { position: relative;}
300
+ .ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block; }
301
+ .ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
302
+ .ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
303
+ .ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
304
+ .ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
305
+ .ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
306
+ .ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
307
+ .ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
308
+ .ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
309
+ .ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/*
310
+ * jQuery UI Selectable 1.8.16
311
+ *
312
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
313
+ * Dual licensed under the MIT or GPL Version 2 licenses.
314
+ * http://jquery.org/license
315
+ *
316
+ * http://docs.jquery.com/UI/Selectable#theming
317
+ */
318
+ .ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }
319
+ /*
320
+ * jQuery UI Accordion 1.8.16
321
+ *
322
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
323
+ * Dual licensed under the MIT or GPL Version 2 licenses.
324
+ * http://jquery.org/license
325
+ *
326
+ * http://docs.jquery.com/UI/Accordion#theming
327
+ */
328
+ /* IE/Win - Fix animation bug - #4615 */
329
+ .ui-accordion { width: 100%; }
330
+ .ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
331
+ .ui-accordion .ui-accordion-li-fix { display: inline; }
332
+ .ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
333
+ .ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
334
+ .ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
335
+ .ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
336
+ .ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
337
+ .ui-accordion .ui-accordion-content-active { display: block; }
338
+ /*
339
+ * jQuery UI Autocomplete 1.8.16
340
+ *
341
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
342
+ * Dual licensed under the MIT or GPL Version 2 licenses.
343
+ * http://jquery.org/license
344
+ *
345
+ * http://docs.jquery.com/UI/Autocomplete#theming
346
+ */
347
+ .ui-autocomplete { position: absolute; cursor: default; }
348
+
349
+ /* workarounds */
350
+ * html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
351
+
352
+ /*
353
+ * jQuery UI Menu 1.8.16
354
+ *
355
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
356
+ * Dual licensed under the MIT or GPL Version 2 licenses.
357
+ * http://jquery.org/license
358
+ *
359
+ * http://docs.jquery.com/UI/Menu#theming
360
+ */
361
+ .ui-menu {
362
+ list-style:none;
363
+ padding: 2px;
364
+ margin: 0;
365
+ display:block;
366
+ float: left;
367
+ }
368
+ .ui-menu .ui-menu {
369
+ margin-top: -3px;
370
+ }
371
+ .ui-menu .ui-menu-item {
372
+ margin:0;
373
+ padding: 0;
374
+ zoom: 1;
375
+ float: left;
376
+ clear: left;
377
+ width: 100%;
378
+ }
379
+ .ui-menu .ui-menu-item a {
380
+ text-decoration:none;
381
+ display:block;
382
+ padding:.2em .4em;
383
+ line-height:1.5;
384
+ zoom:1;
385
+ }
386
+ .ui-menu .ui-menu-item a.ui-state-hover,
387
+ .ui-menu .ui-menu-item a.ui-state-active {
388
+ font-weight: normal;
389
+ margin: -1px;
390
+ }
391
+ /*
392
+ * jQuery UI Button 1.8.16
393
+ *
394
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
395
+ * Dual licensed under the MIT or GPL Version 2 licenses.
396
+ * http://jquery.org/license
397
+ *
398
+ * http://docs.jquery.com/UI/Button#theming
399
+ */
400
+ .ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
401
+ .ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
402
+ button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
403
+ .ui-button-icons-only { width: 3.4em; }
404
+ button.ui-button-icons-only { width: 3.7em; }
405
+
406
+ /*button text element */
407
+ .ui-button .ui-button-text { display: block; line-height: 1.4; }
408
+ .ui-button-text-only .ui-button-text { padding: .4em 1em; }
409
+ .ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
410
+ .ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
411
+ .ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }
412
+ .ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
413
+ /* no icon support for input elements, provide padding by default */
414
+ input.ui-button { padding: .4em 1em; }
415
+
416
+ /*button icon element(s) */
417
+ .ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
418
+ .ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
419
+ .ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
420
+ .ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
421
+ .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
422
+
423
+ /*button sets*/
424
+ .ui-buttonset { margin-right: 7px; }
425
+ .ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
426
+
427
+ /* workarounds */
428
+ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
429
+ /*
430
+ * jQuery UI Dialog 1.8.16
431
+ *
432
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
433
+ * Dual licensed under the MIT or GPL Version 2 licenses.
434
+ * http://jquery.org/license
435
+ *
436
+ * http://docs.jquery.com/UI/Dialog#theming
437
+ */
438
+ .ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
439
+ .ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative;color: #333!important; }
440
+ .ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
441
+ .ui-dialog .ui-dialog-titlebar-close { position: absolute!important; right: .3em!important; top: 50%!important; width: 19px!important; margin: -10px 0 0 0!important; padding: 1px!important; height: 18px!important; background:none!important; }
442
+ .ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
443
+ .ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0!important; }
444
+ .ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
445
+ .ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
446
+ .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
447
+ .ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
448
+ .ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
449
+ .ui-draggable .ui-dialog-titlebar { cursor: move; }
450
+ /*
451
+ * jQuery UI Slider 1.8.16
452
+ *
453
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
454
+ * Dual licensed under the MIT or GPL Version 2 licenses.
455
+ * http://jquery.org/license
456
+ *
457
+ * http://docs.jquery.com/UI/Slider#theming
458
+ */
459
+ .ui-slider { position: relative; text-align: left; }
460
+ .ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
461
+ .ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
462
+
463
+ .ui-slider-horizontal { height: .8em; }
464
+ .ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
465
+ .ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
466
+ .ui-slider-horizontal .ui-slider-range-min { left: 0; }
467
+ .ui-slider-horizontal .ui-slider-range-max { right: 0; }
468
+
469
+ .ui-slider-vertical { width: .8em; height: 100px; }
470
+ .ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
471
+ .ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
472
+ .ui-slider-vertical .ui-slider-range-min { bottom: 0; }
473
+ .ui-slider-vertical .ui-slider-range-max { top: 0; }/*
474
+ * jQuery UI Tabs 1.8.16
475
+ *
476
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
477
+ * Dual licensed under the MIT or GPL Version 2 licenses.
478
+ * http://jquery.org/license
479
+ *
480
+ * http://docs.jquery.com/UI/Tabs#theming
481
+ */
482
+ .ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
483
+ .ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
484
+ .ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
485
+ .ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
486
+ .ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
487
+ .ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
488
+ .ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
489
+ .ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
490
+ .ui-tabs .ui-tabs-hide { display: none !important; }
491
+ /*
492
+ * jQuery UI Datepicker 1.8.16
493
+ *
494
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
495
+ * Dual licensed under the MIT or GPL Version 2 licenses.
496
+ * http://jquery.org/license
497
+ *
498
+ * http://docs.jquery.com/UI/Datepicker#theming
499
+ */
500
+ .ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
501
+ .ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
502
+ .ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
503
+ .ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
504
+ .ui-datepicker .ui-datepicker-prev { left:2px; }
505
+ .ui-datepicker .ui-datepicker-next { right:2px; }
506
+ .ui-datepicker .ui-datepicker-prev-hover { left:1px; }
507
+ .ui-datepicker .ui-datepicker-next-hover { right:1px; }
508
+ .ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
509
+ .ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
510
+ .ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
511
+ .ui-datepicker select.ui-datepicker-month-year {width: 100%;}
512
+ .ui-datepicker select.ui-datepicker-month,
513
+ .ui-datepicker select.ui-datepicker-year { width: 49%;}
514
+ .ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
515
+ .ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
516
+ .ui-datepicker td { border: 0; padding: 1px; }
517
+ .ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
518
+ .ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
519
+ .ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
520
+ .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
521
+
522
+ /* with multiple calendars */
523
+ .ui-datepicker.ui-datepicker-multi { width:auto; }
524
+ .ui-datepicker-multi .ui-datepicker-group { float:left; }
525
+ .ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
526
+ .ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
527
+ .ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
528
+ .ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
529
+ .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
530
+ .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
531
+ .ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
532
+ .ui-datepicker-row-break { clear:both; width:100%; font-size:0em; }
533
+
534
+ /* RTL support */
535
+ .ui-datepicker-rtl { direction: rtl; }
536
+ .ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
537
+ .ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
538
+ .ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
539
+ .ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
540
+ .ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
541
+ .ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
542
+ .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
543
+ .ui-datepicker-rtl .ui-datepicker-group { float:right; }
544
+ .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
545
+ .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
546
+
547
+ /* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
548
+ .ui-datepicker-cover {
549
+ display: none; /*sorry for IE5*/
550
+ display/**/: block; /*sorry for IE5*/
551
+ position: absolute; /*must have*/
552
+ z-index: -1; /*must have*/
553
+ filter: mask(); /*must have*/
554
+ top: -4px; /*must have*/
555
+ left: -4px; /*must have*/
556
+ width: 200px; /*must have*/
557
+ height: 200px; /*must have*/
558
+ }/*
559
+ * jQuery UI Progressbar 1.8.16
560
+ *
561
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
562
+ * Dual licensed under the MIT or GPL Version 2 licenses.
563
+ * http://jquery.org/license
564
+ *
565
+ * http://docs.jquery.com/UI/Progressbar#theming
566
+ */
567
+ .ui-progressbar { height:2em; text-align: left; }
568
+ .ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }
569
+ .ui-progressbar .ui-widget-header {
570
+ background-color: #83B4D8;
571
+ background-image: linear-gradient(bottom, rgb(114,167,207) 0%, rgb(144,197,238) 100%);
572
+ background-image: -o-linear-gradient(bottom, rgb(114,167,207) 0%, rgb(144,197,238) 100%);
573
+ background-image: -moz-linear-gradient(bottom, rgb(114,167,207) 0%, rgb(144,197,238) 100%);
574
+ background-image: -webkit-linear-gradient(bottom, rgb(114,167,207) 0%, rgb(144,197,238) 100%);
575
+ background-image: -ms-linear-gradient(bottom, rgb(114,167,207) 0%, rgb(144,197,238) 100%);
576
+ }
css/eventorganiser-admin-fresh.css ADDED
@@ -0,0 +1,591 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * jQuery UI CSS Framework 1.8.16
3
+ *
4
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
5
+ * Dual licensed under the MIT or GPL Version 2 licenses.
6
+ * http://jquery.org/license
7
+ *
8
+ * http://docs.jquery.com/UI/Theming/API
9
+ */
10
+
11
+ /* Layout helpers
12
+ ----------------------------------*/
13
+ .ui-helper-hidden { display: none; }
14
+ .ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
15
+ .ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
16
+ .ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
17
+ .ui-helper-clearfix { display: inline-block; }
18
+ /* required comment for clearfix to work in Opera \*/
19
+ * html .ui-helper-clearfix { height:1%; }
20
+ .ui-helper-clearfix { display:block; }
21
+ /* end clearfix */
22
+ .ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
23
+
24
+
25
+ /* Interaction Cues
26
+ ----------------------------------*/
27
+ .ui-state-disabled { cursor: default !important; }
28
+
29
+
30
+ /* Icons
31
+ ----------------------------------*/
32
+
33
+ /* states and images */
34
+ .ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
35
+
36
+
37
+ /* Misc visuals
38
+ ----------------------------------*/
39
+
40
+ /* Overlays */
41
+ .ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
42
+
43
+
44
+ /*
45
+ * jQuery UI CSS Framework 1.8.16
46
+ *
47
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
48
+ * Dual licensed under the MIT or GPL Version 2 licenses.
49
+ * http://jquery.org/license
50
+ *
51
+ * http://docs.jquery.com/UI/Theming/API
52
+ *
53
+ * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=sans-serif&fwDefault=normal&fsDefault=12px&cornerRadius=4px&bgColorHeader=ececec&bgTextureHeader=04_highlight_hard.png&bgImgOpacityHeader=75&borderColorHeader=dfdfdf&fcHeader=333333&iconColorHeader=999999&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=dfdfdf&fcContent=333333&iconColorContent=333333&bgColorDefault=ececec&bgTextureDefault=04_highlight_hard.png&bgImgOpacityDefault=75&borderColorDefault=dfdfdf&fcDefault=333333&iconColorDefault=333333&bgColorHover=ececec&bgTextureHover=06_inset_hard.png&bgImgOpacityHover=75&borderColorHover=ccc&fcHover=000000&iconColorHover=333333&bgColorActive=ffffff&bgTextureActive=01_flat.png&bgImgOpacityActive=65&borderColorActive=dfdfdf&fcActive=333333&iconColorActive=333333&bgColorHighlight=ffffe0&bgTextureHighlight=01_flat.png&bgImgOpacityHighlight=55&borderColorHighlight=e6db55&fcHighlight=333333&iconColorHighlight=21759b&bgColorError=ffebe8&bgTextureError=01_flat.png&bgImgOpacityError=95&borderColorError=cc0000&fcError=cc0000&iconColorError=cc0000&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=3px
54
+ */
55
+
56
+
57
+ /* Component containers
58
+ ----------------------------------*/
59
+ .ui-widget { font-family: sans-serif; font-size: 12px; }
60
+ .ui-widget .ui-widget { font-size: 1em; }
61
+ .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: sans-serif; font-size: 1em; }
62
+ .ui-widget-content { border: 1px solid #dfdfdf; background: #ffffff; color: #333333; }
63
+ .ui-widget-header { border: 1px solid #dfdfdf; color: #333333; font-weight: bold; background-color: #f1f1f1; background-image: -ms-linear-gradient(top, #f9f9f9, #ececec); background-image: -moz-linear-gradient(top, #f9f9f9, #ececec); background-image: -o-linear-gradient(top, #f9f9f9, #ececec); background-image: -webkit-gradient(linear, left top, left bottom, from(#f9f9f9), to(#ececec)); background-image: -webkit-linear-gradient(top, #f9f9f9, #ececec); background-image: linear-gradient(top, #f9f9f9, #ececec); }
64
+ .ui-widget-header a { color: #333333; }
65
+
66
+ /* Interaction states
67
+ ----------------------------------*/
68
+ .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #dfdfdf; background-color: #f1f1f1; background-image: -ms-linear-gradient(top, #f9f9f9, #ececec); background-image: -moz-linear-gradient(top, #f9f9f9, #ececec); background-image: -o-linear-gradient(top, #f9f9f9, #ececec); background-image: -webkit-gradient(linear, left top, left bottom, from(#f9f9f9), to(#ececec)); background-image: -webkit-linear-gradient(top, #f9f9f9, #ececec); background-image: linear-gradient(top, #f9f9f9, #ececec); font-weight: normal; color: #333333; }
69
+ .ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #21759b; text-decoration: none; }
70
+ .ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #ccc; background-color: #ececec; background-image: -ms-linear-gradient(top, #ececec, #f9f9f9); background-image: -moz-linear-gradient(top, #ececec, #f9f9f9); background-image: -o-linear-gradient(top, #ececec, #f9f9f9); background-image: -webkit-gradient(linear, left top, left bottom, from(#ececec), to(#f9f9f9)); background-image: -webkit-linear-gradient(top, #ececec, #f9f9f9); background-image: linear-gradient(top, #ececec, #f9f9f9); font-weight: normal; color: #000000; }
71
+ .ui-state-hover a, .ui-state-hover a:hover { color: #d54e21; text-decoration: none; }
72
+ .ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #dfdfdf; background: #ffffff; font-weight: normal; color: #333333; }
73
+ .ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #333333; text-decoration: none; }
74
+ .ui-widget :active { outline: none; }
75
+
76
+ /* Interaction Cues
77
+ ----------------------------------*/
78
+ .ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #e6db55; background: #ffffe0; color: #333333; }
79
+ .ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #333333; }
80
+ .ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cc0000; background: #ffebe8; color: #cc0000; }
81
+ .ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cc0000; }
82
+ .ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cc0000; }
83
+ .ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
84
+ .ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
85
+ .ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
86
+
87
+ /* Icons
88
+ ----------------------------------*/
89
+
90
+ /* states and images */
91
+ .ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_21759b_256x240.png); }
92
+ .ui-widget-content .ui-icon {background-image: url(images/ui-icons_21759b_256x240.png); }
93
+ .ui-widget-header .ui-icon {background-image: url(images/ui-icons_21759b_256x240.png); }
94
+ .ui-state-default .ui-icon { background-image: url(images/ui-icons_21759b_256x240.png); }
95
+ .ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_d54e21_256x240.png); }
96
+ .ui-state-active .ui-icon {background-image: url(images/ui-icons_333333_256x240.png); }
97
+ .ui-state-highlight .ui-icon {background-image: url(images/ui-icons_21759b_256x240.png); }
98
+ .ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cc0000_256x240.png); }
99
+
100
+ /* positioning */
101
+ .ui-icon-carat-1-n { background-position: 0 0; }
102
+ .ui-icon-carat-1-ne { background-position: -16px 0; }
103
+ .ui-icon-carat-1-e { background-position: -32px 0; }
104
+ .ui-icon-carat-1-se { background-position: -48px 0; }
105
+ .ui-icon-carat-1-s { background-position: -64px 0; }
106
+ .ui-icon-carat-1-sw { background-position: -80px 0; }
107
+ .ui-icon-carat-1-w { background-position: -96px 0; }
108
+ .ui-icon-carat-1-nw { background-position: -112px 0; }
109
+ .ui-icon-carat-2-n-s { background-position: -128px 0; }
110
+ .ui-icon-carat-2-e-w { background-position: -144px 0; }
111
+ .ui-icon-triangle-1-n { background-position: 0 -16px; }
112
+ .ui-icon-triangle-1-ne { background-position: -16px -16px; }
113
+ .ui-icon-triangle-1-e { background-position: -32px -16px; }
114
+ .ui-icon-triangle-1-se { background-position: -48px -16px; }
115
+ .ui-icon-triangle-1-s { background-position: -64px -16px; }
116
+ .ui-icon-triangle-1-sw { background-position: -80px -16px; }
117
+ .ui-icon-triangle-1-w { background-position: -96px -16px; }
118
+ .ui-icon-triangle-1-nw { background-position: -112px -16px; }
119
+ .ui-icon-triangle-2-n-s { background-position: -128px -16px; }
120
+ .ui-icon-triangle-2-e-w { background-position: -144px -16px; }
121
+ .ui-icon-arrow-1-n { background-position: 0 -32px; }
122
+ .ui-icon-arrow-1-ne { background-position: -16px -32px; }
123
+ .ui-icon-arrow-1-e { background-position: -32px -32px; }
124
+ .ui-icon-arrow-1-se { background-position: -48px -32px; }
125
+ .ui-icon-arrow-1-s { background-position: -64px -32px; }
126
+ .ui-icon-arrow-1-sw { background-position: -80px -32px; }
127
+ .ui-icon-arrow-1-w { background-position: -96px -32px; }
128
+ .ui-icon-arrow-1-nw { background-position: -112px -32px; }
129
+ .ui-icon-arrow-2-n-s { background-position: -128px -32px; }
130
+ .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
131
+ .ui-icon-arrow-2-e-w { background-position: -160px -32px; }
132
+ .ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
133
+ .ui-icon-arrowstop-1-n { background-position: -192px -32px; }
134
+ .ui-icon-arrowstop-1-e { background-position: -208px -32px; }
135
+ .ui-icon-arrowstop-1-s { background-position: -224px -32px; }
136
+ .ui-icon-arrowstop-1-w { background-position: -240px -32px; }
137
+ .ui-icon-arrowthick-1-n { background-position: 0 -48px; }
138
+ .ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
139
+ .ui-icon-arrowthick-1-e { background-position: -32px -48px; }
140
+ .ui-icon-arrowthick-1-se { background-position: -48px -48px; }
141
+ .ui-icon-arrowthick-1-s { background-position: -64px -48px; }
142
+ .ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
143
+ .ui-icon-arrowthick-1-w { background-position: -96px -48px; }
144
+ .ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
145
+ .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
146
+ .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
147
+ .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
148
+ .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
149
+ .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
150
+ .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
151
+ .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
152
+ .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
153
+ .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
154
+ .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
155
+ .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
156
+ .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
157
+ .ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
158
+ .ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
159
+ .ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
160
+ .ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
161
+ .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
162
+ .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
163
+ .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
164
+ .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
165
+ .ui-icon-arrow-4 { background-position: 0 -80px; }
166
+ .ui-icon-arrow-4-diag { background-position: -16px -80px; }
167
+ .ui-icon-extlink { background-position: -32px -80px; }
168
+ .ui-icon-newwin { background-position: -48px -80px; }
169
+ .ui-icon-refresh { background-position: -64px -80px; }
170
+ .ui-icon-shuffle { background-position: -80px -80px; }
171
+ .ui-icon-transfer-e-w { background-position: -96px -80px; }
172
+ .ui-icon-transferthick-e-w { background-position: -112px -80px; }
173
+ .ui-icon-folder-collapsed { background-position: 0 -96px; }
174
+ .ui-icon-folder-open { background-position: -16px -96px; }
175
+ .ui-icon-document { background-position: -32px -96px; }
176
+ .ui-icon-document-b { background-position: -48px -96px; }
177
+ .ui-icon-note { background-position: -64px -96px; }
178
+ .ui-icon-mail-closed { background-position: -80px -96px; }
179
+ .ui-icon-mail-open { background-position: -96px -96px; }
180
+ .ui-icon-suitcase { background-position: -112px -96px; }
181
+ .ui-icon-comment { background-position: -128px -96px; }
182
+ .ui-icon-person { background-position: -144px -96px; }
183
+ .ui-icon-print { background-position: -160px -96px; }
184
+ .ui-icon-trash { background-position: -176px -96px; }
185
+ .ui-icon-locked { background-position: -192px -96px; }
186
+ .ui-icon-unlocked { background-position: -208px -96px; }
187
+ .ui-icon-bookmark { background-position: -224px -96px; }
188
+ .ui-icon-tag { background-position: -240px -96px; }
189
+ .ui-icon-home { background-position: 0 -112px; }
190
+ .ui-icon-flag { background-position: -16px -112px; }
191
+ .ui-icon-calendar { background-position: -32px -112px; }
192
+ .ui-icon-cart { background-position: -48px -112px; }
193
+ .ui-icon-pencil { background-position: -64px -112px; }
194
+ .ui-icon-clock { background-position: -80px -112px; }
195
+ .ui-icon-disk { background-position: -96px -112px; }
196
+ .ui-icon-calculator { background-position: -112px -112px; }
197
+ .ui-icon-zoomin { background-position: -128px -112px; }
198
+ .ui-icon-zoomout { background-position: -144px -112px; }
199
+ .ui-icon-search { background-position: -160px -112px; }
200
+ .ui-icon-wrench { background-position: -176px -112px; }
201
+ .ui-icon-gear { background-position: -192px -112px; }
202
+ .ui-icon-heart { background-position: -208px -112px; }
203
+ .ui-icon-star { background-position: -224px -112px; }
204
+ .ui-icon-link { background-position: -240px -112px; }
205
+ .ui-icon-cancel { background-position: 0 -128px; }
206
+ .ui-icon-plus { background-position: -16px -128px; }
207
+ .ui-icon-plusthick { background-position: -32px -128px; }
208
+ .ui-icon-minus { background-position: -48px -128px; }
209
+ .ui-icon-minusthick { background-position: -64px -128px; }
210
+ .ui-icon-close { background-position: -80px -128px; }
211
+ .ui-icon-closethick { background-position: -96px -128px; }
212
+ .ui-icon-key { background-position: -112px -128px; }
213
+ .ui-icon-lightbulb { background-position: -128px -128px; }
214
+ .ui-icon-scissors { background-position: -144px -128px; }
215
+ .ui-icon-clipboard { background-position: -160px -128px; }
216
+ .ui-icon-copy { background-position: -176px -128px; }
217
+ .ui-icon-contact { background-position: -192px -128px; }
218
+ .ui-icon-image { background-position: -208px -128px; }
219
+ .ui-icon-video { background-position: -224px -128px; }
220
+ .ui-icon-script { background-position: -240px -128px; }
221
+ .ui-icon-alert { background-position: 0 -144px; }
222
+ .ui-icon-info { background-position: -16px -144px; }
223
+ .ui-icon-notice { background-position: -32px -144px; }
224
+ .ui-icon-help { background-position: -48px -144px; }
225
+ .ui-icon-check { background-position: -64px -144px; }
226
+ .ui-icon-bullet { background-position: -80px -144px; }
227
+ .ui-icon-radio-off { background-position: -96px -144px; }
228
+ .ui-icon-radio-on { background-position: -112px -144px; }
229
+ .ui-icon-pin-w { background-position: -128px -144px; }
230
+ .ui-icon-pin-s { background-position: -144px -144px; }
231
+ .ui-icon-play { background-position: 0 -160px; }
232
+ .ui-icon-pause { background-position: -16px -160px; }
233
+ .ui-icon-seek-next { background-position: -32px -160px; }
234
+ .ui-icon-seek-prev { background-position: -48px -160px; }
235
+ .ui-icon-seek-end { background-position: -64px -160px; }
236
+ .ui-icon-seek-start { background-position: -80px -160px; }
237
+ /* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
238
+ .ui-icon-seek-first { background-position: -80px -160px; }
239
+ .ui-icon-stop { background-position: -96px -160px; }
240
+ .ui-icon-eject { background-position: -112px -160px; }
241
+ .ui-icon-volume-off { background-position: -128px -160px; }
242
+ .ui-icon-volume-on { background-position: -144px -160px; }
243
+ .ui-icon-power { background-position: 0 -176px; }
244
+ .ui-icon-signal-diag { background-position: -16px -176px; }
245
+ .ui-icon-signal { background-position: -32px -176px; }
246
+ .ui-icon-battery-0 { background-position: -48px -176px; }
247
+ .ui-icon-battery-1 { background-position: -64px -176px; }
248
+ .ui-icon-battery-2 { background-position: -80px -176px; }
249
+ .ui-icon-battery-3 { background-position: -96px -176px; }
250
+ .ui-icon-circle-plus { background-position: 0 -192px; }
251
+ .ui-icon-circle-minus { background-position: -16px -192px; }
252
+ .ui-icon-circle-close { background-position: -32px -192px; }
253
+ .ui-icon-circle-triangle-e { background-position: -48px -192px; }
254
+ .ui-icon-circle-triangle-s { background-position: -64px -192px; }
255
+ .ui-icon-circle-triangle-w { background-position: -80px -192px; }
256
+ .ui-icon-circle-triangle-n { background-position: -96px -192px; }
257
+ .ui-icon-circle-arrow-e { background-position: -112px -192px; }
258
+ .ui-icon-circle-arrow-s { background-position: -128px -192px; }
259
+ .ui-icon-circle-arrow-w { background-position: -144px -192px; }
260
+ .ui-icon-circle-arrow-n { background-position: -160px -192px; }
261
+ .ui-icon-circle-zoomin { background-position: -176px -192px; }
262
+ .ui-icon-circle-zoomout { background-position: -192px -192px; }
263
+ .ui-icon-circle-check { background-position: -208px -192px; }
264
+ .ui-icon-circlesmall-plus { background-position: 0 -208px; }
265
+ .ui-icon-circlesmall-minus { background-position: -16px -208px; }
266
+ .ui-icon-circlesmall-close { background-position: -32px -208px; }
267
+ .ui-icon-squaresmall-plus { background-position: -48px -208px; }
268
+ .ui-icon-squaresmall-minus { background-position: -64px -208px; }
269
+ .ui-icon-squaresmall-close { background-position: -80px -208px; }
270
+ .ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
271
+ .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
272
+ .ui-icon-grip-solid-vertical { background-position: -32px -224px; }
273
+ .ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
274
+ .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
275
+ .ui-icon-grip-diagonal-se { background-position: -80px -224px; }
276
+
277
+
278
+ /* Misc visuals
279
+ ----------------------------------*/
280
+
281
+ /* Corner radius */
282
+ .ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 3px; -webkit-border-top-left-radius: 3px; border-top-left-radius: 3px; }
283
+ .ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 3px; -webkit-border-top-right-radius: 3px; border-top-right-radius: 3px; }
284
+ .ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 3px; -webkit-border-bottom-left-radius: 3px; border-bottom-left-radius: 3px; }
285
+ .ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 3px; -webkit-border-bottom-right-radius: 3px; border-bottom-right-radius: 3px; }
286
+
287
+ /* Overlays */
288
+ .ui-widget-overlay { background: #000000; opacity: .6;filter:Alpha(Opacity=60); }
289
+ .ui-widget-shadow { box-shadow: 0 0 16px rgba(0, 0, 0, 0.3); }/*
290
+ * jQuery UI Resizable 1.8.16
291
+ *
292
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
293
+ * Dual licensed under the MIT or GPL Version 2 licenses.
294
+ * http://jquery.org/license
295
+ *
296
+ * http://docs.jquery.com/UI/Resizable#theming
297
+ */
298
+ .ui-resizable { position: relative;}
299
+ .ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block; }
300
+ .ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
301
+ .ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
302
+ .ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
303
+ .ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
304
+ .ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
305
+ .ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
306
+ .ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
307
+ .ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
308
+ .ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/*
309
+ * jQuery UI Selectable 1.8.16
310
+ *
311
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
312
+ * Dual licensed under the MIT or GPL Version 2 licenses.
313
+ * http://jquery.org/license
314
+ *
315
+ * http://docs.jquery.com/UI/Selectable#theming
316
+ */
317
+ .ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }
318
+ /*
319
+ * jQuery UI Accordion 1.8.16
320
+ *
321
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
322
+ * Dual licensed under the MIT or GPL Version 2 licenses.
323
+ * http://jquery.org/license
324
+ *
325
+ * http://docs.jquery.com/UI/Accordion#theming
326
+ */
327
+ /* IE/Win - Fix animation bug - #4615 */
328
+ .ui-accordion { width: 100%; }
329
+ .ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
330
+ .ui-accordion .ui-accordion-li-fix { display: inline; }
331
+ .ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
332
+ .ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
333
+ .ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
334
+ .ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
335
+ .ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
336
+ .ui-accordion .ui-accordion-content-active { display: block; }
337
+ /*
338
+ * jQuery UI Autocomplete 1.8.16
339
+ *
340
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
341
+ * Dual licensed under the MIT or GPL Version 2 licenses.
342
+ * http://jquery.org/license
343
+ *
344
+ * http://docs.jquery.com/UI/Autocomplete#theming
345
+ */
346
+ .ui-autocomplete { position: absolute; cursor: default; }
347
+
348
+ /* workarounds */
349
+ * html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
350
+
351
+ /*
352
+ * jQuery UI Menu 1.8.16
353
+ *
354
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
355
+ * Dual licensed under the MIT or GPL Version 2 licenses.
356
+ * http://jquery.org/license
357
+ *
358
+ * http://docs.jquery.com/UI/Menu#theming
359
+ */
360
+ .ui-menu {
361
+ list-style:none;
362
+ padding: 2px;
363
+ margin: 0;
364
+ display:block;
365
+ float: left;
366
+ }
367
+ .ui-menu .ui-menu {
368
+ margin-top: -3px;
369
+ }
370
+ .ui-menu .ui-menu-item {
371
+ margin:0;
372
+ padding: 0;
373
+ zoom: 1;
374
+ float: left;
375
+ clear: left;
376
+ width: 100%;
377
+ }
378
+ .ui-menu .ui-menu-item a {
379
+ text-decoration:none;
380
+ display:block;
381
+ padding:.2em .4em;
382
+ line-height:1.5;
383
+ zoom:1;
384
+ }
385
+ .ui-menu .ui-menu-item a.ui-state-hover,
386
+ .ui-menu .ui-menu-item a.ui-state-active {
387
+ font-weight: normal;
388
+ margin: -1px;
389
+ }
390
+ /*
391
+ * jQuery UI Button 1.8.16
392
+ *
393
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
394
+ * Dual licensed under the MIT or GPL Version 2 licenses.
395
+ * http://jquery.org/license
396
+ *
397
+ * http://docs.jquery.com/UI/Button#theming
398
+ */
399
+ .ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
400
+ .ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
401
+ button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
402
+ .ui-button-icons-only { width: 3.4em; }
403
+ button.ui-button-icons-only { width: 3.7em; }
404
+
405
+ /*button text element */
406
+ .ui-button .ui-button-text { display: block; line-height: 1.4; }
407
+ .ui-button-text-only .ui-button-text { padding: .4em 1em; }
408
+ .ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
409
+ .ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
410
+ .ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }
411
+ .ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
412
+ /* no icon support for input elements, provide padding by default */
413
+ input.ui-button { padding: .4em 1em; }
414
+
415
+ /*button icon element(s) */
416
+ .ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
417
+ .ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
418
+ .ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
419
+ .ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
420
+ .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
421
+
422
+ /*button sets*/
423
+ .ui-buttonset { margin-right: 7px; }
424
+ .ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
425
+
426
+ /* workarounds */
427
+ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
428
+ /*
429
+ * jQuery UI Dialog 1.8.16
430
+ *
431
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
432
+ * Dual licensed under the MIT or GPL Version 2 licenses.
433
+ * http://jquery.org/license
434
+ *
435
+ * http://docs.jquery.com/UI/Dialog#theming
436
+ */
437
+ .ui-dialog { position: fixed; padding: .2em; width: 300px; overflow: hidden; }
438
+ .ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative;color: #333!important; }
439
+ .ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
440
+ .ui-dialog .ui-dialog-titlebar-close { position: absolute!important; right: .3em!important; top: 50%!important; width: 19px!important; margin: -10px 0 0 0!important; padding: 1px!important; height: 18px!important; background:none!important; }
441
+ .ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
442
+ .ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0!important; }
443
+ .ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
444
+ .ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
445
+ .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
446
+ .ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
447
+ .ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
448
+ .ui-draggable .ui-dialog-titlebar { cursor: move; }
449
+ /*
450
+ * jQuery UI Slider 1.8.16
451
+ *
452
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
453
+ * Dual licensed under the MIT or GPL Version 2 licenses.
454
+ * http://jquery.org/license
455
+ *
456
+ * http://docs.jquery.com/UI/Slider#theming
457
+ */
458
+ .ui-slider { position: relative; text-align: left; }
459
+ .ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
460
+ .ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
461
+
462
+ .ui-slider-horizontal { height: .8em; }
463
+ .ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
464
+ .ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
465
+ .ui-slider-horizontal .ui-slider-range-min { left: 0; }
466
+ .ui-slider-horizontal .ui-slider-range-max { right: 0; }
467
+
468
+ .ui-slider-vertical { width: .8em; height: 100px; }
469
+ .ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
470
+ .ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
471
+ .ui-slider-vertical .ui-slider-range-min { bottom: 0; }
472
+ .ui-slider-vertical .ui-slider-range-max { top: 0; }/*
473
+ * jQuery UI Tabs 1.8.16
474
+ *
475
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
476
+ * Dual licensed under the MIT or GPL Version 2 licenses.
477
+ * http://jquery.org/license
478
+ *
479
+ * http://docs.jquery.com/UI/Tabs#theming
480
+ */
481
+ .ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
482
+ .ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
483
+ .ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
484
+ .ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
485
+ .ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
486
+ .ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
487
+ .ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
488
+ .ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
489
+ .ui-tabs .ui-tabs-hide { display: none !important; }
490
+ /*
491
+ * jQuery UI Datepicker 1.8.16
492
+ *
493
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
494
+ * Dual licensed under the MIT or GPL Version 2 licenses.
495
+ * http://jquery.org/license
496
+ *
497
+ * http://docs.jquery.com/UI/Datepicker#theming
498
+ */
499
+ .ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
500
+ .ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
501
+ .ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
502
+ .ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
503
+ .ui-datepicker .ui-datepicker-prev { left:2px; }
504
+ .ui-datepicker .ui-datepicker-next { right:2px; }
505
+ .ui-datepicker .ui-datepicker-prev-hover { left:1px; }
506
+ .ui-datepicker .ui-datepicker-next-hover { right:1px; }
507
+ .ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
508
+ .ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
509
+ .ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
510
+ .ui-datepicker select.ui-datepicker-month-year {width: 100%;}
511
+ .ui-datepicker select.ui-datepicker-month,
512
+ .ui-datepicker select.ui-datepicker-year { width: 49%;}
513
+ .ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
514
+ .ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
515
+ .ui-datepicker td { border: 0; padding: 1px; }
516
+ .ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
517
+ .ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
518
+ .ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
519
+ .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
520
+
521
+ /* with multiple calendars */
522
+ .ui-datepicker.ui-datepicker-multi { width:auto; }
523
+ .ui-datepicker-multi .ui-datepicker-group { float:left; }
524
+ .ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
525
+ .ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
526
+ .ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
527
+ .ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
528
+ .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
529
+ .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
530
+ .ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
531
+ .ui-datepicker-row-break { clear:both; width:100%; font-size:0em; }
532
+
533
+ /* RTL support */
534
+ .ui-datepicker-rtl { direction: rtl; }
535
+ .ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
536
+ .ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
537
+ .ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
538
+ .ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
539
+ .ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
540
+ .ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
541
+ .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
542
+ .ui-datepicker-rtl .ui-datepicker-group { float:right; }
543
+ .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
544
+ .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
545
+
546
+ /* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
547
+ .ui-datepicker-cover {
548
+ display: none; /*sorry for IE5*/
549
+ display/**/: block; /*sorry for IE5*/
550
+ position: absolute; /*must have*/
551
+ z-index: -1; /*must have*/
552
+ filter: mask(); /*must have*/
553
+ top: -4px; /*must have*/
554
+ left: -4px; /*must have*/
555
+ width: 200px; /*must have*/
556
+ height: 200px; /*must have*/
557
+ }/*
558
+ * jQuery UI Progressbar 1.8.16
559
+ *
560
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
561
+ * Dual licensed under the MIT or GPL Version 2 licenses.
562
+ * http://jquery.org/license
563
+ *
564
+ * http://docs.jquery.com/UI/Progressbar#theming
565
+ */
566
+ .ui-progressbar { height:2em; text-align: left; }
567
+ .ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }
568
+ .ui-progressbar .ui-widget-header {
569
+ background-color: #83B4D8;
570
+ background-image: linear-gradient(bottom, rgb(114,167,207) 0%, rgb(144,197,238) 100%);
571
+ background-image: -o-linear-gradient(bottom, rgb(114,167,207) 0%, rgb(144,197,238) 100%);
572
+ background-image: -moz-linear-gradient(bottom, rgb(114,167,207) 0%, rgb(144,197,238) 100%);
573
+ background-image: -webkit-linear-gradient(bottom, rgb(114,167,207) 0%, rgb(144,197,238) 100%);
574
+ background-image: -ms-linear-gradient(bottom, rgb(114,167,207) 0%, rgb(144,197,238) 100%);
575
+ }
576
+ .wp-tabs, .wp-tabs .ui-tabs-nav, .wp-tabs .ui-state-default {
577
+ background: none;
578
+ border: 0px;
579
+ }
580
+
581
+ .wp-tabs .ui-tabs-nav {
582
+ padding: 0px;
583
+ }
584
+
585
+ .wp-tabs .ui-tabs-selected, .wp-tabs .ui-tabs-panel {
586
+ background-color: #fff;
587
+ border-color: #DFDFDF;
588
+ border-style: solid;
589
+ border-width: 1px;
590
+ }
591
+
css/eventorganiser-admin-style.css ADDED
@@ -0,0 +1,364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Venue map on event edit page */
2
+ #venuemap{height:300px; width:100%;float:right;margin: 10px 0 10px -300px}
3
+
4
+ /*Hide post date filter*/
5
+ .tablenav.top .alignleft.actions select[name="m"]{display: none; }
6
+
7
+ #eventorganiser_event_detail tr.eo-add-new-venue{
8
+ display: none;
9
+ }
10
+
11
+ #eventorganiser_event_detail tr .eo-label{
12
+ width:190px;
13
+ }
14
+ /* Venue page address form spacing */
15
+ #venue_address table tr{line-height: 4em;}
16
+
17
+ /* Venue map on venue page*/
18
+ #venue_address.postbox #venuemap{height: 300px;margin: 0 0 0 -60%;width: 60%;}
19
+
20
+ /* Venue address table in metabox*/
21
+ #eo_venue_form table.address {float:left;text-align:left;margin: 10px;}
22
+
23
+ /* Venue address metabox fix */
24
+ #eo_venue_form #venue_address .inside{padding:0;margin:0;}
25
+
26
+ /* Calendar View*/
27
+ .event.past-event{opacity:0.5;}
28
+ form.eo_cal .ui-autocomplete-input{width:220px;}
29
+ /* Table on event details calendar view*/
30
+ .eo-cal-meta .form-table th {width:50px;}
31
+ /*Drop down filters*/
32
+ #eo_admin_calendar select { min-width:180px; width:auto}
33
+
34
+ #eo-dialog-tabs, #events-meta {border: none;padding: 0}
35
+
36
+ /* Selectmenu
37
+ ----------------------------------*/
38
+ .ui-selectmenu { display: block; display: inline-block; position: relative; height: 2.2em; vertical-align: middle; text-decoration: none; overflow: hidden; zoom: 1; }
39
+ .ui-selectmenu-icon { position:absolute; right:6px; margin-top:-8px; top: 50%; }
40
+ .ui-selectmenu-menu { padding:0; margin:0; position:absolute; top: 0; display: none; z-index: 1005;} /* z-index: 1005 to make selectmenu work with dialog */
41
+ .ui-selectmenu-menu ul { padding:0; margin:0; list-style:none; position: relative; overflow: auto; overflow-y: auto ; overflow-x: hidden;}
42
+ .ui-selectmenu-open { display: block; }
43
+ .ui-selectmenu-menu-popup { margin-top: -1px; }
44
+ .ui-selectmenu-menu li { padding:0; margin:0; display: block; border-top: 1px dotted transparent; border-bottom: 1px dotted transparent; border-right-width: 0 !important; border-left-width: 0 !important; font-weight: normal !important; }
45
+ .ui-selectmenu-menu li a,.ui-selectmenu-status { line-height: 1.4em; display: block; padding: .405em 2.1em .405em 1em; outline:none; text-decoration:none; }
46
+ .ui-selectmenu-menu li.ui-state-disabled a, .ui-state-disabled { cursor: default; }
47
+ .ui-selectmenu-menu li.ui-selectmenu-hasIcon a,
48
+ .ui-selectmenu-hasIcon .ui-selectmenu-status { padding-left: 20px; position: relative; margin-left: 5px; }
49
+ .ui-selectmenu-menu li .ui-icon, .ui-selectmenu-status .ui-icon { position: absolute; top: 1em; margin-top: -8px; left: 0; }
50
+ .ui-selectmenu-status { line-height: 1.4em; }
51
+ .ui-selectmenu-menu li span,.ui-selectmenu-status span { display:block; margin-bottom: .2em; }
52
+ .ui-selectmenu-menu li .ui-selectmenu-item-header { font-weight: bold; }
53
+ .ui-selectmenu-menu li .ui-selectmenu-item-footer { opacity: .8; }
54
+ /* for optgroups */
55
+ .ui-selectmenu-menu .ui-selectmenu-group { font-size: 1em; }
56
+ .ui-selectmenu-menu .ui-selectmenu-group .ui-selectmenu-group-label { line-height: 1.4em; display:block; padding: .6em .5em 0; font-weight: bold; }
57
+ .ui-selectmenu-menu .ui-selectmenu-group ul { margin: 0; padding: 0; }
58
+ /* IE6 workaround (dotted transparent borders) */
59
+ * html .ui-selectmenu-menu li { border-color: pink; filter:chroma(color=pink); width:100%; }
60
+ * html .ui-selectmenu-menu li a { position: relative }
61
+ /* IE7 workaround (opacity disabled) */
62
+ *+html .ui-state-disabled, *+html .ui-state-disabled a { color: silver; }
63
+
64
+ /* Autocomplete / Combobox
65
+ ----------------------------------*/
66
+ .ui-autocomplete .ui-menu-item{border-top: 1px solid #cbc7bd;}
67
+ .ui-autocomplete .ui-menu-item:first-child {border-top: none;}
68
+ .ui-combobox {position: relative;display: inline-block;margin-right:30px;}
69
+ .ui-combobox-toggle {top: 0;bottom: 0;margin-left: -1px;padding: 0;/* adjust styles for IE 6/7 */*height: 1.7em;*top: 0.1em;}
70
+ .ui-combobox-input {margin: 0;padding: 0.3em;}
71
+ .eo-venue-combobox-buttons{position: absolute;width:4.5em;}
72
+
73
+ #eo_occurrence_datepicker .ui-datepicker .ui-state-default a,
74
+ #eo_occurrence_datepicker .ui-datepicker .ui-state-active a{color:#FFF;
75
+ background: #7eacbf; /* Old browsers */
76
+ background: -moz-linear-gradient(top, #7eacbf 0%, #21759b 100%); /* FF3.6+ */
77
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#7eacbf), color-stop(100%,#21759b)); /* Chrome,Safari4+ */
78
+ background: -webkit-linear-gradient(top, #7eacbf 0%,#21759b 100%); /* Chrome10+,Safari5.1+ */
79
+ background: -o-linear-gradient(top, #7eacbf 0%,#21759b 100%); /* Opera 11.10+ */
80
+ background: -ms-linear-gradient(top, #7eacbf 0%,#21759b 100%); /* IE10+ */
81
+ background: linear-gradient(to bottom, #7eacbf 0%,#21759b 100%); /* W3C */
82
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#7eacbf', endColorstr='#21759b',GradientType=0 ); /* IE6-9 */
83
+ }
84
+
85
+
86
+ /*
87
+ * Timepicker stylesheet
88
+ * Highly inspired from datepicker
89
+ * FG - Nov 2010 - Web3R
90
+ */
91
+ .ui-timepicker-inline{display:inline}
92
+ #ui-timepicker-div{background-color:#fff;padding:.2em}
93
+ .ui-timepicker-table{display:inline-table;width:0}
94
+ .ui-timepicker-table table{border-collapse:collapse;margin:.15em 0 0}
95
+ .ui-timepicker-hours,.ui-timepicker-minutes{padding:.2em}
96
+ .ui-timepicker-table .ui-timepicker-title{line-height:1.8em;text-align:center}
97
+ .ui-timepicker-table td span{display:block;width:1.2em;text-align:right;text-decoration:none;padding:.2em .3em .2em .5em}
98
+ .ui-timepicker-table td a{display:block;width:1.2em;cursor:pointer;text-align:right;text-decoration:none;padding:.2em .3em .2em .5em}
99
+ .ui-timepicker .ui-timepicker-buttonpane{background-image:none;border-left:0;border-right:0;border-bottom:0;margin:.7em 0 0;padding:0 .2em}
100
+ .ui-timepicker .ui-timepicker-buttonpane button{cursor:pointer;width:auto;overflow:visible;margin:.5em .2em .4em;padding:.2em .6em .3em}
101
+ .ui-timepicker .ui-timepicker-close{float:right}
102
+ .ui-timepicker-cover{display:block;position:absolute;z-index:-1;filter:mask();top:-4px;left:-4px;width:200px;height:200px}
103
+ .ui-timepicker-table td,.ui-timepicker-table th.periods{width:2.2em;padding:.1em}
104
+ .ui-timepicker .ui-timepicker-now,.ui-timepicker .ui-timepicker-deselect{float:left}
105
+
106
+ /* Debugger */
107
+ .eo-debug-alert{font-weight:bold;color:orange;}
108
+ .eo-debug-warning{font-weight:bold;color:red;}
109
+ .eo-debug-ok{font-weight:bold;color:green;}
110
+
111
+ /*! qTip2 - Pretty powerful tooltips - v2.0.0 - 2012-08-14
112
+ * http://craigsworks.com/projects/qtip2/
113
+ * Copyright (c) 2012 Craig Michael Thompson; Licensed MIT, GPL */
114
+ /* Core qTip styles */
115
+ .ui-tooltip, .qtip{position:absolute; left:-28000px; top:-28000px; display:none; max-width:280px; min-width:50px; font-size:10.5px; line-height:12px; border-width:1px; border-style:solid}
116
+
117
+ /*!
118
+ * qTip2 - Pretty powerful tooltips - v2.0.1-4-g
119
+ * http://qtip2.com
120
+ *
121
+ * Copyright (c) 2013 Craig Michael Thompson
122
+ * Released under the MIT, GPL licenses
123
+ * http://jquery.org/license
124
+ *
125
+ * Date: Fri Jan 4 2013 04:05 GMT+0000
126
+ * Plugins: svg ajax tips modal viewport imagemap ie6
127
+ * Styles: basic css3
128
+ */
129
+ a.eo-inline-help img {
130
+ width: 16px;
131
+ height: 16px;
132
+ display: inline-block;
133
+ text-decoration: none;
134
+ margin-left:3px;
135
+ margin-bottom:-3px;
136
+ }
137
+
138
+ /* Core qTip styles */
139
+ .qtip, .qtip{
140
+ position: absolute;
141
+ left: -28000px;
142
+ top: -28000px;
143
+ display: none;
144
+
145
+ max-width: 280px;
146
+ min-width: 50px;
147
+
148
+ font-size: 10.5px;
149
+ line-height: 12px;
150
+
151
+ direction: ltr;
152
+ }
153
+
154
+ .qtip-content{
155
+ position: relative;
156
+ padding: 5px 9px;
157
+ overflow: hidden;
158
+ font-size: 11px;
159
+ text-align: left;
160
+ word-wrap: break-word;
161
+ background-color: rgb(247, 247, 247);
162
+ }
163
+
164
+ .qtip-titlebar{
165
+ position: relative;
166
+ padding: 5px 35px 5px 10px;
167
+ overflow: hidden;
168
+
169
+ border-width: 0 0 1px;
170
+ font-weight: bold;
171
+ font-weight: bold;
172
+ font-size: 14px;
173
+ color: #21759B;
174
+ }
175
+
176
+ .qtip-titlebar + .qtip-content{ border-top-width: 0 !important; }
177
+
178
+ /* Default close button class */
179
+ .qtip-close{
180
+ position: absolute;
181
+ right: -9px; top: -9px;
182
+
183
+ cursor: pointer;
184
+ outline: medium none;
185
+
186
+ border-width: 1px;
187
+ border-style: solid;
188
+ border-color: transparent;
189
+ }
190
+
191
+ .qtip-titlebar .qtip-close{
192
+ right: 4px; top: 50%;
193
+ margin-top: -9px;
194
+ }
195
+
196
+ * html .qtip-titlebar .qtip-close{ top: 16px; } /* IE fix */
197
+
198
+ .qtip-titlebar .ui-icon,
199
+ .qtip-icon .ui-icon{
200
+ display: block;
201
+ text-indent: -1000em;
202
+ direction: ltr;
203
+ vertical-align: middle;
204
+ }
205
+
206
+ .qtip-icon, .qtip-icon .ui-icon{
207
+ -moz-border-radius: 3px;
208
+ -webkit-border-radius: 3px;
209
+ border-radius: 3px;
210
+ text-decoration: none;
211
+ }
212
+
213
+ .qtip-icon .ui-icon{
214
+ width: 18px;
215
+ height: 14px;
216
+
217
+ text-align: center;
218
+ text-indent: 0;
219
+ font: normal bold 10px/13px Tahoma,sans-serif;
220
+
221
+ color: inherit;
222
+ background: transparent none no-repeat -100em -100em;
223
+ }
224
+
225
+
226
+ /* Applied to 'focused' tooltips e.g. most recently displayed/interacted with */
227
+ .qtip-focus{}
228
+
229
+ /* Applied on hover of tooltips i.e. added/removed on mouseenter/mouseleave respectively */
230
+ .qtip-hover{}
231
+
232
+
233
+
234
+ /* Default tooltip style */
235
+ .qtip-default{
236
+ border-width: 1px;
237
+ border-style: solid;
238
+ border-color: #F1D031;
239
+
240
+ background-color: #FFFFA3;
241
+ color: #555;
242
+ }
243
+
244
+ .qtip-default .qtip-titlebar{
245
+ background-color: #FFEF93;
246
+ }
247
+
248
+ .qtip-default .qtip-icon{
249
+ border-color: #CCC;
250
+ background: #F1F1F1;
251
+ color: #777;
252
+ }
253
+
254
+ .qtip-default .qtip-titlebar .qtip-close{
255
+ border-color: #AAA;
256
+ color: #111;
257
+ }
258
+
259
+
260
+ /*! Light tooltip style */
261
+ .qtip-light{
262
+ background-color: white;
263
+ border-color: #E2E2E2;
264
+ color: #454545;
265
+ }
266
+
267
+ .qtip-light .qtip-titlebar{
268
+ background-color: rgb(247, 247, 247);
269
+ }
270
+
271
+
272
+ /*! Dark tooltip style */
273
+ .qtip-dark{
274
+ background-color: #505050;
275
+ border-color: #303030;
276
+ color: #f3f3f3;
277
+ }
278
+
279
+ .qtip-dark .qtip-titlebar{
280
+ background-color: #404040;
281
+ }
282
+
283
+ .qtip-dark .qtip-icon{
284
+ border-color: #444;
285
+ }
286
+
287
+ .qtip-dark .qtip-titlebar .ui-state-hover{
288
+ border-color: #303030;
289
+ }
290
+
291
+
292
+ /*! Cream tooltip style */
293
+ .qtip-cream{
294
+ background-color: #FBF7AA;
295
+ border-color: #F9E98E;
296
+ color: #A27D35;
297
+ }
298
+
299
+ .qtip-cream .qtip-titlebar{
300
+ background-color: #F0DE7D;
301
+ }
302
+
303
+ .qtip-cream .qtip-close .qtip-icon{
304
+ background-position: -82px 0;
305
+ }
306
+
307
+
308
+ /* Fluid class for determining actual width in IE */
309
+ .ui-tooltip-fluid{display:block; visibility:hidden; position:static !important; float:left !important}
310
+ .ui-tooltip-content{position:relative; padding:5px 9px; overflow:hidden; text-align:left; word-wrap:break-word}
311
+ .ui-tooltip-titlebar{position:relative; min-height:14px; padding:5px 35px 5px 10px; overflow:hidden; border-width:0 0 1px; font-weight:bold}
312
+ .ui-tooltip-titlebar+.ui-tooltip-content{border-top-width:0 !important}
313
+
314
+ /* Default close button class */
315
+ .ui-tooltip-titlebar .ui-state-default{position:absolute; right:4px; top:50%; margin-top:-9px; cursor:pointer; outline:medium none; border-width:1px; border-style:solid}
316
+ * html .ui-tooltip-titlebar .ui-state-default{top:16px}/* IE fix */
317
+ .ui-tooltip-titlebar .ui-icon,
318
+ .ui-tooltip-icon .ui-icon{display:block; text-indent:-1000em; direction:ltr}
319
+ .ui-tooltip-icon, .ui-tooltip-icon .ui-icon{-moz-border-radius:3px; -webkit-border-radius:3px; border-radius:3px; text-decoration:none}
320
+ .ui-tooltip-icon .ui-icon{width:18px; height:14px; text-align:center; text-indent:0; font:normal bold 10px/13px Tahoma,sans-serif; color:inherit; background:transparent none no-repeat -100em -100em}
321
+
322
+ /* Applied to 'focused' tooltips e.g. most recently displayed/interacted with */
323
+ .ui-tooltip-focus{}
324
+
325
+ /* Applied on hover of tooltips i.e. added/removed on mouseenter/mouseleave respectively */
326
+ .ui-tooltip-hover{}
327
+
328
+ /* Add shadows to your tooltips in:FF3+, Chrome 2+, Opera 10.6+, IE9+, Safari 2+*/
329
+ .ui-tooltip-shadow{-webkit-box-shadow:1px 1px 3px 1px rgba(0,0,0,0.15); -moz-box-shadow:1px 1px 3px 1px rgba(0,0,0,0.15); box-shadow:1px 1px 3px 1px rgba(0,0,0,0.15)}
330
+
331
+ /* Add rounded corners to your tooltips in:FF3+, Chrome 2+, Opera 10.6+, IE9+, Safari 2+*/
332
+ .ui-tooltip-rounded,
333
+ .ui-tooltip-tipsy,
334
+ .ui-tooltip-bootstrap{-moz-border-radius:5px; -webkit-border-radius:5px; border-radius:5px}
335
+
336
+ /* jQuery TOOLS Tooltip style */
337
+ .ui-tooltip-jtools{background:#232323; background:rgba(0,0,0,0.7); background-image:-webkit-gradient(linear,left top,left bottom,from(#717171),to(#232323)); background-image:-moz-linear-gradient(top,#717171,#232323); background-image:-webkit-linear-gradient(top,#717171,#232323); background-image:-ms-linear-gradient(top,#717171,#232323); background-image:-o-linear-gradient(top,#717171,#232323); border:2px solid #ddd; border:2px solid rgba(241,241,241,1); -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; -webkit-box-shadow:0 0 12px #333; -moz-box-shadow:0 0 12px #333; box-shadow:0 0 12px #333}
338
+
339
+ /* IE Specific */
340
+ .ui-tooltip-jtools .ui-tooltip-titlebar{background-color:transparent; filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A); -ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A)"}
341
+ .ui-tooltip-jtools .ui-tooltip-content{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323); -ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323)"}
342
+
343
+ .ui-tooltip-jtools .ui-tooltip-titlebar,
344
+ .ui-tooltip-jtools .ui-tooltip-content{background:transparent; color:white; border:0 dashed transparent}
345
+ .ui-tooltip-jtools .ui-tooltip-icon{border-color:#555}
346
+ .ui-tooltip-jtools .ui-tooltip-titlebar .ui-state-hover{border-color:#333}
347
+
348
+ /* Tipped style */
349
+ .ui-tooltip-tipped{border:3px solid #959FA9; -moz-border-radius:3px; -webkit-border-radius:3px; border-radius:3px; background-color:#F9F9F9; color:#454545; font-weight:normal; font-family:serif}
350
+ .ui-tooltip-tipped .ui-tooltip-titlebar{border-bottom-width:0; color:white; background:#3A79B8; background-image:-webkit-gradient(linear,left top,left bottom,from(#3A79B8),to(#2E629D)); background-image:-webkit-linear-gradient(top,#3A79B8,#2E629D); background-image:-moz-linear-gradient(top,#3A79B8,#2E629D); background-image:-ms-linear-gradient(top,#3A79B8,#2E629D); background-image:-o-linear-gradient(top,#3A79B8,#2E629D); filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D); -ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D)"}
351
+ .ui-tooltip-tipped .ui-tooltip-icon{border:2px solid #285589; background:#285589}
352
+ .ui-tooltip-tipped .ui-tooltip-icon .ui-icon{background-color:#FBFBFB; color:#555}
353
+
354
+ /* IE9 fix - removes all filters */
355
+ .ui-tooltip:not(.ie9haxors) div.ui-tooltip-content,
356
+ .ui-tooltip:not(.ie9haxors) div.ui-tooltip-titlebar{filter:none; -ms-filter:none}
357
+
358
+ /* Tips plugin */
359
+ .ui-tooltip .ui-tooltip-tip{margin:0 auto; overflow:hidden; z-index:10}
360
+ .ui-tooltip .ui-tooltip-tip,
361
+ .ui-tooltip .ui-tooltip-tip *{position:absolute; line-height:0.1px !important; font-size:0.1px !important; color:#123456; background:transparent; border:0 dashed transparent}
362
+ .ui-tooltip .ui-tooltip-tip canvas{top:0; left:0}
363
+
364
+
css/eventorganiser-front-end.css ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .fc-button .ui-button-text{line-height: 8px!important;}
2
+ .ui-button-text-only .ui-button-text {padding: .4em .8em;}
3
+ .ui-button { margin-left: -1px; }
4
+ .ui-button-icon-only .ui-button-text { padding: 0.14em; }
5
+ .ui-autocomplete-input { margin: 0; padding: 0.3em 0 0.3em 0.45em; }
6
+
7
+ /*Venue Map*/
8
+ .eo-venue-map{z-index:1000;}
9
+
10
+ /* Agenda CSS */
11
+ .eo-agenda-widget ul{list-style:none; margin:0}
12
+ .eo-agenda-widget .agenda-nav{overflow:hidden}
13
+ .eo-agenda-widget .next,
14
+ .eo-agenda-widget .prev{float:right; padding:5px 0px; background:#ececec; margin:3px}
15
+ .eo-agenda-widget ul.dates{border-bottom:1px solid #ececec; font-weight:bold}
16
+ .eo-agenda-widget ul.a-date{margin:0}
17
+ .eo-agenda-widget li.date{border-top:1px solid #ececec; padding:10px 0px}
18
+ .eo-agenda-widget li.event{padding:5px 0px 5px 10px; ; font-weight:normal; background:#ececec; border-radius:3px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; cursor:pointer; opacity:0.75; color:#333; margin:1px 0px; position:relative}
19
+ .eo-agenda-widget li.event:hover{opacity:1; background:#ececec}
20
+ .eo-agenda-widget li.event .cat{padding:10px 3px; background:none; margin-right:5px; height:100%; position:absolute; top:0; left:0}
21
+ .eo-agenda-widget li.event .meta{font-size:0.9em}
22
+ .eo-agenda-widget li.event a.eo-agenda-event-permalink{display:block;text-decoration:none;color:#333;}
23
+ .eo-agenda-widget li.event a.eo-agenda-event-permalink:hover{text-decoration:none;color:#333;}
24
+ /*
25
+ * jQuery UI CSS Framework 1.8.16
26
+ *
27
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
28
+ * Dual licensed under the MIT or GPL Version 2 licenses.
29
+ * http://jquery.org/license
30
+ * Includes: jquery.ui.core.css, jquery.ui.autocomplete.css, jquery.ui.menu.css, jquery.ui.button.css
31
+ * @see http://jqueryui.com/themeroller/?ffDefault=sans-serif&fwDefault=normal&fsDefault=12px&cornerRadius=4px&bgColorHeader=ececec&bgTextureHeader=04_highlight_hard.png&bgImgOpacityHeader=75&borderColorHeader=dfdfdf&fcHeader=333333&iconColorHeader=999999&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=dfdfdf&fcContent=333333&iconColorContent=333333&bgColorDefault=ececec&bgTextureDefault=04_highlight_hard.png&bgImgOpacityDefault=75&borderColorDefault=dfdfdf&fcDefault=333333&iconColorDefault=333333&bgColorHover=ececec&bgTextureHover=06_inset_hard.png&bgImgOpacityHover=75&borderColorHover=ccc&fcHover=000000&iconColorHover=333333&bgColorActive=ffffff&bgTextureActive=01_flat.png&bgImgOpacityActive=65&borderColorActive=dfdfdf&fcActive=333333&iconColorActive=333333&bgColorHighlight=ffffe0&bgTextureHighlight=01_flat.png&bgImgOpacityHighlight=55&borderColorHighlight=e6db55&fcHighlight=333333&iconColorHighlight=21759b&bgColorError=ffebe8&bgTextureError=01_flat.png&bgImgOpacityError=95&borderColorError=cc0000&fcError=cc0000&iconColorError=cc0000&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=3px
32
+ */
33
+
34
+ /* Layout helpers
35
+ ----------------------------------*/
36
+ .ui-helper-hidden { display: none; }
37
+ .ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
38
+ .ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
39
+ .ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
40
+ .ui-helper-clearfix { display: inline-block; }
41
+ /* required comment for clearfix to work in Opera \*/
42
+ * html .ui-helper-clearfix { height:1%; }
43
+ .ui-helper-clearfix { display:block; }
44
+ /* end clearfix */
45
+ .ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
46
+
47
+
48
+ /* Interaction Cues
49
+ ----------------------------------*/
50
+ .ui-state-disabled { cursor: default !important; }
51
+
52
+
53
+ /* Icons
54
+ ----------------------------------*/
55
+
56
+ /* states and images */
57
+ .ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
58
+
59
+
60
+ /* Misc visuals
61
+ ----------------------------------*/
62
+
63
+ /* Overlays */
64
+ .ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
65
+ .ui-autocomplete {position: absolute;top: 0;left: 0;cursor: default;}
66
+
67
+ /* workarounds */
68
+ * html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
69
+ .ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
70
+ .ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
71
+ .ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
72
+ .ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
73
+ .ui-datepicker .ui-datepicker-prev { left:2px; }
74
+ .ui-datepicker .ui-datepicker-next { right:2px; }
75
+ .ui-datepicker .ui-datepicker-prev-hover { left:1px; }
76
+ .ui-datepicker .ui-datepicker-next-hover { right:1px; }
77
+ .ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
78
+ .ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
79
+ .ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
80
+ .ui-datepicker select.ui-datepicker-month-year {width: 100%;}
81
+ .ui-datepicker select.ui-datepicker-month,
82
+ .ui-datepicker select.ui-datepicker-year { width: 49%;}
83
+ .ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
84
+ .ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
85
+ .ui-datepicker td { border: 0; padding: 1px; }
86
+ .ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
87
+ .ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
88
+ .ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
89
+ .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
90
+
91
+ /* with multiple calendars */
92
+ .ui-datepicker.ui-datepicker-multi { width:auto; }
93
+ .ui-datepicker-multi .ui-datepicker-group { float:left; }
94
+ .ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
95
+ .ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
96
+ .ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
97
+ .ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
98
+ .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
99
+ .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
100
+ .ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
101
+ .ui-datepicker-row-break { clear:both; width:100%; font-size:0em; }
102
+
103
+ /* RTL support */
104
+ .ui-datepicker-rtl { direction: rtl; }
105
+ .ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
106
+ .ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
107
+ .ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
108
+ .ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
109
+ .ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
110
+ .ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
111
+ .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
112
+ .ui-datepicker-rtl .ui-datepicker-group { float:right; }
113
+ .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
114
+ .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
115
+
116
+ /* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
117
+ .ui-datepicker-cover {
118
+ position: absolute; /*must have*/
119
+ z-index: -1; /*must have*/
120
+ filter: mask(); /*must have*/
121
+ top: -4px; /*must have*/
122
+ left: -4px; /*must have*/
123
+ width: 200px; /*must have*/
124
+ height: 200px; /*must have*/
125
+ }.ui-menu { list-style:none; padding: 2px; margin: 0; display:block; outline: none; }
126
+ .ui-menu .ui-menu { margin-top: -3px; position: absolute; }
127
+ .ui-menu .ui-menu-item { margin: 0; padding: 0; zoom: 1; width: 100%; }
128
+ .ui-menu .ui-menu-divider { margin: 5px -2px 5px -2px; height: 0; font-size: 0; line-height: 0; border-width: 1px 0 0 0; }
129
+ .ui-menu .ui-menu-item a { text-decoration: none; display: block; padding: 2px .4em; line-height: 1.5; zoom: 1; font-weight: normal; }
130
+ .ui-menu .ui-menu-item a.ui-state-focus,
131
+ .ui-menu .ui-menu-item a.ui-state-active { font-weight: normal; margin: -1px; }
132
+
133
+ .ui-menu .ui-state-disabled { font-weight: normal; margin: .4em 0 .2em; line-height: 1.5; }
134
+ .ui-menu .ui-state-disabled a { cursor: default; }
135
+
136
+ /* icon support */
137
+ .ui-menu-icons { position: relative; }
138
+ .ui-menu-icons .ui-menu-item a { position: relative; padding-left: 2em; }
139
+
140
+ /* left-aligned */
141
+ .ui-menu .ui-icon { position: absolute; top: .2em; left: .2em; }
142
+
143
+ /* right-aligned */
144
+ .ui-menu .ui-menu-icon { position: static; float: right; }
145
+
146
+ /* Component containers
147
+ ----------------------------------*/
148
+ .ui-widget { font-family: sans-serif; font-size: 12px; }
149
+ .ui-widget .ui-widget { font-size: 1em; }
150
+ .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: sans-serif; font-size: 1em; }
151
+ .ui-widget-content { border: 1px solid #dfdfdf; background: #ffffff; color: #333333; }
152
+ .ui-widget-header { border: 1px solid #dfdfdf; color: #333333; font-weight: bold; background-color: #f1f1f1; background-image: -ms-linear-gradient(top, #f9f9f9, #ececec); background-image: -moz-linear-gradient(top, #f9f9f9, #ececec); background-image: -o-linear-gradient(top, #f9f9f9, #ececec); background-image: -webkit-gradient(linear, left top, left bottom, from(#f9f9f9), to(#ececec)); background-image: -webkit-linear-gradient(top, #f9f9f9, #ececec); background-image: linear-gradient(top, #f9f9f9, #ececec); }
153
+ .ui-widget-header a { color: #333333; }
154
+
155
+ /* Interaction states
156
+ ----------------------------------*/
157
+ .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #dfdfdf; background-color: #f1f1f1; background-image: -ms-linear-gradient(top, #f9f9f9, #ececec); background-image: -moz-linear-gradient(top, #f9f9f9, #ececec); background-image: -o-linear-gradient(top, #f9f9f9, #ececec); background-image: -webkit-gradient(linear, left top, left bottom, from(#f9f9f9), to(#ececec)); background-image: -webkit-linear-gradient(top, #f9f9f9, #ececec); background-image: linear-gradient(top, #f9f9f9, #ececec); font-weight: normal; color: #333333; }
158
+ .ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #21759b; text-decoration: none; }
159
+ .ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #ccc; background-color: #ececec; background-image: -ms-linear-gradient(top, #ececec, #f9f9f9); background-image: -moz-linear-gradient(top, #ececec, #f9f9f9); background-image: -o-linear-gradient(top, #ececec, #f9f9f9); background-image: -webkit-gradient(linear, left top, left bottom, from(#ececec), to(#f9f9f9)); background-image: -webkit-linear-gradient(top, #ececec, #f9f9f9); background-image: linear-gradient(top, #ececec, #f9f9f9); font-weight: normal; color: #000000; }
160
+ .ui-state-hover a, .ui-state-hover a:hover { color: #d54e21; text-decoration: none; }
161
+ .ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #dfdfdf; background: #ffffff; font-weight: normal; color: #333333; }
162
+ .ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #333333; text-decoration: none; }
163
+ .ui-widget :active { outline: none; }
164
+
165
+ /* Interaction Cues
166
+ ----------------------------------*/
167
+ .ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #e6db55; background: #ffffe0; color: #333333; }
168
+ .ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #333333; }
169
+ .ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cc0000; background: #ffebe8; color: #cc0000; }
170
+ .ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cc0000; }
171
+ .ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cc0000; }
172
+ .ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
173
+ .ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
174
+ .ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
175
+
176
+ /* Icons
177
+ ----------------------------------*/
178
+
179
+ /* states and images */
180
+ .ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_21759b_256x240.png); }
181
+ .ui-widget-content .ui-icon {background-image: url(images/ui-icons_21759b_256x240.png); }
182
+ .ui-widget-header .ui-icon {background-image: url(images/ui-icons_21759b_256x240.png); }
183
+ .ui-state-default .ui-icon { background-image: url(images/ui-icons_21759b_256x240.png); }
184
+ .ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_d54e21_256x240.png); }
185
+ .ui-state-active .ui-icon {background-image: url(images/ui-icons_333333_256x240.png); }
186
+ .ui-state-highlight .ui-icon {background-image: url(images/ui-icons_21759b_256x240.png); }
187
+ .ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cc0000_256x240.png); }
188
+
189
+ /* positioning */
190
+ .ui-icon-carat-1-e { background-position: -32px 0; }
191
+ .ui-icon-carat-1-w { background-position: -96px 0; }
192
+ .ui-icon-carat-2-e-w { background-position: -144px 0; }
193
+ .ui-icon-triangle-1-e { background-position: -32px -16px; }
194
+ .ui-icon-triangle-1-s { background-position: -64px -16px; }
195
+ .ui-icon-triangle-1-w { background-position: -96px -16px; }
196
+ .ui-icon-arrow-1-n { background-position: 0 -32px; }
197
+ .ui-icon-arrow-1-e { background-position: -32px -32px; }
198
+ .ui-icon-arrow-1-s { background-position: -64px -32px; }
199
+ .ui-icon-arrow-1-w { background-position: -96px -32px; }
200
+ .ui-icon-circle-triangle-e { background-position: -48px -192px; }
201
+ .ui-icon-circle-triangle-s { background-position: -64px -192px; }
202
+ .ui-icon-circle-triangle-w { background-position: -80px -192px; }
203
+ .ui-icon-circle-triangle-n { background-position: -96px -192px; }
204
+
205
+ /* Misc visuals
206
+ ----------------------------------*/
207
+
208
+ /* Corner radius */
209
+ .ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 3px; -webkit-border-top-left-radius: 3px; border-top-left-radius: 3px; }
210
+ .ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 3px; -webkit-border-top-right-radius: 3px; border-top-right-radius: 3px; }
211
+ .ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 3px; -webkit-border-bottom-left-radius: 3px; border-bottom-left-radius: 3px; }
212
+ .ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 3px; -webkit-border-bottom-right-radius: 3px; border-bottom-right-radius: 3px; }
213
+
214
+ /*
215
+ * jQuery UI Button 1.8.16
216
+ *
217
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
218
+ * Dual licensed under the MIT or GPL Version 2 licenses.
219
+ * http://jquery.org/license
220
+ *
221
+ * http://docs.jquery.com/UI/Button#theming
222
+ */
223
+ .ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
224
+ .ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
225
+ button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
226
+ .ui-button-icons-only { width: 3.4em; }
227
+ button.ui-button-icons-only { width: 3.7em; }
228
+
229
+ /*button text element */
230
+ .ui-button .ui-button-text { display: block; line-height: 1.4; }
231
+ .ui-button-text-only .ui-button-text { padding: .4em 1em; }
232
+ .ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
233
+ .ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
234
+ .ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }
235
+ .ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
236
+ /* no icon support for input elements, provide padding by default */
237
+ input.ui-button { padding: .4em 1em; }
238
+
239
+ /*button icon element(s) */
240
+ .ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
241
+ .ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
242
+ .ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
243
+ .ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
244
+ .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
245
+
246
+ /*button sets*/
247
+ .ui-buttonset { margin-right: 7px; }
248
+ .ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
249
+
250
+ /* workarounds */
251
+ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
css/fullcalendar.css ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*Front-end full calendar key*/
2
+ .eo-fullcalendar-key ul.eo_fullcalendar_key{display: inline-block;font-size: 0.8em;}
3
+ .eo-fullcalendar-key ul.eo_fullcalendar_key li{display: inline-block;margin:0 3px;}
4
+ .eo-fullcalendar-key span.eo_fullcalendar_key_colour{margin: 4px 2px 4px 5px;border: 1px solid #DFDFDF;border-radius: 4px 4px 4px 4px;font-size: 0.9em;vertical-align: top;padding: 0 6px;}
5
+
6
+
7
+ /*styling for calendar view tabs */
8
+ .view-button{float:right;background: #FFFFFF;height: 30px;line-height: 28px;margin-top: 3px;padding: 0 11px;}
9
+ .view-button.active{border-color: #dfdfdf #dfdfdf #FFFFFF;border-style: solid;border-width: 1px;font-size: 120%;}
10
+
11
+ /*calendar view conainer */
12
+ div#calendar-view{clear: both;height: 34px;margin-bottom: 15px;border-bottom: 1px solid #dfdfdf;padding-right: 100px;position: relative;}
13
+
14
+ /* loading text */
15
+ #loading{position:absolute;bottom:0;font-size: 16px;line-height: 16px;margin-bottom: 5px;}
16
+ #loading:before {content: url("images/loading-image.gif");padding-right: 5px;}
17
+
18
+ /*Added to fit with full calendar*/
19
+ /*button.ui-datepicker-trigger.ui-button.ui-widget{vertical-align: top;}*/
20
+ .ui-datepicker{z-index: 100 !important}
21
+ .ui-selectmenu-menu{z-index: 10!important}
22
+
23
+ /*For the blocks of colour*/
24
+ .cat .ui-selectmenu-item-icon { height: 14px; width: 14px; border:1px solid #dfdfdf;border-radius: 4px;}
25
+ .cat .ui-selectmenu-item-icon{ background-image:none!important; }
26
+
27
+ /*
28
+ * FullCalendar v1.5.2 Stylesheet
29
+ *
30
+ * Copyright (c) 2011 Adam Shaw
31
+ * Dual licensed under the MIT and GPL licenses, located in
32
+ * MIT-LICENSE.txt and GPL-LICENSE.txt respectively.
33
+ *
34
+ * Date: Sun Aug 21 22:06:09 2011 -0700
35
+ */
36
+ .fc{direction:ltr;text-align:left}
37
+ .fc table{border-collapse:collapse;border-spacing:0}
38
+ html .fc,.fc table{font-size:1em}
39
+ .fc td,.fc th{vertical-align:top;padding:0}
40
+ .fc-header td{white-space:nowrap;vertical-align:middle}
41
+ .fc-header-left{width:25%;text-align:left}
42
+ .fc-header-right{width:25%;text-align:right}
43
+ .fc-header-title{display:inline-block;vertical-align:top}
44
+ .fc-header-title h2{margin-top:0;white-space:nowrap}
45
+ .fc .fc-header-space{padding-left:10px}
46
+ .fc-header .ui-corner-right{margin-right:0}
47
+ .fc-header .fc-state-hover,.fc-header .ui-state-hover{z-index:2}
48
+ .fc-header .fc-state-down{z-index:3}
49
+ .fc-header .fc-state-active,.fc-header .ui-state-active{z-index:4}
50
+ .fc-content{clear:both}
51
+ .fc-view{width:100%;overflow:hidden}
52
+ .fc-widget-header,/* <th>,usually */
53
+ .fc-widget-content{border:1px solid #ccc}
54
+ .fc-state-highlight{background:#ffc}
55
+ .fc-cell-overlay{background:#9cf;opacity:.2;filter:alpha(opacity=20)}
56
+ .fc-button{position:relative;display:inline-block;cursor:pointer}
57
+ .fc-state-default{border-style:solid;border-width:1px 0}
58
+ .fc-button-inner{position:relative;float:left;overflow:hidden}
59
+ .fc-state-default .fc-button-inner{border-style:solid;border-width:0 1px}
60
+ /*.fc-button-content{position:relative;float:left;height:1.9em;line-height:1.9em;white-space:nowrap;padding:0 .6em}*/
61
+ .fc-button-content{position: relative;float: left;height: 20px;white-space: nowrap;padding: 0 .6em;line-height: 20px;}
62
+ /*.fc-button-content .fc-icon-wrap{position:relative;float:left;top:50%}*/
63
+ .fc-button-content .fc-icon-wrap {position: relative;float: left;top: 2px;}
64
+ .fc-button-content .ui-icon{position:relative;float:left;margin-top:0;top:-50%}
65
+ .fc-state-default .fc-button-effect{position:absolute;top:50%;left:0}
66
+ .fc-state-default .fc-button-effect span{position:absolute;top:-100px;left:0;width:500px;height:100px;background:#444;opacity:.09;filter:alpha(opacity=9);border-color:#fff;border-style:solid;border-width:100px 0 0 1px}
67
+ .fc-state-default,.fc-state-default .fc-button-inner{background:#F3F3F3;color:#000;border-color:#ccc #bbb #aaa;border-style:solid}
68
+ .fc-state-hover,.fc-state-hover .fc-button-inner{border-color:#999}
69
+ .fc-state-down,.fc-state-down .fc-button-inner{background:#777;border-color:#555}
70
+ .fc-state-active,.fc-state-active .fc-button-inner{background:#777;color:#fff;border-color:#555}
71
+ .fc-state-disabled,.fc-state-disabled .fc-button-inner{color:#999;border-color:#ddd}
72
+ .fc-state-disabled{cursor:default}
73
+ .fc-state-disabled .fc-button-effect{display:none}
74
+ .fc-event{font-size:.85em;cursor:default;border-style:solid;border-width:0}
75
+ a.fc-event,.fc-event-draggable{cursor:pointer}
76
+ a.fc-event{text-decoration:none}
77
+ .fc-rtl .fc-event{text-align:right}
78
+ .fc-event-skin{background-color:#36c;color:#fff;border-color:#36c}
79
+ .fc-event-inner{position:relative;width:100%;height:100%;overflow:hidden;border-style:solid;border-width:0}
80
+ .fc-event-time,.fc-event-title{padding:0 1px}
81
+ .fc .ui-resizable-handle{display:block;position:absolute;z-index:99999;overflow:hidden;font-size:300%;line-height:50%}
82
+ .fc-event-hori{margin-bottom:1px;border-width:1px 0}
83
+ .fc-event-hori .ui-resizable-e{top:0!important;right:-3px!important;width:7px!important;height:100%!important;cursor:e-resize}
84
+ .fc-event-hori .ui-resizable-w{top:0!important;left:-3px!important;width:7px!important;height:100%!important;cursor:w-resize}
85
+ .fc-event-hori .ui-resizable-handle{_padding-bottom:14px}
86
+ .fc-corner-left{margin-left:1px}
87
+ .fc-corner-left .fc-button-inner,.fc-corner-left .fc-event-inner{margin-left:-1px}
88
+ .fc-corner-top{margin-top:1px}
89
+ .fc-corner-top .fc-event-inner{margin-top:-1px;border-top-width:1px}
90
+ .fc-corner-bottom{margin-bottom:1px}
91
+ .fc-corner-bottom .fc-event-inner{margin-bottom:-1px;border-bottom-width:1px}
92
+ .fc-corner-left .fc-event-inner{border-left-width:1px}
93
+ .fc-border-separate th,.fc-border-separate td{border-width:1px 0 0 1px}
94
+ .fc-border-separate tr.fc-last th,.fc-border-separate tr.fc-last td{border-bottom-width:1px}
95
+ .fc-grid .fc-day-number{float:right;padding:0 2px}
96
+ .fc-grid .fc-other-month .fc-day-number{opacity:0.3;filter:alpha(opacity=30)}
97
+ .fc-grid .fc-day-content{clear:both;padding:2px 2px 1px}
98
+ .fc-grid .fc-event-time{font-weight:700}
99
+ .fc-rtl .fc-grid .fc-day-number{float:left}
100
+ .fc-rtl .fc-grid .fc-event-time{float:right}
101
+ .fc-agenda .fc-agenda-axis{width:50px;vertical-align:middle;text-align:right;white-space:nowrap;font-weight:400;padding:0 4px}
102
+ .fc-agenda .fc-day-content{padding:2px 2px 1px}
103
+ .fc-agenda-days .fc-col0{border-left-width:0}
104
+ .fc-agenda-allday .fc-day-content{min-height:34px;_height:34px}
105
+ .fc-agenda-divider-inner{height:2px;overflow:hidden}
106
+ .fc-widget-header .fc-agenda-divider-inner{background:#eee}
107
+ .fc-agenda-slots th{border-width:1px 1px 0}
108
+ .fc-agenda-slots td{background:none;border-width:1px 0 0}
109
+ .fc-agenda-slots td div{height:20px}
110
+ .fc-agenda-slots tr.fc-minor th,.fc-agenda-slots tr.fc-minor td{border-top-style:dotted}
111
+ .fc-agenda-slots tr.fc-minor th.ui-widget-header{border-top-style:solid}
112
+ .fc-event-vert .fc-event-head,.fc-event-vert .fc-event-content{position:relative;z-index:2;width:100%;overflow:hidden}
113
+ .fc-event-vert .fc-event-time{white-space:nowrap;font-size:10px}
114
+ .fc-event-vert .fc-event-bg{position:absolute;z-index:1;top:0;left:0;width:100%;height:100%;background:#fff;opacity:.3;filter:alpha(opacity=30)}
115
+ .fc .ui-draggable-dragging .fc-event-bg,/* TODO: something nicer like .fc-opacity */
116
+ .fc-select-helper .fc-event-bg{display:none\9}
117
+ .fc-event-vert .ui-resizable-s{bottom:0!important;width:100%!important;height:8px!important;overflow:hidden!important;line-height:8px!important;font-size:11px!important;font-family:monospace;text-align:center;cursor:s-resize}
118
+ .fc-agenda .ui-resizable-resizing{_overflow:hidden}
119
+ .fc-header-center,.fc-grid th,.fc-agenda-days th{text-align:center}
120
+ .fc-header .fc-button,.fc-corner-right .fc-button-inner,.fc-corner-right .fc-event-inner{margin-right:-1px}
121
+ .fc-header .fc-corner-right,.fc-corner-right{margin-right:1px}
122
+ .fc-corner-right .fc-event-inner,.fc-border-separate th.fc-last,.fc-border-separate td.fc-last,.fc-agenda-days .fc-agenda-axis{border-right-width:1px}
123
+ table.fc-border-separate,.fc-agenda table{border-collapse:separate}
124
+ .fc-border-separate tbody tr.fc-first td,.fc-border-separate tbody tr.fc-first th,.fc-agenda-slots tr.fc-slot0 th,.fc-agenda-slots tr.fc-slot0 td{border-top-width:0}
125
+ .fc-agenda-allday th,.fc-event-vert{border-width:0 1px}
126
+
127
+
128
+ /*! qTip2 - Pretty powerful tooltips - v2.0.0 - 2012-08-14
129
+ * http://craigsworks.com/projects/qtip2/
130
+ * Copyright (c) 2012 Craig Michael Thompson; Licensed MIT, GPL */
131
+ /* Core qTip styles */
132
+ .ui-tooltip, .qtip{position:absolute; left:-28000px; top:-28000px; display:none; max-width:280px; min-width:50px; font-size:10.5px; line-height:12px; border-width:1px; border-style:solid}
133
+
134
+ /* Fluid class for determining actual width in IE */
135
+ .ui-tooltip-fluid{display:block; visibility:hidden; position:static !important; float:left !important}
136
+ .ui-tooltip-content{position:relative; padding:5px 9px; overflow:hidden; text-align:left; word-wrap:break-word}
137
+ .ui-tooltip-titlebar{position:relative; min-height:14px; padding:5px 35px 5px 10px; overflow:hidden; border-width:0 0 1px; font-weight:bold}
138
+ .ui-tooltip-titlebar+.ui-tooltip-content{border-top-width:0 !important}
139
+
140
+ /* Default close button class */
141
+ .ui-tooltip-titlebar .ui-state-default{position:absolute; right:4px; top:50%; margin-top:-9px; cursor:pointer; outline:medium none; border-width:1px; border-style:solid}
142
+ * html .ui-tooltip-titlebar .ui-state-default{top:16px}/* IE fix */
143
+ .ui-tooltip-titlebar .ui-icon,
144
+ .ui-tooltip-icon .ui-icon{display:block; text-indent:-1000em; direction:ltr}
145
+ .ui-tooltip-icon, .ui-tooltip-icon .ui-icon{-moz-border-radius:3px; -webkit-border-radius:3px; border-radius:3px; text-decoration:none}
146
+ .ui-tooltip-icon .ui-icon{width:18px; height:14px; text-align:center; text-indent:0; font:normal bold 10px/13px Tahoma,sans-serif; color:inherit; background:transparent none no-repeat -100em -100em}
147
+
148
+ /* Applied to 'focused' tooltips e.g. most recently displayed/interacted with */
149
+ .ui-tooltip-focus{}
150
+
151
+ /* Applied on hover of tooltips i.e. added/removed on mouseenter/mouseleave respectively */
152
+ .ui-tooltip-hover{}
153
+
154
+ /* Add shadows to your tooltips in:FF3+, Chrome 2+, Opera 10.6+, IE9+, Safari 2+*/
155
+ .ui-tooltip-shadow{-webkit-box-shadow:1px 1px 3px 1px rgba(0,0,0,0.15); -moz-box-shadow:1px 1px 3px 1px rgba(0,0,0,0.15); box-shadow:1px 1px 3px 1px rgba(0,0,0,0.15)}
156
+
157
+ /* Add rounded corners to your tooltips in:FF3+, Chrome 2+, Opera 10.6+, IE9+, Safari 2+*/
158
+ .ui-tooltip-rounded,
159
+ .ui-tooltip-tipsy,
160
+ .ui-tooltip-bootstrap{-moz-border-radius:5px; -webkit-border-radius:5px; border-radius:5px}
161
+
162
+ /* jQuery TOOLS Tooltip style */
163
+ .ui-tooltip-jtools{background:#232323; background:rgba(0,0,0,0.7); background-image:-webkit-gradient(linear,left top,left bottom,from(#717171),to(#232323)); background-image:-moz-linear-gradient(top,#717171,#232323); background-image:-webkit-linear-gradient(top,#717171,#232323); background-image:-ms-linear-gradient(top,#717171,#232323); background-image:-o-linear-gradient(top,#717171,#232323); border:2px solid #ddd; border:2px solid rgba(241,241,241,1); -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; -webkit-box-shadow:0 0 12px #333; -moz-box-shadow:0 0 12px #333; box-shadow:0 0 12px #333}
164
+
165
+ /* IE Specific */
166
+ .ui-tooltip-jtools .ui-tooltip-titlebar{background-color:transparent; filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A); -ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A)"}
167
+ .ui-tooltip-jtools .ui-tooltip-content{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323); -ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323)"}
168
+
169
+ .ui-tooltip-jtools .ui-tooltip-titlebar,
170
+ .ui-tooltip-jtools .ui-tooltip-content{background:transparent; color:white; border:0 dashed transparent}
171
+ .ui-tooltip-jtools .ui-tooltip-icon{border-color:#555}
172
+ .ui-tooltip-jtools .ui-tooltip-titlebar .ui-state-hover{border-color:#333}
173
+
174
+ /* Tipped style */
175
+ .ui-tooltip-tipped{border:3px solid #959FA9; -moz-border-radius:3px; -webkit-border-radius:3px; border-radius:3px; background-color:#F9F9F9; color:#454545; font-weight:normal; font-family:serif}
176
+ .ui-tooltip-tipped .ui-tooltip-titlebar{border-bottom-width:0; color:white; background:#3A79B8; background-image:-webkit-gradient(linear,left top,left bottom,from(#3A79B8),to(#2E629D)); background-image:-webkit-linear-gradient(top,#3A79B8,#2E629D); background-image:-moz-linear-gradient(top,#3A79B8,#2E629D); background-image:-ms-linear-gradient(top,#3A79B8,#2E629D); background-image:-o-linear-gradient(top,#3A79B8,#2E629D); filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D); -ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D)"}
177
+ .ui-tooltip-tipped .ui-tooltip-icon{border:2px solid #285589; background:#285589}
178
+ .ui-tooltip-tipped .ui-tooltip-icon .ui-icon{background-color:#FBFBFB; color:#555}
179
+
180
+ /* IE9 fix - removes all filters */
181
+ .ui-tooltip:not(.ie9haxors) div.ui-tooltip-content,
182
+ .ui-tooltip:not(.ie9haxors) div.ui-tooltip-titlebar{filter:none; -ms-filter:none}
183
+
184
+ /* Tips plugin */
185
+ .ui-tooltip .ui-tooltip-tip{margin:0 auto; overflow:hidden; z-index:10}
186
+ .ui-tooltip .ui-tooltip-tip,
187
+ .ui-tooltip .ui-tooltip-tip *{position:absolute; line-height:0.1px !important; font-size:0.1px !important; color:#123456; background:transparent; border:0 dashed transparent}
188
+ .ui-tooltip .ui-tooltip-tip canvas{top:0; left:0}
189
+
190
+
191
+ /* =Print
192
+ ----------------------------------------------- */
193
+ @media print {
194
+ /*.fc-event-skin {background: none !important;color: #000 !important;}*/
195
+
196
+ /* horizontal events */
197
+ .fc-event-hori {padding: 1px 0 0 0 !important;}
198
+ .fc-event-hori .fc-event-inner {border-width: 0 !important;padding: 0 1px !important}
199
+
200
+ /* vertical events */
201
+ .fc-event-vert {padding: 0 1px 0 0 !important;}
202
+ .fc-event-vert .fc-event-inner {border-width: 0 !important;padding: 1px 0 !important;}
203
+
204
+ /*.fc-event-bg {display: none !important;}*/
205
+ .fc-event .ui-resizable-handle {display: none !important;}
206
+ .fc-button{display: none;}
207
+ .eo-fullcalendar table tr td{width:10%;}
208
+ }
css/images/eo-pro-event-search.png ADDED
Binary file
css/images/eo-pro-ticket-picker.png ADDED
Binary file
css/images/eo-pro-venue-cf.png ADDED
Binary file
css/images/eobadge.png ADDED
Binary file
css/images/eoicon-16.png ADDED
Binary file
css/images/eoicon-32.png ADDED
Binary file
css/images/eoicon-32.xcf ADDED
Binary file
css/images/eoicon-64.png ADDED
Binary file
css/images/help-14.png ADDED
Binary file
css/images/info-14.png ADDED
Binary file
css/images/loading-image.gif ADDED
Binary file
css/images/ui-bg_flat_100_f5f5f5_40x100.png ADDED
Binary file
css/images/ui-bg_flat_100_ffffff_40x100.png ADDED
Binary file
css/images/ui-bg_flat_10_ffebe8_40x100.png ADDED
Binary file
css/images/ui-bg_flat_10_ffffff_40x100.png ADDED
Binary file
css/images/ui-bg_flat_15_f9f9f9_40x100.png ADDED
Binary file
css/images/ui-bg_flat_15_ffffe0_40x100.png ADDED
Binary file
css/images/ui-bg_flat_20_000000_40x100.png ADDED
Binary file
css/images/ui-bg_flat_70_000000_40x100.png ADDED
Binary file
css/images/ui-bg_highlight-soft_100_ececec_1x100.png ADDED
Binary file
css/images/ui-icons_21759b_256x240.png ADDED
Binary file
css/images/ui-icons_222222_256x240.png ADDED
Binary file
css/images/ui-icons_333333_256x240.png ADDED
Binary file
css/images/ui-icons_3572ac_256x240.png ADDED
Binary file
css/images/ui-icons_999999_256x240.png ADDED
Binary file
css/images/ui-icons_cc0000_256x240.png ADDED
Binary file
css/images/ui-icons_d54e21_256x240.png ADDED
Binary file
event-organiser-calendar.php ADDED
@@ -0,0 +1,306 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Calendar Admin Page
4
+ */
5
+ if ( !class_exists( 'EventOrganiser_Admin_Page' ) ){
6
+ require_once( EVENT_ORGANISER_DIR.'classes/class-eventorganiser-admin-page.php' );
7
+ }
8
+ /**
9
+ * Calendar Admin Page
10
+ *
11
+ * Extends the EentOrganiser_Admin_Page class. Creates the calendar admin page
12
+ * @version 1.0
13
+ * @see EventOrganiser_Admin_Page
14
+ * @package event organiser
15
+ */
16
+ class EventOrganiser_Calendar_Page extends EventOrganiser_Admin_Page
17
+ {
18
+ /**
19
+ * This sets the calendar page variables
20
+ */
21
+ function set_constants(){
22
+ $this->hook = 'edit.php?post_type=event';
23
+ $this->title = __( 'Calendar View', 'eventorganiser' );
24
+ $this->menu = __( 'Calendar View', 'eventorganiser' );
25
+ $this->permissions = 'edit_events';
26
+ $this->slug = 'calendar';
27
+ }
28
+
29
+ /**
30
+ * Enqueues the page's scripts and styles, and localises them.
31
+ */
32
+ function page_scripts(){
33
+ global $wp_locale;
34
+
35
+ wp_enqueue_script( 'eo_calendar' );
36
+ wp_enqueue_script( 'eo_event' );
37
+ wp_localize_script( 'eo_event', 'EO_Ajax_Event', array(
38
+ 'ajaxurl' => admin_url( 'admin-ajax.php' ),
39
+ 'startday' => intval( get_option( 'start_of_week' ) ),
40
+ 'format' => eventorganiser_php2jquerydate( eventorganiser_get_option('dateformat') ),
41
+ ));
42
+ wp_localize_script( 'eo_calendar', 'EO_Ajax', array(
43
+ 'ajaxurl' => admin_url( 'admin-ajax.php' ),
44
+ 'startday' => intval( get_option( 'start_of_week' ) ),
45
+ 'format' => eventorganiser_php2jquerydate( eventorganiser_get_option('dateformat') ),
46
+ 'timeFormat' => ( get_current_screen()->get_option( 'eofc_time_format', 'value' ) ? 'h:mmtt' : 'HH:mm' ),
47
+ 'perm_edit' => current_user_can( 'edit_events' ),
48
+ 'categories' => get_terms( 'event-category', array( 'hide_empty' => 0 ) ),
49
+ 'venues' => get_terms( 'event-venue', array( 'hide_empty' => 0 ) ),
50
+ 'locale' => array(
51
+ 'monthNames' => array_values( $wp_locale->month ),
52
+ 'monthAbbrev' => array_values( $wp_locale->month_abbrev ),
53
+ 'dayNames' => array_values( $wp_locale->weekday ),
54
+ 'dayAbbrev' => array_values( $wp_locale->weekday_abbrev ),
55
+ 'today' => __( 'today', 'eventorganiser' ),
56
+ 'day' => __( 'day', 'eventorganiser' ),
57
+ 'week' => __( 'week', 'eventorganiser' ),
58
+ 'month' => __( 'month', 'eventorganiser' ),
59
+ 'gotodate' => __( 'go to date', 'eventorganiser' ),
60
+ 'cat' => __( 'View all categories', 'eventorganiser' ),
61
+ 'venue' => __( 'View all venues', 'eventorganiser' ),
62
+ )
63
+ ));
64
+ wp_enqueue_style( 'eo_calendar-style' );
65
+ wp_enqueue_style( 'eventorganiser-style' );
66
+ }
67
+
68
+ /**
69
+ * Prints page styles
70
+ */
71
+ function page_styles(){
72
+ if ( $terms = get_terms( 'event-category', array( 'hide_empty' => 0 ) ) ):
73
+ $css = '';
74
+ foreach ( $terms as $term ):
75
+ $css .= ".cat-slug-{$term->slug} span.ui-selectmenu-item-icon{ background: ".eo_get_category_color( $term ).";}\n";
76
+ endforeach;
77
+ wp_add_inline_style( 'eo_calendar-style', $css );
78
+ endif;
79
+ }
80
+
81
+ function page_actions(){
82
+
83
+ //Add screen option
84
+ $user = wp_get_current_user();
85
+ $is12hour = get_user_meta( $user->ID, 'eofc_time_format', true );
86
+ add_screen_option( 'eofc_time_format', array( 'value' => $is12hour ) );
87
+ add_filter( 'screen_settings', array( $this, 'screen_options' ), 10, 2 );
88
+
89
+ //Check action
90
+ if ( !empty( $_REQUEST['save'] ) || !empty( $_REQUEST['publish'] ) ){
91
+ //Check nonce
92
+ check_admin_referer( 'eventorganiser_calendar_save' );
93
+
94
+ //authentication checks
95
+ if ( !current_user_can( 'edit_events' ) )
96
+ wp_die( __( 'You do not have sufficient permissions to create events', 'eventorganiser' ) );
97
+
98
+ $input = $_REQUEST['eo_event']; //Retrieve input from posted data
99
+
100
+ //Set the status of the new event
101
+ if ( !empty( $_REQUEST['save'] ) ):
102
+ $status = 'draft';
103
+ else:
104
+ $status = ( current_user_can( 'publish_events' ) ? 'publish' : 'pending' );
105
+ endif;
106
+
107
+ //Set post and event details
108
+ $venue = (int) $input['venue_id'];
109
+
110
+ $post_input = array(
111
+ 'post_title' => $input['event_title'],
112
+ 'post_status' => $status,
113
+ 'post_content' => $input['event_content'],
114
+ 'post_type' => 'event',
115
+ 'tax_input' => array( 'event-venue' => array( $venue ) ),
116
+ );
117
+ $tz = eo_get_blog_timezone();
118
+ $event_data = array(
119
+ 'schedule' => 'once',
120
+ 'all_day' => $input['allday'],
121
+ 'start' => new DateTime( $input['StartDate'].' '.$input['StartTime'], $tz ),
122
+ 'end' => new DateTime( $input['EndDate'].' '.$input['FinishTime'], $tz ),
123
+ );
124
+
125
+ //Insert event
126
+ $post_id = eo_insert_event( $post_input, $event_data );
127
+
128
+ if ( $post_id ){
129
+ //If event was successfully inserted, redirect and display appropriate message
130
+ $redirect = get_edit_post_link( $post_id, '' );
131
+
132
+ if( $status == 'publish' )
133
+ $redirect = add_query_arg( 'message', 6, $redirect );
134
+ else
135
+ $redirect = add_query_arg( 'message', 7, $redirect );
136
+
137
+ //Redirect to event admin page & exit
138
+ wp_redirect( $redirect );
139
+ exit;
140
+ }
141
+ }elseif ( isset( $_REQUEST['action'] ) && ( $_REQUEST['action'] == 'delete_occurrence' || $_REQUEST['action'] == 'break_series') && isset( $_REQUEST['series'] ) && isset( $_REQUEST['event'] ) ){
142
+ $post_id = intval( $_REQUEST['series'] );
143
+ $event_id = intval( $_REQUEST['event'] );
144
+ $action = $_REQUEST['action'];
145
+
146
+ if ( $action == 'break_series' ):
147
+ //Check nonce
148
+ check_admin_referer( 'eventorganiser_break_series_'.$event_id );
149
+
150
+ //Check permissions
151
+ if ( !current_user_can( 'edit_event', $post_id ) || !current_user_can( 'delete_event', $post_id ) )
152
+ wp_die( __( 'You do not have sufficient permissions to edit this event', 'eventorganiser' ) );
153
+
154
+ eo_break_occurrence( $post_id, $event_id );
155
+
156
+ //Redirect to prevent resubmisson
157
+ $redirect = add_query_arg( array( 'post_type' => 'event', 'page' => 'calendar' ), admin_url( 'edit.php' ) );
158
+ wp_redirect( $redirect );
159
+
160
+ elseif( $action == 'delete_occurrence' ):
161
+ global $EO_Errors;
162
+
163
+ //Check nonce
164
+ check_admin_referer( 'eventorganiser_delete_occurrence_'.$event_id );
165
+
166
+ //Check permissions
167
+ if ( ! current_user_can( 'delete_event', $post_id ) )
168
+ wp_die( __( 'You do not have sufficient permissions to delete this event', 'eventorganiser' ) );
169
+
170
+ $response = _eventorganiser_remove_occurrence( $post_id, $event_id );
171
+
172
+ //Break Cache!
173
+ _eventorganiser_delete_calendar_cache();
174
+
175
+ if ( is_wp_error( $response ) ){
176
+ $EO_Errors = $response;
177
+ } else {
178
+ $EO_Errors = new WP_Error( 'eo_notice', '<strong>'.__( 'Occurrence deleted.', 'eventorganiser' ).'</strong>' );
179
+ }
180
+ endif;
181
+ }
182
+ }
183
+
184
+
185
+ function screen_options( $options, $screen ){
186
+ $options .= '<h5>'.__( 'Calendar options', 'eventorganiser' ).'</h5>';
187
+ $options .= sprintf(
188
+ '<p><label for="%s" style="line-height: 20px;"> <input type="checkbox" name="%s" id="%s" %s> %s </label></p>',
189
+ 'eofc_time_format',
190
+ 'eofc_time_format',
191
+ 'eofc_time_format',
192
+ checked( $screen->get_option( 'eofc_time_format', 'value' ), 0, false ),
193
+ __( '24 hour time', 'eventorganiser' )
194
+ );
195
+ return $options;
196
+ }
197
+
198
+
199
+
200
+ function display(){
201
+ //Get the time 'now' according to blog's timezone
202
+ $now = new DateTime( null, eo_get_blog_timezone() );
203
+ $venues = eo_get_venues();
204
+ ?>
205
+
206
+ <div class="wrap">
207
+ <?php screen_icon( 'edit' );?>
208
+ <h2><?php _e( 'Events Calendar', 'eventorganiser' ); ?></h2>
209
+
210
+ <?php
211
+ $current = !empty( $_COOKIE['eo_admin_cal_last_view'] ) ? $_COOKIE['eo_admin_cal_last_view'] : 'month';
212
+ $views = array( 'agendaDay' => __( 'Day', 'eventorganiser' ), 'agendaWeek' => __( 'Week', 'eventorganiser' ), 'month' => __( 'Month', 'eventorganiser' ) );
213
+ ?>
214
+ <div id="calendar-view">
215
+ <span id='loading' style='display:none'><?php _e( 'Loading&#8230;' );?></span>
216
+ <?php foreach( $views as $id => $label )
217
+ printf( '<a href="#" class="view-button %s" id="%s">%s</a>', ( $id == $current ? 'active' : '' ), $id, $label );
218
+ ?>
219
+ </div>
220
+
221
+ <div id='eo_admin_calendar'></div>
222
+ <span><?php _e( 'Current date/time', 'eventorganiser' );?>: <?php echo $now->format( 'Y-m-d G:i:s \G\M\TP' );?></span>
223
+
224
+ <?php eventorganiser_event_detail_dialog(); ?>
225
+
226
+ <?php if ( current_user_can( 'publish_events' ) || current_user_can( 'edit_events' ) ):?>
227
+ <div id='eo_event_create_cal' style="display:none;" class="eo-dialog" title="<?php esc_attr_e( 'Create an event', 'eventorganiser' ); ?>">
228
+ <form name="eventorganiser_calendar" method="post" class="eo_cal">
229
+
230
+ <table class="form-table">
231
+ <tr>
232
+ <th><?php _e( 'When', 'eventorganiser' );?>: </th>
233
+ <td id="date"></td>
234
+ </tr>
235
+ <tr>
236
+ <th><?php _e( 'Event Title', 'eventorganiser' );?>: </th>
237
+ <td><input name="eo_event[event_title]" class="eo-event-title ui-autocomplete-input ui-widget-content ui-corner-all" ></td>
238
+ </tr>
239
+ <tr>
240
+ <th><?php _e( 'Where', 'eventorganiser' );?>: </th>
241
+ <td><!-- If javascript is disabed, a simple drop down menu box is displayed to choose venue.
242
+ Otherwise, the user is able to search the venues by typing in the input box.-->
243
+ <select size="30" id="venue_select" name="eo_event[venue_id]">
244
+ <option>Select a venue </option>
245
+ <?php foreach ( $venues as $venue ):?>
246
+ <option value="<?php echo intval( $venue->term_id );?>"><?php echo esc_html( $venue->name ); ?></option>
247
+ <?php endforeach;?>
248
+ </select>
249
+ </td>
250
+ </tr>
251
+ <tr>
252
+ <th></th>
253
+ <td><textarea rows="4" name="eo_event[event_content]" style="width: 220px;"></textarea></td>
254
+ </tr>
255
+ </table>
256
+ <p class="submit">
257
+ <input type="hidden" name="eo_event[StartDate]">
258
+ <input type="hidden" name="eo_event[EndDate]">
259
+ <input type="hidden" name="eo_event[StartTime]">
260
+ <input type="hidden" name="eo_event[FinishTime]">
261
+ <input type="hidden" name="eo_event[allday]">
262
+ <?php wp_nonce_field( 'eventorganiser_calendar_save' ); ?>
263
+ <?php if ( current_user_can( 'publish_events' ) ):?>
264
+ <input type="submit" class="button" tabindex="4" value="<?php _e( 'Save Draft', 'eventorganiser' );?>"" id="event-draft" name="save">
265
+ <input type="reset" class="button" id="reset" value="<?php _e( 'Cancel', 'eventorganiser' );?>">
266
+
267
+ <span id="publishing-action">
268
+ <input type="submit" accesskey="p" tabindex="5" value="<?php _e( 'Publish Event', 'eventorganiser' );?>" class="button-primary" id="publish" name="publish">
269
+ </span>
270
+
271
+ <?php elseif( current_user_can( 'edit_events' ) ):?>
272
+ <input type="reset" class="button" id="reset" value="<?php _e( 'Cancel', 'eventorganiser' );?>">
273
+ <span id="publishing-action">
274
+ <input type="submit" accesskey="p" tabindex="5" value="<?php _e( 'Submit for Review', 'eventorganiser' );?>" class="eo_alignright button-primary" id="submit-for-review" name="publish">
275
+ </span>
276
+ <?php endif; ?>
277
+
278
+ <br class="clear">
279
+ </form>
280
+ </div>
281
+ <?php endif; ?>
282
+ </div><!-- .wrap -->
283
+ <?php
284
+ }
285
+ }
286
+ $calendar_page = new EventOrganiser_Calendar_Page();
287
+
288
+
289
+ function eventorganiser_event_detail_dialog(){
290
+
291
+ $tabs = apply_filters( 'eventorganiser_calendar_dialog_tabs', array( 'summary' => __( 'Event Details', 'eventorganiser' ) ) );
292
+
293
+ printf( "<div id='events-meta' class='eo-dialog' style='display:none;' title='%s'>", esc_attr__( 'Event Detail', 'eventorganiser' ) );
294
+ echo "<div id='eo-dialog-tabs'>";
295
+ echo "<ul style='position: relative;'>";
296
+ foreach ( $tabs as $id => $label ){
297
+ printf( '<li id="eo-dialog-tab-%1$s"><a href="#eo-dialog-tab-%1$s-content">%2$s</a></li>', esc_attr( $id ), esc_html( $label ) );
298
+ }
299
+ echo '</ul>';
300
+ foreach ( $tabs as $id => $label ){
301
+ printf( '<div id="eo-dialog-tab-%s-content"> </div>', esc_attr( $id ) );
302
+ }
303
+ echo '</div>';
304
+ echo '</div>';
305
+ }
306
+ ?>
event-organiser-debug.php ADDED
@@ -0,0 +1,387 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /****** DEBUG PAGE ******/
3
+ if ( !class_exists( 'EventOrganiser_Admin_Page' ) ){
4
+ require_once( EVENT_ORGANISER_DIR.'classes/class-eventorganiser-admin-page.php' );
5
+ }
6
+ class EventOrganiser_Debug_Page extends EventOrganiser_Admin_Page
7
+ {
8
+ function set_constants(){
9
+ $this->hook = 'edit.php?post_type=event';
10
+ $this->title = __( 'System Info', 'eventorganiser' );
11
+ $this->menu = __( 'System Info', 'eventorganiser' );
12
+ $this->permissions = 'manage_options';
13
+ $this->slug = 'debug';
14
+ }
15
+
16
+ function add_page(){
17
+ self::$page = add_submenu_page($this->hook,$this->title, $this->menu, $this->permissions,$this->slug, array($this,'render_page'),10);
18
+ add_action('load-'.self::$page, array($this,'page_actions'),9);
19
+ add_action('admin_print_scripts-'.self::$page, array($this,'page_styles'),10);
20
+ add_action('admin_print_styles-'.self::$page, array($this,'page_scripts'),10);
21
+ add_action("admin_footer-".self::$page,array($this,'footer_scripts'));
22
+ if( !defined( "WP_DEBUG" ) || !WP_DEBUG ){
23
+ remove_submenu_page('edit.php?post_type=event',$this->slug);
24
+ }
25
+ }
26
+
27
+ function page_actions(){
28
+ wp_enqueue_style('eventorganiser-style');
29
+ }
30
+
31
+ function display(){
32
+ ?>
33
+ <div class="wrap">
34
+ <?php screen_icon( 'edit' );?>
35
+
36
+ <h2><?php _e('System Info','eventorganiser');?> </h2>
37
+
38
+ <?php
39
+ $eo_debugger = new EventOrganiser_Debugger();
40
+ $eo_debugger->set_prequiste( 'WordPress', '3.3', '3.5.1');
41
+ //$eo_debugger->set_known_plugin_conflicts();
42
+ //$eo_debugger->set_known_theme_conflicts();
43
+ $eo_debugger->set_db_tables( 'eo_events', 'eo_venuemeta' );
44
+ do_action_ref_array( 'eventorganiser_debugger_setup', array( &$eo_debugger ) );
45
+ ?>
46
+ <p>
47
+ <?php
48
+ _e( "This page highlights useful information for debugging. If you're reporting a bug, please include this information.", 'eventorganiser' );
49
+ echo " ";
50
+ _e( "The 'system info' link in under the Events admin tab is only visible to admins and only when <code>WP_DEBUG</code> is set to <code>true</code>.", 'eventorganiser' );
51
+ ?>
52
+ </p>
53
+ <p class="description">
54
+ <?php
55
+ _e( "Most bugs arise from theme or plug-in conflicts. You can check this by disabling all other plug-ins and switching to TwentyTweleve.", 'eventorganiser' );
56
+ echo " ";
57
+ _e( "To help speed things along, if you report a bug please indicate if you have done so. Once the plug-in or theme has been identified it is often easy to resolve the issue.", 'eventorganiser' );
58
+ echo " ";
59
+ _e( "Below any <strong>known</strong> plug-in and theme conflicts are highlighted in red.", 'eventorganiser' );
60
+ ?>
61
+ </p>
62
+ <table class="widefat">
63
+ <tr>
64
+ <th><?php esc_html_e('Site url');?></th>
65
+ <td><?php echo site_url(); ?></td>
66
+ </tr>
67
+ <tr>
68
+ <th><?php esc_html_e('Home url');?></th>
69
+ <td><?php echo home_url(); ?></td>
70
+ </tr>
71
+ <tr>
72
+ <th><?php esc_html_e('Multisite');?></th>
73
+ <td><?php echo is_multisite() ? 'Yes' : 'No' ?></td>
74
+ </tr>
75
+ <tr>
76
+ <th><?php esc_html_e('Event Organiser version');?></th>
77
+ <td><?php global $eventorganiser_db_version; echo $eventorganiser_db_version; ?></td>
78
+ </tr>
79
+ <tr>
80
+ <th><?php esc_html_e('WordPress');?></th>
81
+ <td>
82
+ <?php $eo_debugger->verbose_prequiste_check( 'WordPress', get_bloginfo( 'version' ) );?>
83
+ </td>
84
+ </tr>
85
+ <tr>
86
+ <th><?php esc_html_e('PHP Version');?></th>
87
+ <td> <?php echo PHP_VERSION; ?></td>
88
+ </tr>
89
+ <tr>
90
+ <th><?php esc_html_e('MySQL Version');?></th>
91
+ <td> <?php echo mysql_get_server_info(); ?></td>
92
+ </tr>
93
+ <tr>
94
+ <th><?php esc_html_e('Web Server');?></th>
95
+ <td> <?php echo $_SERVER['SERVER_SOFTWARE']; ?></td>
96
+ </tr>
97
+ <tr>
98
+ <th><?php esc_html_e('PHP Memory Usage');?></th>
99
+ <th><?php echo $eo_debugger->verbose_memory_check(); ?>
100
+ </tr>
101
+ <tr>
102
+ <th><?php esc_html_e('PHP Post Max Size');?></th>
103
+ <td><?php echo ini_get('post_max_size'); ?></td>
104
+ </tr>
105
+ <tr>
106
+ <th><?php esc_html_e('PHP Upload Max Size');?></th>
107
+ <td><?php echo ini_get('upload_max_filesize'); ?></td>
108
+ </tr>
109
+ <tr>
110
+ <th><?php esc_html_e('PHP cURL Support');?></th>
111
+ <td> <?php echo (function_exists('curl_init')) ? _e('Yes', 'eventorganiser') . "\n" : _e('No', 'eventorganiser') . "\n"; ?></td>
112
+ </tr>
113
+ <tr>
114
+ <th><?php esc_html_e('Plug-ins');?></th>
115
+ <td>
116
+ <?php $eo_debugger->verbose_plugin_check();?>
117
+ </td>
118
+ </tr>
119
+ <tr>
120
+ <th><?php esc_html_e('Theme');?></th>
121
+ <td>
122
+ <?php $eo_debugger->verbose_theme_check();?>
123
+ </td>
124
+ </tr>
125
+ <tr>
126
+ <th><?php esc_html_e('Databse Prefix:');?></th>
127
+ <td><?php global $wpdb; echo $wpdb->prefix; ?></td>
128
+ </tr>
129
+ <tr>
130
+ <th><?php esc_html_e('Database tables');?></th>
131
+ <td>
132
+ <?php $eo_debugger->verbose_database_check();?>
133
+ </td>
134
+ </tr>
135
+ <tr>
136
+ <th><?php esc_html_e('Database character set');?></th>
137
+ <td>
138
+ <?php $eo_debugger->verbose_database_charset_check();?>
139
+ </td>
140
+ </tr>
141
+ <tr>
142
+ <th><?php esc_html_e('Debug mode');?></th>
143
+ <td><?php echo defined( 'WP_DEBUG' ) && WP_DEBUG ? 'Enabled' : 'Disabled'; ?></td>
144
+ </tr>
145
+
146
+ <tr>
147
+ <th><?php printf( esc_html__( '%s present', 'eventorganiser' ), '<code>wp_footer()</code>' );?></th>
148
+ <td><?php $eo_debugger->verbose_footer_check();?></td>
149
+ </tr>
150
+ <tr>
151
+ <th><?php esc_html_e('Widget Sidebars');?></th>
152
+ <td><?php $eo_debugger->verbose_sidebar_check();?></td>
153
+ </tr>
154
+ <tr>
155
+ <th><?php esc_html_e('Timezone');?></th>
156
+ <td><?php echo eo_get_blog_timezone()->getName(); printf( ' ( %s / %s ) ', get_option( 'gmt_offset' ), get_option( 'timezone_string' ) )?></td>
157
+ </tr>
158
+ </table>
159
+ <?php
160
+ printf(
161
+ '<p class="description"> <span class="eo-debug-warning">*</span> %s </p>',
162
+ __( 'Known plug-in & theme conflicts, highlighted in red, may be minor or have a simple resolution. Please contact support.' )
163
+ );
164
+ ?>
165
+ </div><!--End .wrap -->
166
+ <?php
167
+ }
168
+
169
+ }
170
+ $venues_page = new EventOrganiser_Debug_Page();
171
+
172
+ class EventOrganiser_Debugger{
173
+
174
+ var $prequiste = array();
175
+ var $plugins = array();
176
+ var $themes = array();
177
+ var $db_tables = array();
178
+ var $plugin = 'Event Organiser';
179
+
180
+ var $ok_class = 'eo-debug-ok';
181
+ var $warning_class = 'eo-debug-warning';
182
+ var $alert_class = 'eo-debug-alert';
183
+
184
+ function set_plugin( $plugin ){
185
+ $this->plugin = $plugin;
186
+ }
187
+
188
+ function set_prequiste( $requirement, $min = false, $max = false ){
189
+ $this->prequiste[$requirement] = compact( 'min', 'max' );
190
+ }
191
+
192
+ function set_known_plugin_conflicts( ){
193
+ $this->plugins = array_merge( $this->plugins, array_map( 'strtolower', func_get_args() ) );
194
+ }
195
+
196
+ function set_known_theme_conflicts( ){
197
+ $this->themes = array_merge( $this->themes, func_get_args() );
198
+ }
199
+
200
+ function set_db_tables( ){
201
+ $this->db_tables = array_merge( $this->db_tables, func_get_args() );
202
+ }
203
+
204
+ function check_prequiste( $requirement, $v ){
205
+ if( !isset( $this->prequiste[$requirement] ) )
206
+ return 1;
207
+
208
+ $versions = $this->prequiste[$requirement];
209
+
210
+ if( $versions['min'] && version_compare( $versions['min'], $v ) > 0 )
211
+ return -1;
212
+
213
+ if( $versions['max'] && version_compare( $versions['max'], $v ) < 0 )
214
+ return 0;
215
+
216
+ return 1;
217
+ }
218
+
219
+ function verbose_plugin_check(){
220
+ $installed = get_plugins();
221
+
222
+ foreach( $installed as $plugin_slug => $plugin_data ){
223
+ if( ! is_plugin_active( $plugin_slug ) )
224
+ continue;
225
+
226
+ $class = in_array( strtolower( $plugin_slug ), $this->plugins ) ? $this->warning_class : '';
227
+
228
+ printf(
229
+ ' <span class="%s"> %s %s </span> </br>',
230
+ esc_attr( $class ),
231
+ $plugin_data['Name'],
232
+ $plugin_data['Version']
233
+ );
234
+ }
235
+ }
236
+
237
+ function verbose_theme_check(){
238
+ if( version_compare( '3.4', get_bloginfo( 'version' ) ) <= 0 ){
239
+ $theme = wp_get_theme();
240
+ $class = in_array( strtolower( $theme->stylesheet ), $this->themes ) ? $this->warning_class : '';
241
+ printf(
242
+ ' <span class="%s"> %s %s </span> </br> %s',
243
+ esc_attr( $class ),
244
+ $theme->get('Name'),
245
+ $theme->get('Version'),
246
+ $theme->get('ThemeURI')
247
+ );
248
+ }else{
249
+ $theme_name = get_current_theme();
250
+ $class = in_array( strtolower( $theme_name ), $this->themes ) ? $this->warning_class : '';
251
+ printf(
252
+ ' <span class="%s"> %s </span> </br>',
253
+ esc_attr( $class ),
254
+ $theme_name
255
+ );
256
+ }
257
+ }
258
+
259
+ function verbose_database_check(){
260
+ if( $this->db_tables ){
261
+ foreach( $this->db_tables as $db_table ){
262
+ $class = $this->table_exists( $db_table ) ? $this->ok_class : $this->warning_class;
263
+ printf( '<span class="%s"> %s </span></br>', esc_attr( $class ), esc_attr( $db_table ) );
264
+ }
265
+ }
266
+ }
267
+
268
+ function table_exists( $table ){
269
+ global $wpdb;
270
+ return $wpdb->get_var("show tables like '".$wpdb->prefix.$table."'") == $wpdb->prefix.$table;
271
+ }
272
+
273
+ function verbose_database_charset_check(){
274
+ global $wpdb;
275
+
276
+ if( $wpdb->query( $wpdb->prepare( 'SHOW CHARACTER SET WHERE LOWER( Charset ) = LOWER( %s )', DB_CHARSET ) ) )
277
+ $class = '';
278
+ else
279
+ $class = $this->warning_class;
280
+
281
+ printf( '<span class="%s"> %s </span></br>', esc_attr( $class ), esc_attr( DB_CHARSET ) );
282
+ }
283
+
284
+ function verbose_prequiste_check( $requirement, $v ){
285
+
286
+ $versions = $this->prequiste[$requirement];
287
+
288
+ if( 1 == $this->check_prequiste( $requirement, $v ) ){
289
+ printf( '<span class="%s">%s</span>', esc_attr( $this->ok_class ), $v );
290
+ }elseif( 0 == $this->check_prequiste( $requirement, $v ) ){
291
+ printf(
292
+ '<span class="%s">%s</span>. %s',
293
+ esc_attr( $this->alert_class ),
294
+ $v,
295
+ sprintf( __( '%s has only been tested up to %s %s' ), $this->plugin, $requirement, $versions['max'] )
296
+ );
297
+ }elseif( -1 == $this->check_prequiste( $requirement, $v ) ){
298
+ printf(
299
+ '<span class="%s">%s</span>. %s',
300
+ esc_attr( $this->warning_class ),
301
+ $v,
302
+ sprintf( __( '%s requires %s version %s or higher' ), $this->plugin, $requirement, $versions['min'] )
303
+ );
304
+ }
305
+ }
306
+
307
+ function verbose_sidebar_check(){
308
+ $footer_present = get_option( 'eo_sidebar_correct' );
309
+
310
+ if( $footer_present === false ){
311
+ printf(
312
+ '<span class="%s">%s</span><br/> %s',
313
+ esc_attr( $this->alert_class ),
314
+ __( 'Unknown', 'eventorganiser' ),
315
+ sprintf( __( 'Turn <a href="%s">WP_Debug mode</a> on and revisit the front-end to check' ), 'http://codex.wordpress.org/WP_DEBUG' )
316
+ );
317
+ }elseif( $footer_present == 1 ){
318
+ printf(
319
+ '<span class="%s">%s</span><br/> %s',
320
+ esc_attr( $this->ok_class ),
321
+ __( 'Correctly registered', 'eventorganiser' ),
322
+ sprintf( __( 'Turn <a href="%s">WP_Debug mode</a> on and revisit the front-end to check' ), 'http://codex.wordpress.org/WP_DEBUG' )
323
+ );
324
+ }else{
325
+ printf(
326
+ '<span class="%s">%s</span><br/> %s',
327
+ esc_attr( $this->warning_class ),
328
+ __( 'Incorrectly registered', 'eventorganiser' ),
329
+ sprintf(
330
+ __( "The widget sidebars are incorrectly registered. See the <a href='%s'>FAQ</a> or contact support to resolve this." ),
331
+ 'http://wp-event-organiser.com/faq#i-cannot-navigate-between-months-on-the-widget-calendar'
332
+ )
333
+ );
334
+ }
335
+ }
336
+
337
+ function verbose_memory_check(){
338
+
339
+ if( function_exists( 'memory_get_usage' ) ){
340
+ $memory_usage = round( memory_get_usage() / 1024 / 1024, 2);
341
+ $percentage = round( $memory_usage / ini_get( 'memory_limit' ) * 100, 0 );
342
+ printf( '%d / %d <span class="%s">( %s )</span>',
343
+ ceil( $memory_usage ),
344
+ ini_get( 'memory_limit' ),
345
+ $percentage > 90 ? $this->alert_class : $this->ok_class,
346
+ $percentage . "%"
347
+ );
348
+ }else{
349
+ printf( ' ? / %d <span class="%s">( %s )</span>',
350
+ ini_get( 'memory_limit' ),
351
+ $this->alert_class,
352
+ __( 'unknown', 'eventorganiser' )
353
+ );
354
+ }
355
+
356
+ }
357
+
358
+ function verbose_footer_check(){
359
+ $footer_present = get_option( 'eo_wp_footer_present' );
360
+
361
+ if( $footer_present === false ){
362
+ printf(
363
+ '<span class="%s">%s</span><br/> %s',
364
+ esc_attr( $this->alert_class ),
365
+ __( 'Unknown', 'eventorganiser' ),
366
+ sprintf( __( 'Turn <a href="%s">WP_Debug mode</a> on and revisit the front-end to check' ), 'http://codex.wordpress.org/WP_DEBUG' )
367
+ );
368
+ }elseif( $footer_present == 1 ){
369
+ printf(
370
+ '<span class="%s">%s</span><br/> %s',
371
+ esc_attr( $this->ok_class ),
372
+ __( 'Yes', 'eventorganiser' ),
373
+ sprintf( __( 'Turn <a href="%s">WP_Debug mode</a> on and revisit the front-end to check' ), 'http://codex.wordpress.org/WP_DEBUG' )
374
+ );
375
+ }else{
376
+ printf(
377
+ '<span class="%s">%s</span><br/> %s',
378
+ esc_attr( $this->warning_class ),
379
+ __( 'No', 'eventorganiser' ),
380
+ sprintf(
381
+ __( "The <a href='%s'>wp_footer hook</a> could be not be found. Without, for example, the calendar will not function" ),
382
+ 'http://codex.wordpress.org/Function_Reference/wp_footer'
383
+ )
384
+ );
385
+ }
386
+ }
387
+ }
event-organiser-edit.php ADDED
@@ -0,0 +1,389 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Functions for Event CPT editing / creating page
4
+ * @since 1.0.0
5
+ */
6
+ /**
7
+ * Initialises the plug-ins metaboxs on Event CPT
8
+ * @since 1.0.0
9
+ * @ignore
10
+ */
11
+ function eventorganiser_edit_init(){
12
+
13
+ // add a meta box to event post types.
14
+ add_meta_box( 'eventorganiser_detail', __( 'Event Details', 'eventorganiser' ), '_eventorganiser_details_metabox', 'event', 'normal', 'high' );
15
+
16
+ // add a callback function to save any data a user enters in
17
+ add_action( 'save_post', 'eventorganiser_details_save' );
18
+ }
19
+ add_action( 'admin_init', 'eventorganiser_edit_init' );
20
+
21
+ /**
22
+ * Repurposes author metabox as organiser
23
+ * @since 1.0.0
24
+ * @ignore
25
+ */
26
+ function _eventorganiser_author_meta_box_title() {
27
+ remove_meta_box( 'authordiv', 'event', 'core' );
28
+ add_meta_box( 'authordiv', __( 'Organiser', 'eventorganiser' ), 'post_author_meta_box', 'event', 'core', 'high' );
29
+ }
30
+ add_action( 'add_meta_boxes_event', '_eventorganiser_author_meta_box_title' );
31
+
32
+ /**
33
+ * Sets up the event data metabox
34
+ * This allows user to enter date / time, reoccurrence and venue data for the event
35
+ * @ignore
36
+ * @since 1.0.0
37
+ */
38
+ function _eventorganiser_details_metabox( $post ){
39
+ global $wp_locale;
40
+
41
+ //Sets the format as php understands it, and textual.
42
+ $phpFormat = eventorganiser_get_option( 'dateformat' );
43
+ if ( 'd-m-Y' == $phpFormat ){
44
+ $format = 'dd-mm-yyyy'; //Human form
45
+ } elseif( 'Y-m-d' == $phpFormat ) {
46
+ $format = 'yyyy-mm-dd'; //Human form
47
+ }else{
48
+ $format = 'mm-dd-yyyy'; //Human form
49
+ }
50
+
51
+ //Get the starting day of the week
52
+ $start_day = intval( get_option( 'start_of_week' ) );
53
+ $ical_days = array( 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA' );
54
+
55
+ //Retrieve event details
56
+ extract( eo_get_event_schedule( $post->ID ) );
57
+ $venues = eo_get_venues();
58
+ $venue_id = (int) eo_get_venue( $post->ID );
59
+
60
+
61
+ //$sche_once is used to disable date editing unless the user specifically requests it.
62
+ //But a new event might be recurring (via filter), and we don't want to 'lock' new events.
63
+ //See http://wordpress.org/support/topic/wrong-default-in-input-element
64
+ $sche_once = ( $schedule == 'once' || !empty(get_current_screen()->action) );
65
+
66
+ if ( !$sche_once ){
67
+ $notices = '<strong>'. __( 'This is a reoccurring event', 'eventorganiser' ).'</strong>. '
68
+ . __( 'Check to edit this event and its reoccurrences', 'eventorganiser' )
69
+ .' <input type="checkbox" id="HWSEvent_rec" name="eo_input[AlterRe]" value="yes">';
70
+ }else{
71
+ $notices = '';
72
+ }
73
+
74
+ //Start of meta box
75
+ if( $notices = apply_filters('eventorganiser_event_metabox_notice', $notices, $post ) ){
76
+ echo '<div class="updated below-h2"><p>'.$notices.'</p></div>';
77
+ }
78
+ ?>
79
+ <div class="<?php echo ( $sche_once ? 'onetime': 'reoccurence' );?>">
80
+ <p><?php printf( __( 'Ensure dates are entered in %1$s format and times in %2$s (24 hour) format', 'eventorganiser' ), '<strong>'.$format.'</strong>', ' <strong>hh:mm</strong>' );?> </p>
81
+
82
+ <table id="eventorganiser_event_detail" class="form-table">
83
+ <tr valign="top" class="event-date">
84
+ <td class="eo-label"><?php echo __( 'Start Date/Time', 'eventorganiser' ).':'; ?> </td>
85
+ <td>
86
+ <input class="ui-widget-content ui-corner-all" name="eo_input[StartDate]" size="10" maxlength="10" id="from_date" <?php disabled( !$sche_once );?> value="<?php echo $start->format( $phpFormat ); ?>"/>
87
+
88
+ <input name="eo_input[StartTime]" class="eo_time ui-widget-content ui-corner-all" size="4" maxlength="5" id="HWSEvent_time" <?php disabled( (!$sche_once) || $all_day );?> value="<?php echo $start->format( 'H:i' );?>"/>
89
+
90
+ </td>
91
+ </tr>
92
+
93
+ <tr valign="top" class="event-date">
94
+ <td class="eo-label"><?php echo __( 'End Date/Time', 'eventorganiser' ).':';?> </td>
95
+ <td>
96
+ <input class="ui-widget-content ui-corner-all" name="eo_input[EndDate]" size="10" maxlength="10" id="to_date" <?php disabled( !$sche_once );?> value="<?php echo $end->format( $phpFormat ); ?>"/>
97
+ <input name="eo_input[FinishTime]" class="eo_time ui-widget-content ui-corner-all " size="4" maxlength="5" id="HWSEvent_time2" <?php disabled( (!$sche_once) || $all_day );?> value="<?php echo $end->format( 'H:i' ); ?>"/>
98
+
99
+ <label>
100
+ <input type="checkbox" id="eo_allday" <?php checked( $all_day ); ?> name="eo_input[allday]" <?php disabled( !$sche_once );?> value="1"/>
101
+ <?php _e( 'All day', 'eventorganiser' );?>
102
+ </label>
103
+
104
+ </td>
105
+ </tr>
106
+
107
+ <tr class="event-date">
108
+ <td class="eo-label"><?php _e( 'Reoccurence:', 'eventorganiser' );?> </td>
109
+ <td>
110
+ <?php $reoccurrence_schedules = array( 'once' => __( 'once', 'eventorganiser' ), 'daily' => __( 'daily', 'eventorganiser' ), 'weekly' => __( 'weekly', 'eventorganiser' ),
111
+ 'monthly' => __( 'monthly', 'eventorganiser' ), 'yearly' => __( 'yearly', 'eventorganiser' ), 'custom' => __( 'custom', 'eventorganiser' ) );?>
112
+
113
+ <select id="HWSEventInput_Req" name="eo_input[schedule]">
114
+ <?php foreach ( $reoccurrence_schedules as $index => $val ): ?>
115
+ <option value="<?php echo $index;?>" <?php selected( $schedule, $index );?>><?php echo $val;?></option>
116
+ <?php endforeach; //End foreach $allowed_reoccurs?>
117
+ </select>
118
+ </td>
119
+ </tr>
120
+
121
+ <tr valign="top" class="event-date reocurrence_row">
122
+ <td></td>
123
+ <td>
124
+ <p><?php _e( 'Repeat every', 'eventorganiser' );?>
125
+ <input <?php disabled( !$sche_once || $all_day );?> class="ui-widget-content ui-corner-all" name="eo_input[event_frequency]" id="HWSEvent_freq" type="number" min="1" max="365" maxlength="4" size="4" disabled="disabled" value="<?php echo $frequency;?>" />
126
+ <span id="recpan" > </span>
127
+ </p>
128
+
129
+ <p id="dayofweekrepeat">
130
+ <?php _e( 'on', 'eventorganiser' );?>
131
+ <?php for ($i = 0; $i <= 6; $i++ ):
132
+ $d = ($start_day + $i) % 7;
133
+ $ical_d = $ical_days[$d];
134
+ $day = $wp_locale->weekday_abbrev[$wp_locale->weekday[$d]];
135
+ $schedule_days = ( is_array( $schedule_meta ) ? $schedule_meta : array() );
136
+ ?>
137
+ <input type="checkbox" id="day-<?php echo $day;?>" <?php checked( in_array( $ical_d, $schedule_days ), true ); ?> value="<?php echo esc_attr( $ical_d )?>" class="daysofweek" name="eo_input[days][]" disabled="disabled" />
138
+ <label for="day-<?php echo $day;?>" > <?php echo $day;?></label>
139
+ <?php endfor; ?>
140
+ </p>
141
+
142
+ <p id="dayofmonthrepeat">
143
+ <label for="bymonthday" >
144
+ <input type="radio" id="bymonthday" disabled="disabled" name="eo_input[schedule_meta]" <?php checked( $occurs_by, 'BYMONTHDAY' ); ?> value="BYMONTHDAY=" />
145
+ <?php _e( 'day of month', 'eventorganiser' );?>
146
+ </label>
147
+ <label for="byday" >
148
+ <input type="radio" id="byday" disabled="disabled" name="eo_input[schedule_meta]" <?php checked( $occurs_by != 'BYMONTHDAY', true ); ?> value="BYDAY=" />
149
+ <?php _e( 'day of week', 'eventorganiser' );?>
150
+ </label>
151
+ </p>
152
+
153
+ <p class="reoccurrence_label">
154
+ <?php _e( 'until', 'eventorganiser' );?>
155
+ <input <?php disabled( (!$sche_once) || $all_day ); ?> class="ui-widget-content ui-corner-all" name="eo_input[schedule_end]" id="recend" size="10" maxlength="10" disabled="disabled" value="<?php echo $schedule_last->format( $phpFormat ); ?>"/>
156
+ </p>
157
+
158
+ <p id="event_summary"> </p>
159
+ </td>
160
+ </tr>
161
+
162
+ <tr valign="top" id="eo_occurrence_picker_row" class="event-date">
163
+ <td class="eo-label">
164
+ <?php esc_html_e( 'Include/Exclude occurrences', 'eventorganiser' );?>
165
+ </td>
166
+ <td>
167
+ <?php submit_button( __( 'Show dates', 'eventorganiser' ), 'hide-if-no-js eo_occurrence_toogle button small', 'eo_date_toggle', false ); ?>
168
+
169
+ <div id="eo_occurrence_datepicker"></div>
170
+ <?php
171
+ //var_dump($include);
172
+ if ( !empty( $include ) ){
173
+ $include_str = array_map( 'eo_format_datetime', $include, array_fill( 0, count( $include ), 'Y-m-d' ) );
174
+ $include_str = esc_textarea( sanitize_text_field( implode( ',', $include_str ) ) );
175
+ } else {
176
+ $include_str = '';
177
+ }?>
178
+ <textarea style="display:none;" name="eo_input[include]" id="eo_occurrence_includes"><?php echo $include_str; ?></textarea>
179
+
180
+ <?php
181
+ if ( !empty($exclude) ){
182
+ $exclude_str = array_map( 'eo_format_datetime', $exclude, array_fill( 0, count( $exclude ), 'Y-m-d' ) );
183
+ $exclude_str = esc_textarea( sanitize_text_field( implode( ',', $exclude_str ) ) );
184
+ } else {
185
+ $exclude_str = '';
186
+ }?>
187
+ <textarea style="display:none;" name="eo_input[exclude]" id="eo_occurrence_excludes"><?php echo $exclude_str; ?></textarea>
188
+
189
+ </td>
190
+ </tr>
191
+ <tr valign="top"class="eo-venue-combobox-select">
192
+ <td class="eo-label"> <?php _e( 'Venue', 'eventorganiser' );?>: </td>
193
+ <td>
194
+ <select size="50" id="venue_select" name="eo_input[event-venue]">
195
+ <option><?php _e( 'Select a venue', 'eventorganiser' );?></option>
196
+ <?php foreach ( $venues as $venue ):?>
197
+ <option <?php selected( $venue->term_id, $venue_id );?> value="<?php echo intval( $venue->term_id );?>"><?php echo esc_html( $venue->name ); ?></option>
198
+ <?php endforeach;?>
199
+ </select>
200
+ </td>
201
+ </tr>
202
+
203
+ <!-- Add New Venue -->
204
+ <tr valign="top"class="eo-add-new-venue">
205
+ <td class="eo-label"><label><?php _e( 'Venue Name', 'eventorganiser' );?>:</label></td>
206
+ <td><input type="text" name="eo_venue[name]" id="eo_venue_name" value=""/></td>
207
+ </tr>
208
+ <?php
209
+ $address_fields = _eventorganiser_get_venue_address_fields();
210
+ foreach ( $address_fields as $key => $label ){
211
+ printf( '<tr valign="top"class="eo-add-new-venue">
212
+ <th><label>%1$s:</label></th>
213
+ <td><input type="text" name="eo_venue[%2$s]" class="eo_addressInput" id="eo_venue_add" value=""/></td>
214
+ </tr>',
215
+ $label,
216
+ esc_attr( trim( $key, '_' ) )/* Keys are prefixed by '_' */
217
+ );
218
+ }
219
+ ?>
220
+ <tr valign="top" class="eo-add-new-venue" >
221
+ <td class="eo-label"></td>
222
+ <td>
223
+ <a class="button eo-add-new-venue-cancel" href="#"><?php esc_html_e( 'Cancel','eventorganiser' );?> </a>
224
+ </td>
225
+ </tr>
226
+
227
+ <!-- Venue Map -->
228
+ <tr valign="top" class="venue_row <?php if ( !$venue_id ) echo 'novenue';?>" >
229
+ <td class="eo-label"></td>
230
+ <td>
231
+ <div id="eventorganiser_venue_meta" style="display:none;">
232
+ <input type="hidden" id="eo_venue_Lat" name="eo_venue[latitude]" value="<?php eo_venue_lat( $venue_id );?>" />
233
+ <input type="hidden" id="eo_venue_Lng" name="eo_venue[longtitude]" value="<?php eo_venue_lng( $venue_id ); ?>" />
234
+ </div>
235
+ <div id="venuemap" class="ui-widget-content ui-corner-all gmap3"></div>
236
+ <div class="clear"></div>
237
+ </td>
238
+ </tr>
239
+ </table>
240
+ </div>
241
+ <?php
242
+
243
+ // create a custom nonce for submit verification later
244
+ wp_nonce_field( 'eventorganiser_event_update_'.get_the_ID(), '_eononce' );
245
+ }
246
+
247
+
248
+ /**
249
+ * Saves the event data posted from the event metabox.
250
+ * Hooked to the 'save_post' action
251
+ *
252
+ * @since 1.0.0
253
+ *
254
+ * @param int $post_id the event post ID
255
+ * @return int $post_id the event post ID
256
+ */
257
+ function eventorganiser_details_save( $post_id ) {
258
+
259
+ //make sure data came from our meta box
260
+ if ( !isset( $_POST['_eononce'] ) || !wp_verify_nonce( $_POST['_eononce'], 'eventorganiser_event_update_'.$post_id ) ) return;
261
+
262
+ // verify this is not an auto save routine.
263
+ if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
264
+
265
+ //authentication checks
266
+ if (!current_user_can( 'edit_event', $post_id ) ) return;
267
+
268
+ //Collect raw data
269
+ $raw_data = ( isset( $_POST['eo_input'] ) ? $_POST['eo_input'] : array() );
270
+ $raw_data = wp_parse_args( $raw_data, array(
271
+ 'StartDate' => '', 'EndDate' => '', 'StartTime' => '00:00', 'FinishTime' => '23:59', 'schedule' => 'once', 'event_frequency' => 1,
272
+ 'schedule_end' => '', 'allday' => 0, 'schedule_meta' => '', 'days' => array(), 'include' => '', 'exclude' => '', ) );
273
+
274
+ //Update venue
275
+ $venue_id = !empty( $raw_data['event-venue'] ) ? intval( $raw_data['event-venue'] ) : null;
276
+
277
+ //Maybe create a new venue
278
+ if ( empty( $venue_id ) && !empty( $_POST['eo_venue']) && current_user_can( 'manage_venues' ) ){
279
+ $venue = $_POST['eo_venue'];
280
+ if ( !empty( $venue['name'] ) ){
281
+ $new_venue = eo_insert_venue( $venue['name'], $venue );
282
+ if ( !is_wp_error( $new_venue ) )
283
+ $venue_id = $new_venue['term_id'];
284
+ }
285
+ }
286
+
287
+ //Set venue
288
+ $r = wp_set_post_terms( $post_id, array( $venue_id ), 'event-venue', false );
289
+
290
+
291
+ //If reocurring, but not editing occurrences, can abort here, but trigger hook.
292
+ if ( eo_reoccurs( $post_id ) && ( !isset( $raw_data['AlterRe'] ) || 'yes' != $raw_data['AlterRe'] ) ){
293
+ do_action( 'eventorganiser_save_event', $post_id );//Need this to update cache
294
+ return;
295
+ }
296
+
297
+ //Set times for all day events
298
+ $all_day = intval($raw_data['allday']);
299
+ if ( $all_day ){
300
+ $raw_data['StartTime'] = '00:00';
301
+ $raw_data['FinishTime'] = '23:59';
302
+ }
303
+
304
+ //Check dates
305
+ $start = _eventorganiser_check_datetime( trim( $raw_data['StartDate'] ) . ' ' . trim( $raw_data['StartTime'] ) );
306
+ $end = _eventorganiser_check_datetime( trim( $raw_data['EndDate'] ) . ' ' . trim( $raw_data['FinishTime'] ) );
307
+ $schedule_last = _eventorganiser_check_datetime( trim( $raw_data['schedule_end'] ) . ' ' . trim( $raw_data['StartTime'] ) );
308
+
309
+ //Collect schedule meta
310
+ $schedule = $raw_data['schedule'];
311
+ if ( 'weekly' == $schedule ){
312
+ $schedule_meta = $raw_data['days'];
313
+ $occurs_by = '';
314
+ } elseif ( 'monthly' == $schedule ){
315
+ $schedule_meta = $raw_data['schedule_meta'];
316
+ $occurs_by = trim( $schedule_meta, '=' );
317
+ } else {
318
+ $schedule_meta = '';
319
+ $occurs_by = '';
320
+ }
321
+
322
+ //Collect include/exclude
323
+ $in_ex = array();
324
+ foreach ( array( 'include', 'exclude' ) as $key ):
325
+ $in_ex[$key] = array();
326
+ $arr = explode( ',', sanitize_text_field( $raw_data[$key] ) );
327
+ if ( !empty( $arr ) ){
328
+ foreach ( $arr as $date ):
329
+ $date_obj = _eventorganiser_check_datetime( $date . ' ' . $raw_data['StartTime'], 'Y-m-d' );
330
+ if( $date_obj )
331
+ $in_ex[$key][] = $date_obj;
332
+ endforeach;
333
+ }
334
+ endforeach;
335
+
336
+ $event_data = array(
337
+ 'start' => $start,
338
+ 'end' => $end,
339
+ 'all_day' => $all_day,
340
+ 'schedule' => $schedule,
341
+ 'frequency' => (int) $raw_data['event_frequency'],
342
+ 'schedule_last' => $schedule_last,
343
+ 'schedule_meta' => $schedule_meta,
344
+ 'occurs_by' => $occurs_by,
345
+ 'include' => $in_ex['include'],
346
+ 'exclude' => $in_ex['exclude'],
347
+ );
348
+
349
+ $response = eo_update_event( $post_id, $event_data );
350
+
351
+ if ( is_wp_error( $response ) ){
352
+ global $EO_Errors;
353
+ $code = $response->get_error_code();
354
+ $message = $response->get_error_message( $code );
355
+ $errors[$post_id][] = __( 'Event dates were not saved.', 'eventorganiser' );
356
+ $errors[$post_id][] = $message;
357
+ $EO_Errors->add( 'eo_error',$message );
358
+ update_option( 'eo_notice', $errors );
359
+ }
360
+
361
+ return;
362
+ }
363
+
364
+ /**
365
+ * Display custom error or alert messages on events CPT page
366
+ *
367
+ * @ignore
368
+ * @since 1.0.0
369
+ */
370
+ add_action( 'admin_notices', '_eventorganiser_event_edit_admin_notice', 0 );
371
+ function _eventorganiser_event_edit_admin_notice(){
372
+
373
+ global $post;
374
+
375
+ $notice = get_option( 'eo_notice' );
376
+ if ( empty( $notice ) || empty( $post->ID ) ) return '';
377
+
378
+ foreach ( $notice as $pid => $messages ){
379
+ if ( $post->ID == $pid ){
380
+ printf( '<div id="message" class="error"> <p> %s </p> </div>', implode( ' </p> <p> ', $messages ) );
381
+
382
+ //make sure to remove notice after its displayed so its only displayed when needed.
383
+ unset( $notice[0] );
384
+ unset( $notice[$pid] );
385
+ update_option( 'eo_notice', $notice );
386
+ }
387
+ }
388
+ }
389
+ ?>
event-organiser-go-pro.php ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Calendar Admin Page
4
+ */
5
+ if(!class_exists('EventOrganiser_Admin_Page')){
6
+ require_once(EVENT_ORGANISER_DIR.'classes/class-eventorganiser-admin-page.php' );
7
+ }
8
+ /**
9
+ * Calendar Admin Page
10
+ *
11
+ * Extends the EentOrganiser_Admin_Page class. Creates the calendar admin page
12
+ * @version 1.0
13
+ * @see EventOrganiser_Admin_Page
14
+ * @package event organiser
15
+ */
16
+ class EventOrganiser_Pro_Page extends EventOrganiser_Admin_Page
17
+ {
18
+ /**
19
+ * This sets the calendar page variables
20
+ */
21
+ function set_constants(){
22
+ $this->hook = 'edit.php?post_type=event';
23
+ $this->title = __('Get Event Organiser Pro Add-On','eventorganiser');
24
+ $this->menu =__('Go Pro','eventorganiser');
25
+ $this->permissions ='manage_options';
26
+ $this->slug ='eo-pro';
27
+ }
28
+
29
+ function add_page(){
30
+ self::$page = add_dashboard_page($this->title, $this->menu, $this->permissions,$this->slug, array($this,'render_page'),10);
31
+ add_action('load-'.self::$page, array($this,'page_actions'),9);
32
+ add_action('admin_print_scripts-'.self::$page, array($this,'page_styles'),10);
33
+ add_action('admin_print_styles-'.self::$page, array($this,'page_scripts'),10);
34
+ add_action("admin_footer-".self::$page,array($this,'footer_scripts'));
35
+ remove_submenu_page('index.php',$this->slug);
36
+ }
37
+
38
+ /**
39
+ * Enqueues the page's scripts and styles, and localises them.
40
+ */
41
+ function page_scripts(){
42
+ }
43
+
44
+
45
+ function display(){
46
+ $plugins = get_plugins();
47
+ $plugin = $plugins['event-organiser/event-organiser.php'];
48
+ ?>
49
+ <div class="wrap">
50
+ <div class="wrap about-wrap">
51
+ <h1> <?php printf(__('Get Event Organiser Pro', 'eventorganiser'), $plugin['Version']); ?> </h1>
52
+
53
+ <div class="about-text"><?php
54
+ echo '<p>'.__( 'Event Organiser Pro is a premium add-on bringing advanced booking management to Event Organiser.', 'eventorganiser' );
55
+ echo '<p>'.__( "But that's not all &hellip;", 'eventorganiser' ); ?>
56
+ </div>
57
+
58
+ <div class="event-organiser-logo" style="background: url('<?php echo EVENT_ORGANISER_URL.'css/images/eobadge.png';?>');padding-top: 150px;height: 52px;width: 185px;color: #666;font-weight: bold;font-size: 14px;text-align: center;text-shadow: 0 1px 0 rgba(255, 255, 255, 0.8);margin: 0 -5px;position: absolute;top: 0;right: 0;"></div>
59
+
60
+ <hr style="color:#CCC;background-color:#CCC;border:0;border-bottom:1px solid #CCC;">
61
+
62
+ <style>
63
+ .eo-feature-section {float: left;margin: 2%;width: 29%;}
64
+ .eo-feature-section img{border: 1px #CCC solid;-webkit-box-shadow: 0 1px 3px rgba( 0, 0, 0, 0.3 );box-shadow: 0 1px 3px rgba( 0, 0, 0, 0.3 );}
65
+ </style>
66
+
67
+ <?php
68
+
69
+ self::print_feature(
70
+ __( 'Flexible Booking Options', 'eventorganiser' ),
71
+ __("Sell tickets for specific dates or sell tickets for all dates of an event – such as booking places on a course. You can offer multiple tickets, and customise the booking form to suit your needs.", 'eventorganiser'),
72
+ 'eo-pro-ticket-picker.png'
73
+ );
74
+ self::print_feature(
75
+ __( 'Additional shortcodes & improved UI', 'eventorganiser' ),
76
+ __( 'Give your users the ability to search and filter through your events with the event search shortcode. Event Organiser Pro also adds a text editor button to make inserting and configuring your shorcodes that bit easier. ', 'eventorganiser' ),
77
+ 'eo-pro-event-search.png'
78
+ );
79
+ self::print_feature(
80
+ __( 'Venue custom fields & thumbnails', 'eventorganiser' ),
81
+ __( "Add action information on your venue pages with venue custom fields, or give your venues more attentioned with their own 'featured image'", 'eventorganiser' ),
82
+ 'eo-pro-venue-cf.png'
83
+ );
84
+ ?>
85
+
86
+ <div style="clear:both"></div>
87
+
88
+ <p>
89
+ <strong><a href="http://wp-event-organiser.com/pro-features"><?php _e('Find out more &hellip;', 'eventorganiser')?></a></strong>
90
+ </p>
91
+
92
+ <hr style="color:#CCC;background-color:#CCC;border:0;border-bottom:1px solid #CCC;">
93
+
94
+ <div class="return-to-dashboard">
95
+ <a href="<?php echo admin_url('options-general.php?page=event-settings');?>"><?php _e('Go to Event Organiser settings', 'eventorganiser');?></a>
96
+ </div>
97
+ </div>
98
+ </div><!-- .wrap -->
99
+ <?php
100
+ }
101
+
102
+ static function print_feature( $title, $content, $img ){
103
+ ?>
104
+ <div class="eo-feature-section images-stagger-right">
105
+ <img src="<?php echo EVENT_ORGANISER_URL.'css/images/'.$img?>">
106
+ <?php echo '<h4>'.$title.'</h4>'; ?>
107
+ <p> <?php echo $content; ?></p>
108
+ </div>
109
+ <?php
110
+ }
111
+
112
+ }
113
+ $calendar_page = new EventOrganiser_Pro_Page();
114
+
115
+ ?>
event-organiser-manage.php ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Functions altering the CPT Event table
4
+ *
5
+ * @since 1.0.0
6
+ */
7
+
8
+ /**
9
+ * Adds custom columns to Event CPT table
10
+ * @since 1.0.0
11
+ */
12
+ add_filter( 'manage_edit-event_columns', 'eventorganiser_event_add_columns' );
13
+ function eventorganiser_event_add_columns( $columns ) {
14
+
15
+ unset( $columns['date'] );//Unset unnecessary columns
16
+
17
+ //Set 'title' column title
18
+ $columns['title'] = __( 'Event', 'eventorganiser' );
19
+
20
+ //If displaying 'author', change title
21
+ if ( isset( $columns['author'] ) )
22
+ $columns['author'] = __( 'Organiser', 'eventorganiser' );
23
+
24
+ $columns['venue'] = __( 'Venue', 'eventorganiser' );
25
+ $columns['eventcategories'] = __( 'Categories' );
26
+ $columns['datestart'] = __( 'Start Date/Time', 'eventorganiser' );
27
+ $columns['dateend'] = __( 'End Date/Time', 'eventorganiser' );
28
+ $columns['reoccurence'] = __( 'Reoccurrence', 'eventorganiser' );
29
+
30
+ return $columns;
31
+ }
32
+
33
+
34
+ /**
35
+ * Registers the custom columns in Event CPT table to be sortable
36
+ * @since 1.0.0
37
+ */
38
+ add_filter( 'manage_edit-event_sortable_columns', 'eventorganiser_event_sortable_columns' );
39
+ function eventorganiser_event_sortable_columns( $columns ) {
40
+ $columns['datestart'] = 'eventstart';
41
+ $columns['dateend'] = 'eventend';
42
+ return $columns;
43
+ }
44
+
45
+
46
+ /**
47
+ * What to display in custom columns of Event CPT table
48
+ * @since 1.0.0
49
+ */
50
+ add_action( 'manage_event_posts_custom_column', 'eventorganiser_event_fill_columns', 10, 2 );
51
+ function eventorganiser_event_fill_columns( $column_name, $id ) {
52
+ global $post;
53
+
54
+ $series_id = ( empty( $post->event_id) ? $id :'' );
55
+
56
+ $phpFormat = 'M, j Y';
57
+ if ( !eo_is_all_day( $series_id ) )
58
+ $phpFormat .= '\<\/\b\r\>'. get_option( 'time_format' );
59
+
60
+ switch ( $column_name ) {
61
+ case 'venue':
62
+ $terms = get_the_terms( $post->ID, 'event-venue' );
63
+
64
+ if ( !empty( $terms) ) {
65
+ foreach ( $terms as $term )
66
+ $post_terms[] = '<a href="'. add_query_arg( 'event-venue', $term->slug ) .'">'.esc_html( sanitize_term_field( 'name', $term->name, $term->term_id, 'event-venue', 'display' ) ) . '</a>';
67
+ echo join( ', ', $post_terms );
68
+ echo '<input type="hidden" value="'.$term->term_id.'"/>';
69
+ }
70
+ break;
71
+
72
+ case 'datestart':
73
+ eo_the_start( $phpFormat, $series_id );
74
+ break;
75
+
76
+ case 'dateend':
77
+ eo_the_end( $phpFormat, $series_id );
78
+ break;
79
+
80
+ case 'reoccurence':
81
+ eo_display_reoccurence( $series_id );
82
+ break;
83
+
84
+ case 'eventcategories':
85
+ $terms = get_the_terms( $post->ID, 'event-category' );
86
+
87
+ if ( !empty( $terms) ) {
88
+ foreach ( $terms as $term )
89
+ $post_terms[] = '<a href=""'.add_query_arg( 'event-category', $term->slug ).'">'.esc_html( sanitize_term_field( 'name', $term->name, $term->term_id, 'event-category', 'display' ) ).'</a>';
90
+ echo join( ', ', $post_terms );
91
+ }
92
+ break;
93
+
94
+ default:
95
+ break;
96
+ } // end switch
97
+ }
98
+
99
+ /**
100
+ * Adds a drop-down filter to the Event CPT table by category
101
+ * @since 1.0.0
102
+ */
103
+ add_action( 'restrict_manage_posts', 'eventorganiser_restrict_events_by_category' );
104
+ function eventorganiser_restrict_events_by_category() {
105
+
106
+ // only display these taxonomy filters on desired custom post_type listings
107
+ global $typenow, $wp_query;
108
+ if ( $typenow == 'event' ) {
109
+ eo_event_category_dropdown( array( 'hide_empty' => false, 'show_option_all' => __( 'View all categories' ) ) );
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Adds a drop-down filter to the Event CPT table by venue
115
+ * @since 1.0.0
116
+ */
117
+ add_action( 'restrict_manage_posts', 'eventorganiser_restrict_events_by_venue' );
118
+ function eventorganiser_restrict_events_by_venue() {
119
+ global $typenow;
120
+
121
+ //Only add if CPT is event
122
+ if ( $typenow == 'event' ) :
123
+ eo_event_venue_dropdown( array( 'hide_empty' => false, 'show_option_all' => __( 'View all venues', 'eventorganiser' ) ) );
124
+ endif;
125
+ }
126
+
127
+ /**
128
+ * Adds a drop-down filter to the Event CPT table by intervals
129
+ * @since 1.2.0
130
+ */
131
+ add_action( 'restrict_manage_posts', 'eventorganiser_display_occurrences' );
132
+ function eventorganiser_display_occurrences() {
133
+ global $typenow, $wp_query;
134
+ if ( $typenow == 'event' ):
135
+ $intervals = array(
136
+ 'all' => __( 'View all events', 'eventorganiser' ),
137
+ 'future' => __( 'Future events', 'eventorganiser' ),
138
+ 'expired' => __( 'Expired events', 'eventorganiser' ),
139
+ 'P1D' => __( 'Events within 24 hours', 'eventorganiser' ),
140
+ 'P1W' => __( 'Events within 1 week', 'eventorganiser' ),
141
+ 'P2W' => sprintf( __( 'Events within %d weeks', 'eventorganiser' ), 2 ),
142
+ 'P1M' => __( 'Events within 1 month', 'eventorganiser' ),
143
+ 'P6M' => sprintf( __( 'Events within %d months', 'eventorganiser' ), 6 ),
144
+ 'P1Y' => __( 'Events within 1 year', 'eventorganiser' ),
145
+ );
146
+ $current = ( !empty( $wp_query->query_vars['eo_interval'] ) ? $wp_query->query_vars['eo_interval'] : 'all' );
147
+ ?>
148
+ <select style="width:150px;" name='eo_interval' id='show-events-in-interval' class='postform'>
149
+ <?php foreach ( $intervals as $id => $interval ): ?>
150
+ <option value="<?php echo $id; ?>" <?php selected( $current, $id )?>> <?php echo $interval;?> </option>
151
+ <?php endforeach; ?>
152
+ </select>
153
+ <?php
154
+ endif;//End if CPT is event
155
+ }
156
+
157
+
158
+ /*
159
+ * Bulk and quick editting of venues. Add drop-down menu for quick editing
160
+ * @Since 1.3
161
+ */
162
+ add_action( 'quick_edit_custom_box', 'eventorganiser_quick_edit_box', 10, 2 );
163
+ function eventorganiser_quick_edit_box( $column_name, $post_type ) {
164
+ if ( $column_name != 'venue' || $post_type != 'event' ) return;?>
165
+
166
+ <fieldset class="inline-edit-col-left"><div class="inline-edit-col">
167
+ <?php wp_nonce_field( 'eventorganiser_event_quick_edit', '_eononce' );?>
168
+ <label class="">
169
+ <span class="title">Event Venue</span><?php
170
+ $args = array( 'show_option_all' => 'No venue', 'orderby' => 'name', 'hide_empty' => 0, 'name' => 'eo_input[event-venue]', 'id' => 'eventorganiser_venue', 'taxonomy' => 'event-venue' );
171
+ wp_dropdown_categories( $args ); ?>
172
+ </label>
173
+ </div></fieldset>
174
+ <?php
175
+ }
176
+
177
+ /*
178
+ * Bulk and quick editting of venues. Add drop-down menu for bulk editing
179
+ * @Since 1.3
180
+ */
181
+ add_action( 'bulk_edit_custom_box', 'eventorganiser_bulk_edit_box', 10, 2 );
182
+ function eventorganiser_bulk_edit_box( $column_name, $post_type ) {
183
+ if ( $column_name != 'venue' || $post_type != 'event' ) return;?>
184
+
185
+ <fieldset class="inline-edit-col-left"><div class="inline-edit-col">
186
+ <?php wp_nonce_field( 'eventorganiser_event_quick_edit', '_eononce' );?>
187
+ <label class="">
188
+ <span class="title">Event Venue</span><?php
189
+ $args = array( 'show_option_none' => __( '&mdash; No Change &mdash;' ), 'orderby' => 'name', 'hide_empty' => 0, 'name' => 'eo_input[event-venue]', 'id' => 'eventorganiser_venue_bulk', 'taxonomy' => 'event-venue' );
190
+ wp_dropdown_categories( $args ); ?>
191
+ </label>
192
+ </div></fieldset>
193
+ <?php
194
+ }
195
+
196
+ /*
197
+ * Bulk and quick editting of venues. Save venue update.
198
+ * @Since 1.3
199
+ */
200
+ add_action( 'save_post', 'eventorganiser_quick_edit_save' );
201
+ function eventorganiser_quick_edit_save( $post_id ) {
202
+ global $wpdb;
203
+
204
+ // verify this is not an auto save routine.
205
+ if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return $post_id;
206
+
207
+ //make sure data came from our quick/bulk box
208
+ if ( !isset( $_REQUEST['_eononce'] ) || !wp_verify_nonce( $_REQUEST['_eononce'], 'eventorganiser_event_quick_edit' ) )
209
+ return;
210
+
211
+ //authentication checks
212
+ if ( !current_user_can( 'edit_event', $post_id ) ) return $post_id;
213
+
214
+ $venue_id = ( isset( $_REQUEST['eo_input']['event-venue'] ) ? (int) $_REQUEST['eo_input']['event-venue'] : - 1 );
215
+
216
+ if ( $venue_id >= 0 ) {
217
+ $r = wp_set_post_terms( $post_id, array( $venue_id ), 'event-venue', false );
218
+ }
219
+
220
+ do_action( 'eventorganiser_save_event', $post_id );
221
+ return;
222
+ }
223
+
224
+
225
+ add_action( 'admin_head-edit.php', 'eventorganiser_quick_edit_script' );
226
+ function eventorganiser_quick_edit_script() { ?>
227
+ <script type="text/javascript">
228
+ jQuery(document).ready(function() {
229
+ jQuery( 'a.editinline' ).live( 'click', function() {
230
+ jQuery( '#eventorganiser_venue option' ).attr("selected", false);
231
+ var id = inlineEditPost.getId(this);
232
+ var val = parseInt(jQuery( '#post-' + id + ' td.column-venue input' ).val() );
233
+ jQuery( '#eventorganiser_venue option[value="'+val+'"]' ).attr( 'selected', 'selected' );
234
+ });
235
+ });
236
+ </script>
237
+ <?php
238
+ }
239
+
240
+ ?>
event-organiser-settings.php ADDED
@@ -0,0 +1,562 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /****** SETTINGS PAGE ******/
3
+ if ( !class_exists( 'EventOrganiser_Admin_Page' ) ){
4
+ require_once(EVENT_ORGANISER_DIR.'classes/class-eventorganiser-admin-page.php' );
5
+ }
6
+ class EventOrganiser_Settings_Page extends EventOrganiser_Admin_Page{
7
+
8
+ static $eventorganiser_roles;
9
+
10
+ /**
11
+ * Initialises the tabs.
12
+ */
13
+ function setup_tabs(){
14
+ return apply_filters( 'eventorganiser_settings_tabs', array(
15
+ 'general' => __( 'General', 'eventorganiser' ),
16
+ 'permissions' => __( 'Permissions', 'eventorganiser' ),
17
+ 'permalinks' => __( 'Permalinks', 'eventorganiser' ),
18
+ 'imexport' => __( 'Import', 'eventorganiser' ).'/'.__( 'Export', 'eventorganiser' ),
19
+ )
20
+ );
21
+ }
22
+
23
+ function set_constants(){
24
+ $this->hook = 'options-general.php';
25
+ $this->title = __( 'Event Organiser Settings', 'eventorganiser' );
26
+ $this->menu = __( 'Event Organiser', 'eventorganiser' );
27
+ $this->permissions = 'manage_options';
28
+ $this->slug = 'event-settings';
29
+ }
30
+
31
+ function admin_init_actions(){
32
+ //Register options
33
+ register_setting( 'eventorganiser_options', 'eventorganiser_options', array( $this, 'validate' ) );
34
+
35
+ add_action( 'eventorganiser_event_settings_permalinks', 'flush_rewrite_rules' );
36
+
37
+ //Initialise the tab array
38
+ $this->tabs = $this->setup_tabs();
39
+
40
+ foreach ( $this->tabs as $tab_id => $label ){
41
+ //Add sections to each tabbed page
42
+ switch ( $tab_id){
43
+ case 'general':
44
+ register_setting( 'eventorganiser_'.$tab_id, 'eventorganiser_options', array( $this, 'validate' ) );
45
+ add_settings_section( $tab_id,__( 'General', 'eventorganiser' ), '__return_false', 'eventorganiser_'.$tab_id);
46
+ add_settings_section( $tab_id.'_templates',__( 'Templates', 'eventorganiser' ), '__return_false', 'eventorganiser_'.$tab_id);
47
+ break;
48
+ case 'permissions':
49
+ register_setting( 'eventorganiser_'.$tab_id, 'eventorganiser_options', array( $this, 'validate' ) );
50
+ add_settings_section( $tab_id, '',array( $this, 'display_permissions' ), 'eventorganiser_'.$tab_id);
51
+ break;
52
+ case 'permalinks':
53
+ register_setting( 'eventorganiser_'.$tab_id, 'eventorganiser_options', array( $this, 'validate' ) );
54
+ add_settings_section( $tab_id, '',array( $this, 'display_permalinks' ), 'eventorganiser_'.$tab_id);
55
+ break;
56
+ case 'imexport':
57
+ register_setting( 'eventorganiser_'.$tab_id, 'eventorganiser_options', array( $this, 'validate' ) );
58
+ add_settings_section( $tab_id, '',array( $this, 'display_imexport' ), 'eventorganiser_'.$tab_id );
59
+ break;
60
+ }
61
+ do_action("eventorganiser_register_tab_{$tab_id}", $tab_id );
62
+ $this->add_fields( $tab_id );
63
+ }
64
+
65
+ self::$eventorganiser_roles = array(
66
+ 'edit_events' => __( 'Edit Events', 'eventorganiser' ),
67
+ 'publish_events' => __( 'Publish Events', 'eventorganiser' ),
68
+ 'delete_events' => __( 'Delete Events', 'eventorganiser' ),
69
+ 'edit_others_events' => __( 'Edit Others\' Events', 'eventorganiser' ),
70
+ 'delete_others_events' => __( 'Delete Other\'s Events', 'eventorganiser' ),
71
+ 'read_private_events' => __( 'Read Private Events', 'eventorganiser' ),
72
+ 'manage_venues' => __( 'Manage Venues', 'eventorganiser' ),
73
+ 'manage_event_categories' => __( 'Manage Event Categories & Tags', 'eventorganiser' ),
74
+ );
75
+ }
76
+
77
+
78
+ /**
79
+ * This is called in the register_settings method.
80
+ * Once the tabs have been registered, and sections added to each tabbed page, we now add the fields for each section
81
+ * A section should have the form {tab_id}._{identifer} (e.g. general_main, or gateways_google).
82
+ *
83
+ * @param $tab_id - the string identifer for the tab (given in $this->tabs as the key).
84
+ * @uses add_settings_field
85
+ */
86
+ function add_fields( $tab_id){
87
+
88
+ switch( $tab_id){
89
+ case 'general':
90
+ /* General - main */
91
+ add_settings_field( 'supports', __( 'Select which features events should support', 'eventorganiser' ), 'eventorganiser_checkbox_field', 'eventorganiser_'.$tab_id, $tab_id,
92
+ array(
93
+ 'label_for' => 'supports',
94
+ 'checked' => eventorganiser_get_option( 'supports' ),
95
+ 'options' => array(
96
+ 'author' => __( 'Organiser', 'eventorganiser' ).' ( '.__( 'Author' ).' )',
97
+ 'thumbnail' => __( 'Thumbnail' ),
98
+ 'excerpt' => __( 'Excerpt' ),
99
+ 'custom-fields' => __( 'Custom Fields' ),
100
+ 'comments' => __( 'Comments' ),
101
+ 'revisions' => __( 'Revisions' ),
102
+ 'eventtag' => __( 'Event Tags', 'eventorganiser' ),
103
+ ),
104
+ 'name' => 'eventorganiser_options[supports]'
105
+ ) );
106
+
107
+ add_settings_field( 'showpast', __("Show past events:", 'eventorganiser' ), 'eventorganiser_checkbox_field' , 'eventorganiser_'.$tab_id, $tab_id,
108
+ array(
109
+ 'label_for' => 'showpast',
110
+ 'name' => 'eventorganiser_options[showpast]',
111
+ 'options' => 1,
112
+ 'checked' => eventorganiser_get_option( 'showpast' ),
113
+ 'help' => __("Display past events on calendars, event lists and archives (this can be over-ridden by shortcode attributes and widget options).", 'eventorganiser' )
114
+ ) );
115
+
116
+ add_settings_field( 'addtomenu', __("Add an 'events' link to the navigation menu:", 'eventorganiser' ), array( $this, 'menu_option' ), 'eventorganiser_'.$tab_id, $tab_id,
117
+ array(
118
+ 'label_for' => 'addtomenu',
119
+ ) );
120
+
121
+ add_settings_field( 'dateformat', __( 'Date Format:', 'eventorganiser' ), 'eventorganiser_select_field' , 'eventorganiser_'.$tab_id, $tab_id,
122
+ array(
123
+ 'label_for' => 'dateformat',
124
+ 'selected' => eventorganiser_get_option( 'dateformat' ),
125
+ 'name' => 'eventorganiser_options[dateformat]',
126
+ 'options' => array(
127
+ 'd-m-Y' => __( 'dd-mm-yyyy', 'eventorganiser' ),
128
+ 'm-d-Y' => __( 'mm-dd-yyyy', 'eventorganiser' ),
129
+ 'Y-m-d' => __( 'yyyy-mm-dd', 'eventorganiser' ),
130
+ ),
131
+ 'help' => __("This alters the default format for inputting dates.", 'eventorganiser' ),
132
+ ) );
133
+
134
+ add_settings_field( 'showpast', __("Show past events:", 'eventorganiser' ), 'eventorganiser_checkbox_field' , 'eventorganiser_'.$tab_id, $tab_id,
135
+ array(
136
+ 'label_for' => 'showpast',
137
+ 'name' => 'eventorganiser_options[showpast]',
138
+ 'options' => 1,
139
+ 'checked' => eventorganiser_get_option( 'showpast' ),
140
+ 'help' => __("Display past events on calendars, event lists and archives (this can be over-ridden by shortcode attributes and widget options).", 'eventorganiser' )
141
+ ) );
142
+
143
+ add_settings_field( 'group_events', __("Group occurrences", 'eventorganiser' ), 'eventorganiser_checkbox_field' , 'eventorganiser_'.$tab_id, $tab_id,
144
+ array(
145
+ 'label_for' => 'group_events',
146
+ 'name' => 'eventorganiser_options[group_events]',
147
+ 'options' => 'series',
148
+ 'checked' => eventorganiser_get_option( 'group_events' ),
149
+ 'help' => __("If selected only one occurrence of an event will be displayed on event lists and archives (this can be over-ridden by shortcode attributes and widget options.", 'eventorganiser' )
150
+ ) );
151
+
152
+ add_settings_field( 'runningisnotpast', __("Are current events past?", 'eventorganiser' ), 'eventorganiser_select_field' , 'eventorganiser_'.$tab_id, $tab_id,
153
+ array(
154
+ 'label_for' => 'runningisnotpast',
155
+ 'name' => 'eventorganiser_options[runningisnotpast]',
156
+ 'selected' => eventorganiser_get_option( 'runningisnotpast',0),
157
+ 'options' => array(
158
+ '0' => __( 'No', 'eventorganiser' ),
159
+ '1' => __( 'Yes', 'eventorganiser' ),
160
+ ),
161
+ 'help' => __("If 'no' is selected, an occurrence of an event is only past when it has finished. Otherwise, an occurrence is considered 'past' as soon as it starts.", 'eventorganiser' ),
162
+ ) );
163
+
164
+ add_settings_field( 'deleteexpired', __("Delete expired events:", 'eventorganiser' ), 'eventorganiser_checkbox_field' , 'eventorganiser_'.$tab_id, $tab_id,
165
+ array(
166
+ 'label_for' => 'deleteexpired',
167
+ 'name' => 'eventorganiser_options[deleteexpired]',
168
+ 'options' => 1,
169
+ 'checked' => eventorganiser_get_option( 'deleteexpired' ),
170
+ 'help' => __("If selected the event will be automatically trashed 24 hours after the last occurrence finishes.", 'eventorganiser' )
171
+ ) );
172
+
173
+ add_settings_field( 'feed', __("Enable events ICAL feed:", 'eventorganiser' ), 'eventorganiser_checkbox_field' , 'eventorganiser_'.$tab_id, $tab_id,
174
+ array(
175
+ 'label_for' => 'feed',
176
+ 'name' => 'eventorganiser_options[feed]',
177
+ 'options' => 1,
178
+ 'checked' => eventorganiser_get_option( 'feed' ),
179
+ 'help' => sprintf(__( 'If selected, visitors can subscribe to your events with the url: %s', 'eventorganiser' ), '<code>'.eo_get_events_feed().'</code>' )
180
+ ) );
181
+
182
+ add_settings_field( 'excludefromsearch', __("Exclude events from searches:", 'eventorganiser' ), 'eventorganiser_checkbox_field' , 'eventorganiser_'.$tab_id, $tab_id,
183
+ array(
184
+ 'label_for' => 'excludefromsearch',
185
+ 'name' => 'eventorganiser_options[excludefromsearch]',
186
+ 'options' => 1,
187
+ 'checked' => eventorganiser_get_option( 'excludefromsearch' ),
188
+ ) );
189
+
190
+ add_settings_field( 'templates', __("Enable templates:", 'eventorganiser' ), 'eventorganiser_checkbox_field' , 'eventorganiser_'.$tab_id, $tab_id.'_templates',
191
+ array(
192
+ 'label_for' => 'templates',
193
+ 'name' => 'eventorganiser_options[templates]',
194
+ 'options' => 1,
195
+ 'checked' => eventorganiser_get_option( 'templates' ),
196
+ 'help' => __("For each of the pages, the corresponding template is used. To use your own template simply give it the same name and store in your theme folder. By default, if Event Organiser cannot find a template in your theme directory, it will use its own default template. To prevent this, uncheck this option. WordPress will then decide which template from your theme's folder to use.", 'eventorganiser' ). sprintf("<p><strong> %s </strong><code>archive-event.php</code></p>
197
+ <p><strong> %s </strong><code>single-event.php</code></p>
198
+ <p><strong> %s </strong><code>venue-template.php</code></p>
199
+ <p><strong> %s </strong><code>taxonomy-event-category.php</code></p>",
200
+ __("Events archives:", 'eventorganiser' ),
201
+ __("Event page:", 'eventorganiser' ),
202
+ __("Venue page:", 'eventorganiser' ),
203
+ __("Events Category page:", 'eventorganiser' )
204
+ )
205
+ ) );
206
+ break;
207
+
208
+
209
+ case 'permissions':
210
+ break;
211
+
212
+ case 'permalinks':
213
+ add_settings_field( 'prettyurl', __("Enable event pretty permalinks:", 'eventorganiser' ), 'eventorganiser_checkbox_field' , 'eventorganiser_'.$tab_id, $tab_id,
214
+ array(
215
+ 'label_for' => 'prettyurl',
216
+ 'name' => 'eventorganiser_options[prettyurl]',
217
+ 'options' => 1,
218
+ 'checked' => eventorganiser_get_option( 'prettyurl' ),
219
+ 'help' => __("If you have pretty permalinks enabled, select to have pretty premalinks for events.", 'eventorganiser' )
220
+ ) );
221
+
222
+ $home_url = home_url();
223
+ add_settings_field( 'url_event', __("Event (single)", 'eventorganiser' ), 'eventorganiser_text_field' , 'eventorganiser_'.$tab_id, $tab_id,
224
+ array(
225
+ 'label_for' => 'url_event',
226
+ 'name' => 'eventorganiser_options[url_event]',
227
+ 'value' => eventorganiser_get_option( 'url_event' ),
228
+ 'help' => "<code>{$home_url}/<strong>".eventorganiser_get_option( 'url_event' )."</strong>/[event_slug]</code>"
229
+ ) );
230
+
231
+ $now = new DateTime();
232
+ add_settings_field( 'url_events', __("Event (archive)", 'eventorganiser' ), 'eventorganiser_text_field' , 'eventorganiser_'.$tab_id, $tab_id,
233
+ array(
234
+ 'label_for' => 'url_events',
235
+ 'name' => 'eventorganiser_options[url_events]',
236
+ 'value' => eventorganiser_get_option( 'url_events' ),
237
+ 'help' => "<code>{$home_url}/<strong>".eventorganiser_get_option( 'url_events' )."</strong></code>".'<br>'
238
+ ."<code>{$home_url}/<strong>".eventorganiser_get_option( 'url_events' )."</strong>/on/{$now->format('Y')}</code> ".__('Year archive', 'eventorganiser').'<br>'
239
+ ."<code>{$home_url}/<strong>".eventorganiser_get_option( 'url_events' )."</strong>/on/{$now->format('Y/m')}</code>".__('Month archive', 'eventorganiser').'<br>'
240
+ ."<code>{$home_url}/<strong>".eventorganiser_get_option( 'url_events' )."</strong>/on/{$now->format('Y/m/d')}</code>".__('Day archive', 'eventorganiser')
241
+ ) );
242
+
243
+ add_settings_field( 'url_venue', __("Venues", 'eventorganiser' ), 'eventorganiser_text_field' , 'eventorganiser_'.$tab_id, $tab_id,
244
+ array(
245
+ 'label_for' => 'url_venue',
246
+ 'name' => 'eventorganiser_options[url_venue]',
247
+ 'value' => eventorganiser_get_option( 'url_venue' ),
248
+ 'help' => "<code>{$home_url}/<strong>".eventorganiser_get_option( 'url_venue' )."</strong>/[venue_slug]</code>"
249
+ ) );
250
+
251
+ add_settings_field( 'url_cat', __("Event Categories", 'eventorganiser' ), 'eventorganiser_text_field' , 'eventorganiser_'.$tab_id, $tab_id,
252
+ array(
253
+ 'label_for' => 'url_cat',
254
+ 'name' => 'eventorganiser_options[url_cat]',
255
+ 'value' => eventorganiser_get_option( 'url_cat' ),
256
+ 'help' => "<code>{$home_url}/<strong>".eventorganiser_get_option( 'url_cat' )."</strong>/[event_cat_slug]</code>"
257
+ ) );
258
+
259
+ add_settings_field( 'url_tag', __("Event Tags", 'eventorganiser' ), 'eventorganiser_text_field' , 'eventorganiser_'.$tab_id, $tab_id,
260
+ array(
261
+ 'label_for' => 'url_tag',
262
+ 'name' => 'eventorganiser_options[url_tag]',
263
+ 'value' => eventorganiser_get_option( 'url_tag' ),
264
+ 'help' => "<label><code>{$home_url}/<strong>".eventorganiser_get_option( 'url_tag' )."</strong>/[event_tag_slug]</code></label>"
265
+ ) );
266
+
267
+ break;
268
+ }
269
+ }
270
+
271
+
272
+ function validate( $option ){
273
+ /* Backwards compatibility: all EO options are in one row, but on seperate pages. Merge with existing options*/
274
+
275
+ if ( isset( $option['tab'] ) ){
276
+ $tab = $option['tab'];
277
+ unset( $option['tab'] );
278
+ } else {
279
+ $tab = false;
280
+ }
281
+
282
+ $clean = array();
283
+
284
+ switch ( $tab ){
285
+ case 'general':
286
+ $checkboxes = array( 'showpast', 'templates', 'excludefromsearch', 'deleteexpired', 'feed', 'group_events' );
287
+ $text = array( 'navtitle', 'dateformat', 'runningisnotpast', 'addtomenu' );
288
+
289
+ foreach ( $checkboxes as $cb ){
290
+
291
+ //Empty checkboxes send no data..
292
+ $value = isset( $option[$cb] ) ? $option[$cb] : 0;
293
+
294
+ $clean[$cb] = $this->validate_checkbox( $value );
295
+ }
296
+
297
+ foreach ( $text as $txt ){
298
+ if ( !isset( $option[$txt] ) )
299
+ continue;
300
+
301
+ $clean[$txt] = $this->validate_text( $option[$txt] );
302
+ }
303
+
304
+ //Group events is handled differently
305
+ $clean['group_events'] = ( !empty( $clean['group_events'] ) ? 'series' : '' );
306
+
307
+ //Post type supports
308
+ $clean['supports'] = (isset( $option['supports'] ) ? array_map( 'esc_html',$option['supports'] ) : array() );
309
+ $clean['supports'] = array_merge( $clean['supports'],array( 'title', 'editor' ) );
310
+
311
+ //Navigation menu - $addtomenu int 0 if no menu, menu databse ID otherwise
312
+ $clean['menu_item_db_id'] = $this->update_nav_menu( $clean['addtomenu'], $clean['navtitle'] );
313
+
314
+ if( $clean['deleteexpired'] && !eventorganiser_get_next_cron_time( 'eventorganiser_delete_expired' ) ){
315
+ eventorganiser_cron_jobs();
316
+ }elseif( !$clean['deleteexpired'] ){
317
+ eventorganiser_clear_cron_jobs();
318
+ }
319
+ break;
320
+
321
+
322
+ case 'permalinks':
323
+ $permalinks = array( 'url_event', 'url_events', 'url_venue', 'url_cat', 'url_tag' );
324
+
325
+ foreach ( $permalinks as $permalink ){
326
+ if ( !isset( $option[$permalink] ) )
327
+ continue;
328
+
329
+ $value = $this->validate_permalink( $option[$permalink] );
330
+
331
+ if ( !empty( $value) )
332
+ $clean[$permalink] = $value;
333
+ }
334
+
335
+ $clean['prettyurl'] = isset( $option['prettyurl'] ) ? $this->validate_checkbox( $option['prettyurl'] ) : 0;
336
+ break;
337
+
338
+
339
+ case 'permissions':
340
+ //Permissions
341
+ $permissions = (isset( $option['permissions'] ) ? $option['permissions'] : array() );
342
+ $this->update_roles( $permissions );
343
+ break;
344
+ }
345
+
346
+ $existing_options = get_option( 'eventorganiser_options', array() );
347
+ $clean = array_merge( $existing_options, $clean );
348
+ return $clean;
349
+ }
350
+
351
+ function validate_checkbox( $value ){
352
+ return ( !empty( $value ) ? 1 : 0 );
353
+ }
354
+
355
+ function validate_text( $value ){
356
+ return ( !empty( $value ) ? esc_html( $value ) : false );
357
+ }
358
+
359
+ function validate_permalink( $value ){
360
+ return trim( str_replace( 'http://', '', esc_url_raw( $value ) ), '/' );
361
+ return $value;
362
+ }
363
+
364
+ /**
365
+ *
366
+ *@param $menu_databse_id int 0 for no menu, 1 for 'fallback', term ID for menu otherwise
367
+ *
368
+ */
369
+ function update_nav_menu( $menu_id, $menu_item_title ){
370
+
371
+ //Get existing menu item
372
+ $menu_item_db_id = (int) eventorganiser_get_option( 'menu_item_db_id' );
373
+
374
+ //Validate exiting menu item ID
375
+ if ( !is_nav_menu_item( $menu_item_db_id ) ){
376
+ $menu_item_db_id = 0;
377
+ }
378
+
379
+ //If Menu is not selected, or 'page list' fallback is, and we have an 'events' item added to some menu, remove it
380
+ if ( (empty( $menu_id ) || $menu_id == '1' ) && is_nav_menu_item( $menu_item_db_id ) ){
381
+ //Remove menu item
382
+ wp_delete_post( $menu_item_db_id, true );
383
+ $menu_item_db_id = 0;
384
+ }
385
+
386
+ //If the $menu is an int > 1, we are adding/updating an item (post type) so that it has term set to $menu_id
387
+ if ( ( !empty( $menu_id ) && $menu_id != '1' ) ){
388
+ $menu_item_data = array();
389
+
390
+ //Validate menu ID
391
+ $menu_obj = wp_get_nav_menu_object( $menu_id );
392
+ $menu_id = ( $menu_obj ? $menu_obj->term_id : 0 );
393
+
394
+ //Set status
395
+ $status = ( $menu_id == 0 ? '' : 'publish' );
396
+
397
+ $menu_item_data = array(
398
+ 'menu-item-title' => $menu_item_title,
399
+ 'menu-item-url' => get_post_type_archive_link( 'event' ),
400
+ 'menu-item-object' => 'event',
401
+ 'menu-item-status' => $status,
402
+ 'menu-item-type' => 'post_type_archive',
403
+ );
404
+
405
+ //If we're updating preserve parent and position
406
+ if( is_nav_menu_item( $menu_item_db_id ) ){
407
+ $menu_item = wp_setup_nav_menu_item( get_post( $menu_item_db_id ) );
408
+ $menu_item_data += array(
409
+ 'menu-item-parent-id' => $menu_item->menu_item_parent,
410
+ 'menu-item-position' => $menu_item->menu_order,
411
+ );
412
+ }
413
+
414
+ //Update menu item (post type) to have taxonom term $menu_id
415
+ $menu_item_db_id = wp_update_nav_menu_item( $menu_id, $menu_item_db_id,$menu_item_data );
416
+ }
417
+
418
+ //Return the menu item (post type) ID. 0 For no item added, or item removed.
419
+ return $menu_item_db_id;
420
+ }
421
+
422
+
423
+ function update_roles( $permissions ){
424
+ global $wp_roles,$EO_Errors;
425
+
426
+ foreach ( get_editable_roles() as $role_name => $display_name ):
427
+ $role = $wp_roles->get_role( $role_name );
428
+ //Don't edit the administrator
429
+ if ( $role_name == 'administrator' )
430
+ continue;
431
+
432
+ //Foreach custom role, add or remove option.
433
+ foreach ( self::$eventorganiser_roles as $eo_role => $eo_role_display ):
434
+ if ( isset( $permissions[$role_name][$eo_role] ) && $permissions[$role_name][$eo_role] == 1 ){
435
+ $role->add_cap( $eo_role );
436
+ } else {
437
+ $role->remove_cap( $eo_role );
438
+ }
439
+ endforeach; //End foreach $eventRoles
440
+ endforeach; //End foreach $editable_roles
441
+ }
442
+
443
+
444
+
445
+ function display(){
446
+ ?>
447
+ <div class="wrap">
448
+
449
+ <?php screen_icon( 'options-general' ); ?>
450
+ <?php
451
+
452
+ $active_tab = ( isset( $_GET[ 'tab' ] ) && isset( $this->tabs[$_GET[ 'tab' ]] ) ? $_GET[ 'tab' ] : 'general' );
453
+ $page = $this->slug;
454
+
455
+ echo '<h2 class="nav-tab-wrapper">';
456
+
457
+ foreach ( $this->tabs as $tab_id => $label ){
458
+ printf(
459
+ '<a href="?page=%s&tab=%s" class="nav-tab %s">%s</a>',
460
+ $page,
461
+ $tab_id,
462
+ ( $active_tab == $tab_id ? 'nav-tab-active' : '' ),
463
+ esc_html( $label )
464
+ );
465
+ }
466
+ echo '</h2>';
467
+ ?>
468
+
469
+ <?php if ( 'imexport' != $active_tab ){
470
+ echo '<form method="post" action="options.php">';
471
+ settings_fields( 'eventorganiser_'.$active_tab );
472
+ do_settings_sections( 'eventorganiser_'.$active_tab );
473
+ //Tab identifier - so we know which tab we are validating. See $this->validate().
474
+ do_action( 'eventorganiser_event_settings_'.$active_tab );
475
+ printf( '<input type="hidden" name="eventorganiser_options[tab]" value="%s" />', esc_attr( $active_tab ) );
476
+ submit_button();
477
+ echo '</form>';
478
+ } else {
479
+ do_action( 'eventorganiser_event_settings_imexport' );
480
+ }
481
+ ?>
482
+
483
+ </div><!-- /.wrap -->
484
+
485
+ <?php
486
+ }
487
+
488
+ function display_imexport(){
489
+ do_action( 'eventorganiser_event_settings_imexport' );
490
+ }
491
+
492
+ function display_permissions(){
493
+ global $wp_roles;
494
+ echo '<p>'.__( 'Set permissions for events and venue management', 'eventorganiser' ).'</p>';
495
+ ?>
496
+ <table class="widefat fixed posts">
497
+ <thead>
498
+ <tr>
499
+ <th><?php _e( 'Role', 'eventorganiser' ); ?></th>
500
+ <?php foreach ( self::$eventorganiser_roles as $eo_role => $eo_role_display ): ?>
501
+ <th><?php echo esc_html( $eo_role_display );?></th>
502
+ <?php endforeach; ?>
503
+ </tr>
504
+ </thead>
505
+ <tbody id="the-list">
506
+ <?php
507
+ $array_index = 0;
508
+ foreach ( get_editable_roles() as $role_name => $display_name ):
509
+ $role = $wp_roles->get_role( $role_name );
510
+ $role_name = isset( $wp_roles->role_names[$role_name] ) ? translate_user_role( $wp_roles->role_names[$role_name] ) : __( 'None' );
511
+
512
+ printf( '<tr %s>', $array_index == 0 ? 'class="alternate"' : '' );
513
+ printf( '<td> %s </td>',esc_html( $role_name ) );
514
+
515
+ foreach ( self::$eventorganiser_roles as $eo_role => $eo_role_display ):
516
+ printf(
517
+ '<td>
518
+ <input type="checkbox" name="eventorganiser_options[permissions][%s][%s]" value="1" %s %s />
519
+ </td>',
520
+ esc_attr( $role->name ),
521
+ esc_attr( $eo_role ),
522
+ checked( '1', $role->has_cap( $eo_role ), false ),
523
+ disabled( $role->name, 'administrator', false )
524
+ );
525
+ endforeach; //End foreach $eventRoles
526
+ echo '</tr>';
527
+
528
+ $array_index = ( $array_index + 1) % 2;
529
+ endforeach; //End foreach $editable_role ?>
530
+ </tbody>
531
+ </table>
532
+ <?php
533
+ }
534
+
535
+
536
+ function display_permalinks(){
537
+ printf(
538
+ '<p> %s </p> <p> %s %s </p>',
539
+ esc_html__( 'Choose a custom permalink structure for events, venues, event categories and event tags.', 'eventorganiser' ),
540
+ esc_html__( 'Please note to enable these structures you must first have pretty permalinks enabled on WordPress in Settings > Permalinks.', 'eventorganiser' ),
541
+ esc_html__( "You may also need to go to WordPress Settings > Permalinks and click 'Save Changes' before any changes will take effect.", 'eventorganiser' )
542
+ );
543
+ }
544
+
545
+
546
+ function menu_option(){
547
+
548
+ $menus = get_terms( 'nav_menu' );?>
549
+ <select name="eventorganiser_options[addtomenu]">
550
+ <option <?php selected( 0,eventorganiser_get_option( 'addtomenu' ) );?> value="0"><?php _e( 'Do not add to menu', 'eventorganiser' ); ?> </option>
551
+ <?php foreach ( $menus as $menu ): ?>
552
+ <option <?php selected( $menu->slug,eventorganiser_get_option( 'addtomenu' ) );?> value="<?php echo $menu->slug; ?>"><?php echo $menu->name;?> </option>
553
+ <?php endforeach; ?>
554
+ <option <?php selected( 1, eventorganiser_get_option( 'addtomenu' ) );?> value="1"><?php _e( 'Page list (fallback)', 'eventorganiser' ); ?></option>
555
+ </select>
556
+
557
+ <?php printf( '<input type="hidden" name ="eventorganiser_options[menu_item_db_id]" value="%d" />',eventorganiser_get_option( 'menu_item_db_id' ) ); ?>
558
+ <?php printf( '<input type="text" name="eventorganiser_options[navtitle]" value="%s" />',eventorganiser_get_option( 'navtitle' ) );
559
+ }
560
+ }
561
+ $settings_page = new EventOrganiser_Settings_Page();
562
+ ?>
event-organiser-venues.php ADDED
@@ -0,0 +1,433 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /****** VENUE PAGE ******/
3
+ if ( !class_exists( 'EventOrganiser_Admin_Page' ) ){
4
+ require_once( EVENT_ORGANISER_DIR.'classes/class-eventorganiser-admin-page.php' );
5
+ }
6
+ class EventOrganiser_Venues_Page extends EventOrganiser_Admin_Page
7
+ {
8
+ function set_constants(){
9
+ $this->hook = 'edit.php?post_type=event';
10
+ $this->title = __( 'Venues', 'eventorganiser' );
11
+ $this->menu = __( 'Venues', 'eventorganiser' );
12
+ $this->permissions = 'manage_venues';
13
+ $this->slug = 'venues';
14
+
15
+ //Workaround for bug http://core.trac.wordpress.org/ticket/18958
16
+ add_filter( 'set-screen-option', array( $this, 'set_per_page' ),10,3 );
17
+ }
18
+
19
+ /*
20
+ * Actions to be taken prior to page loading. Hooked on to load-{page}
21
+ */
22
+ function page_actions(){
23
+
24
+ global $EO_Errors;
25
+ add_action( 'admin_notices',array( $this, 'admin_notices' ) );
26
+
27
+ //Determine action if any
28
+ $action = $this->current_action();
29
+ $venue = ( isset( $_REQUEST['event-venue'] ) ? $_REQUEST['event-venue'] : false );
30
+
31
+ if ( ( $action && $venue ) || $action == 'add' ):
32
+
33
+ if ( !current_user_can( 'manage_venues' ) )
34
+ wp_die( __( 'You do not have permission to manage venues', 'eventorganiser' ) );
35
+
36
+ switch( $action ):
37
+ case 'update':
38
+ if( !check_admin_referer( 'eventorganiser_update_venue_'.$venue ) )
39
+ wp_die( __( 'You do not have permission to edit this venue.', 'eventorganiser' ) );
40
+
41
+ $venue = get_term_by( 'slug', esc_attr( $venue ), 'event-venue' );
42
+
43
+ $return = eo_update_venue( $venue->term_id, $_POST['eo_venue'] );
44
+
45
+ if ( is_wp_error( $return ) ){
46
+ $EO_Errors->add( 'eo_error', __( 'Venue <strong>was not</strong> updated', 'eventorganiser' ).': '.$return->get_error_message() );
47
+ } else{
48
+ $term_id = (int) $return['term_id'];
49
+ $venue = get_term( $term_id, 'event-venue' );
50
+ $url = add_query_arg(
51
+ array(
52
+ 'page' => 'venues',
53
+ 'action' => 'edit',
54
+ 'event-venue' => $venue->slug,
55
+ 'message' => 2,
56
+ ),
57
+ admin_url( 'edit.php?post_type=event' )
58
+ );
59
+
60
+ wp_redirect( $url );
61
+ exit();
62
+ }
63
+ break;
64
+
65
+
66
+ case 'add':
67
+ if( !check_admin_referer( 'eventorganiser_add_venue' ) )
68
+ wp_die( __( 'You do not have permission to edit this venue.', 'eventorganiser' ) );
69
+
70
+ $args = $_POST['eo_venue'];
71
+ $name = isset( $args['name'] ) ? $args['name'] : '';
72
+
73
+ //Venue may already exist in database since it may have been added via ajax (by pro plug-in);
74
+ if ( !empty( $args['venue_id'] ) && $_venue = eo_get_venue_by( 'id', $args['venue_id'] ) ){
75
+ //Since we're updating, need to explicitly provide slug to update it. Slug will be 'new-venue'.
76
+ $args['slug'] = sanitize_title( $args['name'] );
77
+ $return = eo_update_venue( $_venue->term_id, $args );
78
+ } else{
79
+ $return = eo_insert_venue( $name, $args );
80
+ }
81
+
82
+ if ( is_wp_error( $return ) ){
83
+ $EO_Errors->add( 'eo_error', __( 'Venue <strong>was not</strong> created', 'eventorganiser' ).': '.$return->get_error_message() );
84
+ $_REQUEST['action'] = 'create';
85
+ } else{
86
+ $term_id = (int) $return['term_id'];
87
+ $venue = get_term( $term_id, 'event-venue' );
88
+
89
+ $url = add_query_arg(
90
+ array(
91
+ 'page' => 'venues',
92
+ 'action' => 'edit',
93
+ 'event-venue' => $venue->slug,
94
+ 'message' => 1,
95
+ ),
96
+ admin_url( 'edit.php?post_type=event' )
97
+ );
98
+
99
+ wp_redirect( $url );
100
+ exit();
101
+ }
102
+ break;
103
+
104
+
105
+ case 'delete':
106
+ if( is_array( $_REQUEST['event-venue'] ) )
107
+ $nonce = 'bulk-venues';
108
+ else
109
+ $nonce = 'eventorganiser_delete_venue_'.$venue;
110
+
111
+ if( !check_admin_referer( $nonce ) )
112
+ wp_die( __( 'You do not have permission to delete this venue', 'eventorganiser' ) );
113
+
114
+ $venues = (array) $venue;
115
+
116
+ //Count the number of deleted venues
117
+ $deleted = 0;
118
+
119
+ foreach ( $venues as $venue ):
120
+ $venue = get_term_by( 'slug',esc_attr( $venue ), 'event-venue' );
121
+ $resp = eo_delete_venue( $venue->term_id );
122
+
123
+ if ( !is_wp_error( $resp) && true === $resp ){
124
+ $deleted++;
125
+ }
126
+ endforeach;
127
+
128
+ if ( $deleted > 0 ){
129
+ $url = add_query_arg(
130
+ array(
131
+ 'page' => 'venues',
132
+ 'message' => 3,
133
+ ),
134
+ admin_url( 'edit.php?post_type=event' )
135
+ );
136
+ wp_redirect( $url );
137
+ exit();
138
+ } else{
139
+ $EO_Errors = new WP_Error( 'eo_error', __( 'Venue(s) <strong>were not </strong> deleted', 'eventorganiser' ) );
140
+ }
141
+ break;
142
+ endswitch;
143
+ endif;
144
+
145
+ $action = $this->current_action();
146
+
147
+ if ( in_array( $action, array( 'edit', 'update', 'create' ) ) ){
148
+ $venue = ( isset( $_REQUEST['event-venue'] ) ? $_REQUEST['event-venue'] : false );
149
+
150
+ //Venued edit page
151
+ add_meta_box( 'submitdiv', __( 'Save', 'eventorganiser' ), 'eventorganiser_venue_submit', 'event_page_venues', 'side', 'high' );
152
+ do_action( 'add_meta_boxes_event_page_venues', $venue );
153
+ do_action( 'add_meta_boxes', 'event_page_venues', $venue );
154
+ add_screen_option( 'layout_columns', array( 'max' => 2, 'default' => 2 ) );
155
+ } else{
156
+ //Venue admin list
157
+ require_once( 'classes/class-eo-venue-list-table.php' );
158
+ add_filter( 'manage_event_page_venues_columns', 'eventorganiser_venue_admin_columns' ) ;
159
+ add_screen_option( 'per_page', array( 'option' => 'edit_event_venue_per_page', 'label' => __( 'Venues', 'eventorganiser' ), 'default' => 20 ) );
160
+ }
161
+ }
162
+
163
+
164
+ function admin_notices(){
165
+ $m = isset( $_GET['message'] ) ? (int) $_GET['message'] : 0;
166
+ $messages = array(
167
+ 1 => __( 'Venue <strong>created</strong>', 'eventorganiser' ),
168
+ 2 => __( 'Venue <strong>updated</strong>', 'eventorganiser' ),
169
+ 3 => __( 'Venue(s) <strong>deleted</strong>', 'eventorganiser' )
170
+ );
171
+
172
+ if( isset( $messages[$m]) )
173
+ printf( '<div class="updated"><p>%s</p></div>', $messages[$m] );
174
+ }
175
+
176
+ function page_scripts(){
177
+ $action = $this->current_action();
178
+ $screen = get_current_screen();
179
+
180
+ if ( in_array( $action,array( 'create', 'edit', 'add', 'update' ) ) ):
181
+ wp_enqueue_script( 'eo_venue' );
182
+ wp_localize_script( 'eo_venue', 'EO_Venue', array( 'location' => get_option( 'timezone_string' ), 'draggable' => true, 'screen_id' => $screen->id) );
183
+ wp_enqueue_style( 'eventorganiser-style' );
184
+ wp_enqueue_script( 'media-upload' );
185
+ wp_enqueue_script( 'postbox' );
186
+ add_thickbox();
187
+ endif;
188
+ }
189
+
190
+ function set_per_page( $validated_value, $option, $value ){
191
+ //Workaround for bug http://core.trac.wordpress.org/ticket/18958
192
+
193
+ if ( 'edit_event_venue_per_page' != $option )
194
+ return $validated_value;
195
+
196
+ $value = (int) $value;
197
+ if ( $value < 1 || $value > 999 )
198
+ return false;
199
+
200
+ return $value;
201
+ }
202
+
203
+
204
+ function display(){
205
+
206
+ $action = $this->current_action();
207
+ $venue = ( isset( $_REQUEST['event-venue'] ) ? $_REQUEST['event-venue'] : false );
208
+ ?>
209
+ <div class="wrap">
210
+ <?php screen_icon( 'edit' );?>
211
+
212
+ <?php
213
+ if ( ( ( $action == 'edit' || $action == 'update' ) && $venue ) || $action == 'create' ):
214
+ $this->edit_form( $venue );
215
+
216
+ else:
217
+
218
+ //Else we are not creating or editing. Display table
219
+ $venue_table = new EO_Venue_List_Table();
220
+ $venue_table->prepare_items();
221
+
222
+ //Check if we have searched the venues
223
+ $search_term = ( isset( $_REQUEST['s'] ) ? esc_attr( $_REQUEST['s']) : '' );?>
224
+
225
+ <h2>
226
+ <?php _e( 'Venues', 'eventorganiser' );?>
227
+ <a href="edit.php?post_type=event&page=venues&action=create" class="add-new-h2"><?php _ex( 'Add New', 'post' ); ?></a>
228
+ <?php
229
+ if ( $search_term)
230
+ printf( '<span class="subtitle">' . __( 'Search results for &#8220;%s&#8221;' ) . '</span>', $search_term )
231
+ ?>
232
+ </h2>
233
+
234
+ <form id="eo-venue-table" method="get">
235
+ <!-- Ensure that the form posts back to our current page -->
236
+ <input type="hidden" name="page" value="venues" />
237
+ <input type="hidden" name="post_type" value="event" />
238
+
239
+ <!-- Now we can render the completed list table -->
240
+ <?php $venue_table->search_box( __( 'Search Venues', 'eventorganiser' ), 's' ); ?>
241
+ <?php $venue_table->display(); ?>
242
+ </form>
243
+ <?php endif;?>
244
+
245
+ </div><!--End .wrap -->
246
+ <?php
247
+ }
248
+
249
+
250
+ /**
251
+ * Display form for creating / editing venues
252
+ *
253
+ * @since 1.0.0
254
+ */
255
+ function edit_form( $venue = false ){
256
+
257
+ $venue = get_term_by( 'slug', $venue, 'event-venue' );
258
+ $term_id = isset( $venue->term_id ) ? (int) $venue->term_id : 0;
259
+ $do = ( $this->current_action() == 'edit' ? 'update' : 'add' );
260
+ $nonce = ( $do == 'update' ? 'eventorganiser_update_venue_'.$venue->slug : 'eventorganiser_add_venue' );
261
+
262
+ if ( $this->current_action() == 'edit' ) : ?>
263
+ <h2>
264
+ <?php _e( 'Edit Venue', 'eventorganiser' ); ?>
265
+ <a href="edit.php?post_type=event&page=venues&action=create" class="add-new-h2"><?php _ex( 'Add New', 'post' ); ?></a>
266
+ </h2>
267
+ <?php else: ?>
268
+ <h2>
269
+ <?php _e( 'Add New Venue', 'eventorganiser' );?>
270
+ </h2>
271
+ <?php endif; ?>
272
+
273
+ <form name="venuedetails" id="eo_venue_form" method="post" action="<?php echo admin_url( 'edit.php?post_type=event&page=venues' ); ?>">
274
+ <input type="hidden" name="action" value="<?php echo $do; ?>">
275
+ <input type="hidden" id="eo_venue_id" name="eo_venue[venue_id]" value="<?php echo $term_id;?>">
276
+ <input type="hidden" name="event-venue" value="<?php echo ( isset( $venue->slug ) ? $venue->slug : '' ) ;?>">
277
+
278
+ <?php wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false ); ?>
279
+ <?php wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false ); ?>
280
+ <?php wp_nonce_field( $nonce ); ?>
281
+
282
+ <?php
283
+ //WP3.3-3.3.1 backwards compabt
284
+ if( version_compare( get_bloginfo('version'), 3.4 ) < 0 )
285
+ $columns = '1';
286
+ else
287
+ $columns = (1 == get_current_screen()->get_columns() ? '1' : '2' );
288
+ ?>
289
+ <div id="poststuff">
290
+
291
+ <div id="post-body" class="metabox-holder columns-<?php echo $columns;?>">
292
+
293
+ <div id="post-body-content">
294
+ <div id="titlediv"><?php eventorganiser_venue_title( $venue ); ?></div>
295
+ <div class="postbox " id="venue_address">
296
+ <div class="handlediv" title="Click to toggle"><br></div>
297
+ <h3 class="hndle"><span><?php _e( 'Venue Location', 'eventorganiser' );?></span></h3>
298
+ <div class="inside"><?php eventorganiser_venue_location( $venue ); ?></div>
299
+ </div><!-- .postbox -->
300
+ <div id="<?php echo user_can_richedit() ? 'postdivrich' : 'postdiv'; ?>" class="venue_description postarea">
301
+ <?php wp_editor(eo_get_venue_meta($term_id,'_description'), 'content', array( 'textarea_name' => 'eo_venue[description]', 'dfw' => false, 'tabindex' => 7 ) ); ?>
302
+ </div>
303
+ </div><!-- #post-body-content -->
304
+
305
+ <div id="postbox-container-1" class="postbox-container">
306
+ <?php do_meta_boxes( '', 'side', $venue ); ?>
307
+ </div>
308
+
309
+ <div id="postbox-container-2" class="postbox-container">
310
+ <?php do_meta_boxes( '', 'normal', $venue ); ?>
311
+ <?php do_meta_boxes( '', 'advanced', $venue ); ?>
312
+ </div>
313
+
314
+ </div><!-- #post-body -->
315
+ <br class="clear">
316
+
317
+ </div><!-- #poststuff -->
318
+ </form>
319
+ <?php
320
+ }
321
+ }
322
+ $venues_page = new EventOrganiser_Venues_Page();
323
+
324
+
325
+ //Helper function to get the 'structure' of the event-venue taxonomy
326
+ function eo_get_venue_permastructure(){
327
+ global $wp_rewrite;
328
+ $termlink = $wp_rewrite->get_extra_permastruct( 'event-venue' );
329
+ if ( empty( $termlink ) ){
330
+ $t = get_taxonomy( 'event-venue' );
331
+ $termlink = "?$t->query_var=";
332
+ } else{
333
+ $termlink = preg_replace( '/%event-venue%/', '', $termlink );
334
+ }
335
+ $termlink = home_url( $termlink );
336
+
337
+ return $termlink;
338
+ }
339
+
340
+ //Submit metabox
341
+ function eventorganiser_venue_submit( $venue ){
342
+ $value = $venue ? __( 'Update Venue', 'eventorganiser' ) : __( 'Add Venue', 'eventorganiser' ); ?>
343
+
344
+ <div id="minor-publishing-actions">
345
+ <div id="save-action">
346
+ <input type="submit" class="button button-primary" id="save-venue" name="eo_venue[Submit]" value="<?php echo esc_attr( $value ); ?>" tabindex="10" />
347
+ </div>
348
+ <div class="clear"></div>
349
+ </div>
350
+
351
+ <?php
352
+ }
353
+
354
+ //Location metabox - called directly. Is not movable.
355
+ function eventorganiser_venue_location( $venue ){
356
+ $term_id = isset( $venue->term_id ) ? (int) $venue->term_id : 0;
357
+ $address = eo_get_venue_address( $term_id );?>
358
+
359
+ <table class ="address">
360
+ <tbody>
361
+ <?php
362
+ $address_fields = _eventorganiser_get_venue_address_fields();
363
+ $tabindex = 2;
364
+ foreach ( $address_fields as $key => $label ){
365
+ //Keys are prefixed by '_'.
366
+ $key = trim( $key, '_' );
367
+ printf(
368
+ '<tr>
369
+ <th><label>%1$s:</label></th>
370
+ <td><input type="text" name="eo_venue[%2$s]" class="eo_addressInput" id="eo-venue-%2$s" value="%3$s" tabindex="%4$s"/></td>
371
+ </tr>',
372
+ $label,
373
+ esc_attr( $key ),
374
+ esc_attr( $address[$key] ),
375
+ $tabindex++
376
+ );
377
+ }
378
+ ?>
379
+ </tbody>
380
+ </table>
381
+
382
+ <div id="venuemap" class="gmap3"></div>
383
+ <div class="clear"></div>
384
+
385
+ <input type="hidden" name="eo_venue[latitude]" id="eo_venue_Lat" value="<?php echo esc_attr( eo_get_venue_lat( $term_id ) ); ?>"/>
386
+ <input type="hidden" name="eo_venue[longtitude]" id="eo_venue_Lng" value="<?php echo esc_attr( eo_get_venue_lng( $term_id ) ); ?>"/>
387
+ <?php
388
+ }
389
+
390
+ //Venue title input
391
+ function eventorganiser_venue_title( $venue ){
392
+ $term_id = isset( $venue->term_id) ? (int) $venue->term_id : 0;
393
+ $name = isset( $venue->name ) ? $venue->name : ''; ?>
394
+
395
+ <div id="titlewrap">
396
+ <input type="text" placeholder="<?php esc_attr_e( 'Venue name', 'eventorganiser' );?>" autocomplete="off" id="title" value="<?php echo esc_attr( $name );?>" tabindex="1" size="30" name="eo_venue[name]">
397
+ </div>
398
+
399
+ <div class="inside">
400
+ <div id="edit-slug-box">
401
+ <?php if ( $venue ): ?>
402
+ <strong><?php _e( 'Permalink:' );?></strong>
403
+ <span id="sample-permalink">
404
+ <?php echo eo_get_venue_permastructure();?>
405
+ <input type="text" name="eo_venue[slug]"value="<?php echo ( isset( $venue->slug ) ? esc_attr( $venue->slug ) : '' ) ;?>" id="<?php echo $term_id; ?>-slug">
406
+ </span>
407
+
408
+ <input type="hidden" value="<?php echo get_term_link( $venue, 'event-venue' ); ?>" id="shortlink">
409
+ <a onclick="prompt( 'URL:', jQuery( '#shortlink' ).val() ); return false;" class="button" href=""><?php _e( 'Get Link', 'eventorganiser' );?></a>
410
+ <span id='view-post-btn'><a href="<?php echo get_term_link( $venue, 'event-venue' ); ?>" class='button' target='_blank'><?php _e( 'View Venue', 'eventorganiser' );?></a></span>
411
+ <?php endif;?>
412
+ </div><!-- #edit-slug-box -->
413
+ </div> <!-- .inside -->
414
+ <?php
415
+ }
416
+
417
+
418
+ function eventorganiser_venue_admin_columns( $columns ){
419
+ $columns = array(
420
+ 'cb' => '<input type="checkbox" />', //Render a checkbox instead of text
421
+ 'name' => __( 'Venue', 'eventorganiser' ),
422
+ );
423
+ $address_columns = _eventorganiser_get_venue_address_fields();
424
+ foreach( $address_columns as $key => $label )
425
+ $columns['venue'.$key] = $label;
426
+
427
+ $columns += array(
428
+ 'venue_slug' => __( 'Slug' ),
429
+ 'posts' => __( 'Events', 'eventorganiser' ),
430
+ );
431
+
432
+ return $columns;
433
+ }
event-organiser.php ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Plugin Name: Event Organiser
4
+ Plugin URI: http://www.wp-event-organiser.com
5
+ Version: 2.0.2
6
+ Description: Creates a custom post type 'events' with features such as reoccurring events, venues, Google Maps, calendar views and events and venue pages
7
+ Author: Stephen Harris
8
+ Author URI: http://www.stephenharris.info
9
+ Network: false
10
+ Text Domain: eventorganiser
11
+ Domain Path: /languages
12
+ */
13
+ /* Copyright 2011 Stephen Harris (contact@stephenharris.info)
14
+
15
+ This program is free software; you can redistribute it and/or modify
16
+ it under the terms of the GNU General Public License as published by
17
+ the Free Software Foundation; either version 2 of the License, or
18
+ (at your option) any later version.
19
+
20
+ This program is distributed in the hope that it will be useful,
21
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
22
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
+ GNU General Public License for more details.
24
+
25
+ You should have received a copy of the GNU General Public License
26
+ along with this program; if not, write to the Free Software
27
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28
+ */
29
+ /* Cookies used:
30
+ eo_admin_cal_last_viewed_date - stores the last viewed date on the admin calendar.
31
+ eo_admin_cal_last_view - stores the last used admin calendar view (month, week, day).
32
+ Expires: 10 minutes. Used for persitant admin calendar
33
+ */
34
+ /**
35
+ * The main plug-in loader
36
+ */
37
+
38
+ /**
39
+ * Set the plug-in database version
40
+ * @global string $eventorganiser_db_version
41
+ * @name $eventorganiser_db_version
42
+ */
43
+ global $eventorganiser_db_version;
44
+ $eventorganiser_db_version = '2.0.2';
45
+
46
+
47
+ add_action( 'after_setup_theme', '_eventorganiser_set_constants' );
48
+ function _eventorganiser_set_constants(){
49
+ /*
50
+ * Defines the plug-in directory url
51
+ * <code>url:http://mysite.com/wp-content/plugins/event-organiser</code>
52
+ */
53
+ define( 'EVENT_ORGANISER_URL', plugin_dir_url( __FILE__ ) );
54
+ }
55
+
56
+ /*
57
+ * Defines the plug-in directory path
58
+ * <code>/home/mysite/public_html/wp-content/plugins/event-organiser</code>
59
+ */
60
+ define( 'EVENT_ORGANISER_DIR', plugin_dir_path( __FILE__ ) );
61
+
62
+ /**
63
+ * For use in datetime formats. To return a datetime object rather than formatted string
64
+ */
65
+ define( 'DATETIMEOBJ', 'DATETIMEOBJ', true );
66
+
67
+
68
+ /**
69
+ * Load the translation file for current language. Checks in wp-content/languages first
70
+ * and then the event-organiser/languages.
71
+ *
72
+ * Edits to translation files inside event-organiser/languages will be lost with an update
73
+ * **If you're creating custom translation files, please use the global language folder.**
74
+ *
75
+ * @since 1.3
76
+ * @ignore
77
+ * @uses apply_filters() Calls 'plugin_locale' with the get_locale() value
78
+ * @uses load_textdomain() To load the textdomain from global language folder
79
+ * @uses load_plugin_textdomain() To load the textdomain from plugin folder
80
+ */
81
+ function eventorganiser_load_textdomain() {
82
+ $domain = 'eventorganiser';
83
+ $locale = apply_filters( 'plugin_locale', get_locale(), $domain );
84
+
85
+ $mofile = $domain . '-' . $locale . '.mo';
86
+
87
+ /* Check the global language folder */
88
+ $files = array( WP_LANG_DIR . '/event-organiser/' . $mofile, WP_LANG_DIR . '/' . $mofile );
89
+ foreach ( $files as $file ){
90
+ if( file_exists( $file ) )
91
+ return load_textdomain( $domain, $file );
92
+ }
93
+
94
+ //If we got this far, fallback to the plug-in language folder.
95
+ //We could use load_textdomain - but this avoids touching any more constants.
96
+ load_plugin_textdomain( 'eventorganiser', false, basename( dirname( __FILE__ ) ).'/languages' );
97
+ }
98
+ add_action( 'init', 'eventorganiser_load_textdomain' );
99
+
100
+ global $eventorganiser_roles;
101
+ $eventorganiser_roles = array(
102
+ 'edit_events' => __( 'Edit Events', 'eventorganiser' ),
103
+ 'publish_events' => __( 'Publish Events', 'eventorganiser' ),
104
+ 'delete_events' => __( 'Delete Events', 'eventorganiser' ),
105
+ 'edit_others_events' => __( 'Edit Others\' Events', 'eventorganiser' ),
106
+ 'delete_others_events' => __( 'Delete Other\'s Events', 'eventorganiser' ),
107
+ 'read_private_events' => __( 'Read Private Events', 'eventorganiser' ),
108
+ 'manage_venues' => __( 'Manage Venues', 'eventorganiser' ),
109
+ 'manage_event_categories' => __( 'Manage Event Categories & Tags', 'eventorganiser' ),
110
+ );
111
+
112
+ /****** Install, activation & deactivation******/
113
+ require_once( EVENT_ORGANISER_DIR . 'includes/event-organiser-install.php' );
114
+
115
+ register_activation_hook( __FILE__, 'eventorganiser_install' );
116
+ register_deactivation_hook( __FILE__, 'eventorganiser_deactivate' );
117
+ register_uninstall_hook( __FILE__, 'eventorganiser_uninstall' );
118
+
119
+
120
+ function eventorganiser_get_option( $option, $default = false ){
121
+
122
+ $defaults = array(
123
+ 'url_event' => 'events/event',
124
+ 'url_events' => 'events/event',
125
+ 'url_venue' => 'events/venue',
126
+ 'url_cat' => 'events/category',
127
+ 'url_tag' => 'events/tag',
128
+ 'navtitle' => __( 'Events', 'eventorganiser' ),
129
+ 'group_events' => '',
130
+ 'feed' => 1,
131
+ 'deleteexpired' => 0,
132
+ 'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields', 'comments', 'eventtag' ),
133
+ 'event_redirect' => 'events',
134
+ 'dateformat' => 'd-m-Y',
135
+ 'prettyurl' => 1,
136
+ 'templates' => 1,
137
+ 'addtomenu' => 0,
138
+ 'menu_item_db_id' => 0,
139
+ 'excludefromsearch' => 0,
140
+ 'showpast' => 0,
141
+ 'runningisnotpast' => 0,
142
+ );
143
+ $options = get_option( 'eventorganiser_options', $defaults );
144
+ $options = wp_parse_args( $options, $defaults );
145
+
146
+ /* Backwards compatibility for 'eventag' option */
147
+ if( $option === 'eventtag' )
148
+ return in_array( 'eventtag', $options['supports'] );
149
+
150
+ if( $option === 'dateformat' ){
151
+ //Backwards compatibility (migration from mm-dd/dd-mm to php format):
152
+ if( $options[$option] == 'mm-dd' ){
153
+ $options[$option] = 'm-d-Y';
154
+ }elseif( $options[$option] == 'dd-mm' ){
155
+ $options[$option] = 'd-m-Y';
156
+ }
157
+ }
158
+
159
+ if( !isset($options[$option]) )
160
+ return $default;
161
+
162
+ return $options[$option];
163
+ }
164
+
165
+
166
+ /****** Register event post type and event taxonomy******/
167
+ require_once(EVENT_ORGANISER_DIR.'includes/event-organiser-cpt.php');
168
+
169
+ /****** Register scripts, styles and actions******/
170
+ require_once(EVENT_ORGANISER_DIR.'includes/event-organiser-register.php');
171
+
172
+ /****** Deals with the queries******/
173
+ require_once(EVENT_ORGANISER_DIR.'includes/event-organiser-archives.php');
174
+
175
+ /****** Deals with importing/exporting & subscriptions******/
176
+ require_once(EVENT_ORGANISER_DIR.'includes/class-event-organiser-im-export.php');
177
+
178
+ if ( is_admin() ):
179
+ require_once(EVENT_ORGANISER_DIR.'classes/class-eventorganiser-admin-page.php');
180
+
181
+ /****** event editing pages******/
182
+ require_once(EVENT_ORGANISER_DIR.'event-organiser-edit.php');
183
+ require_once(EVENT_ORGANISER_DIR.'event-organiser-manage.php');
184
+
185
+ /****** settings, venue and calendar pages******/
186
+ require_once(EVENT_ORGANISER_DIR.'event-organiser-settings.php');
187
+ require_once(EVENT_ORGANISER_DIR.'event-organiser-venues.php');
188
+ require_once(EVENT_ORGANISER_DIR.'event-organiser-calendar.php');
189
+
190
+ require_once(EVENT_ORGANISER_DIR.'event-organiser-debug.php');
191
+ require_once(EVENT_ORGANISER_DIR.'event-organiser-go-pro.php');
192
+ else:
193
+ if( defined( 'WP_DEBUG' ) && WP_DEBUG ){
194
+ require_once(EVENT_ORGANISER_DIR.'includes/debug.php');
195
+ }
196
+
197
+ endif;
198
+
199
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
200
+ /****** Ajax actions ******/
201
+ require_once(EVENT_ORGANISER_DIR.'includes/event-organiser-ajax.php');
202
+ }
203
+
204
+ if( defined( 'WP_DEBUG' ) && WP_DEBUG ){
205
+ add_action( 'register_sidebar', '_eventorganiser_check_sidebars' );
206
+ }
207
+
208
+ /****** Functions ******/
209
+ require_once(EVENT_ORGANISER_DIR.'includes/event-organiser-event-functions.php');
210
+ require_once(EVENT_ORGANISER_DIR.'includes/event-organiser-venue-functions.php');
211
+ require_once(EVENT_ORGANISER_DIR.'includes/event-organiser-utility-functions.php');
212
+ require_once(EVENT_ORGANISER_DIR.'includes/deprecated.php');
213
+ require_once(EVENT_ORGANISER_DIR.'includes/event.php');
214
+
215
+ /****** Templates - note some plug-ins will require this to included admin-side too ******/
216
+ require_once('includes/event-organiser-templates.php');
217
+
218
+ /****** Widgets and Shortcodes ******/
219
+ require_once(EVENT_ORGANISER_DIR.'classes/class-eo-agenda-widget.php');
220
+ require_once(EVENT_ORGANISER_DIR.'classes/class-eo-event-list-widget.php');
221
+ require_once(EVENT_ORGANISER_DIR.'classes/class-eo-calendar-widget.php');
222
+ require_once(EVENT_ORGANISER_DIR.'classes/class-eo-widget-categories.php');
223
+ require_once(EVENT_ORGANISER_DIR.'classes/class-eo-widget-venues.php');
224
+ require_once(EVENT_ORGANISER_DIR.'classes/class-eventorganiser-shortcodes.php');
225
+
226
+ add_action( 'widgets_init', 'eventorganiser_widgets_init' );
227
+ function eventorganiser_widgets_init(){
228
+ eventorganiser_load_textdomain();
229
+ register_widget( 'EO_Event_List_Widget' );
230
+ register_widget( 'EO_Events_Agenda_Widget' );
231
+ register_widget( 'EO_Calendar_Widget' );
232
+ register_widget( 'EO_Widget_Categories' );
233
+ register_widget( 'EO_Widget_Venues' );
234
+ }
includes/class-event-organiser-im-export.php ADDED
@@ -0,0 +1,852 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Event importer / exporter
4
+ */
5
+ if ( ! function_exists( 'add_action' ) ) {
6
+ echo "Hi there! I'm just a part of plugin, not much I can do when called directly.";
7
+ exit;
8
+ }
9
+ class Event_Organiser_Im_Export {
10
+
11
+ static private $classobj = NULL;
12
+
13
+ /**
14
+ * Handler for the action 'init'. Instantiates this class.
15
+ */
16
+ public function get_object() {
17
+
18
+ if ( NULL === self :: $classobj ) {
19
+ self :: $classobj = new self;
20
+ }
21
+
22
+ return self :: $classobj;
23
+ }
24
+
25
+ public function __construct() {
26
+ global $pagenow, $EO_Errors;
27
+
28
+ if(!isset($EO_Errors)) $EO_Errors = new WP_Error();
29
+
30
+ if( is_feed('eo-events') && eventorganiser_get_option('feed') ){
31
+ $this->get_export_file();
32
+ }
33
+
34
+ //If importing / exporting events make sure we a logged in and check nonces.
35
+ if ( is_admin() && !empty($_POST['eventorganiser_download_events']) && check_admin_referer( 'eventorganiser_download_events' )
36
+ && current_user_can('manage_options') ):
37
+ //Exporting events
38
+ //mmm... maybe finally a legitimate use of query_posts
39
+ query_posts(array(
40
+ 'post_type'=>'event',
41
+ 'showpastevents'=>true,
42
+ 'group_events_by'=>'series',
43
+ 'posts_per_page'=>-1,
44
+ ));
45
+ $this->get_export_file();
46
+
47
+ elseif ( is_admin() && !empty($_POST['eventorganiser_import_events']) && check_admin_referer( 'eventorganiser_import_events')
48
+ && current_user_can('manage_options') ):
49
+ //Importing events
50
+
51
+ //Perform checks on file:
52
+ if ( in_array($_FILES["ics"]["type"], array("text/calendar","application/octet-stream")) && ($_FILES["ics"]["size"] < 2097152) ):
53
+ if($_FILES["ics"]["error"] > 0){
54
+ $EO_Errors = new WP_Error('eo_error', sprintf(__("File Error encountered: %d",'eventorganiser'), $_FILES["ics"]["error"]));
55
+ }else{
56
+ //Import file
57
+ $this->import_file($_FILES['ics']['tmp_name']);
58
+ }
59
+
60
+ elseif(!isset($_FILES) || empty($_FILES['ics']['name'])):
61
+ $EO_Errors = new WP_Error('eo_error', __("No file detected.",'eventorganiser'));
62
+
63
+ else:
64
+ $EO_Errors = new WP_Error('eo_error', __("Invalid file uploaded. The file must be a ics calendar file of type 'text/calendar', no larger than 2MB.",'eventorganiser'));
65
+ $size = size_format($_FILES["ics"]["size"],2);
66
+ $details = sprintf( __('File size: %s. File type: %s','eventorganiser'),$size, $_FILES["ics"]["type"]);
67
+ $EO_Errors->add('eo_error', $details);
68
+
69
+ endif;
70
+
71
+ endif;
72
+
73
+ add_action( 'eventorganiser_event_settings_imexport', array( $this, 'get_im_export_markup' ) );
74
+ }
75
+
76
+
77
+ /**
78
+ * get markup for ex- and import on settings page
79
+ *
80
+ * @since 1.0.0
81
+ */
82
+ public function get_im_export_markup() {
83
+ ?>
84
+ <h3 class="title"><?php _e('Export Events', 'eventorganiser'); ?></h3>
85
+ <form method="post" action="">
86
+ <?php settings_fields( 'eventorganiser_imexport'); ?>
87
+ <p><?php _e( 'The export button below generates an ICS file of your events that can be imported in to other calendar applications such as Google Calendar.', 'eventorganiser'); ?></p>
88
+ <?php wp_nonce_field('eventorganiser_download_events'); ?>
89
+ <input type="hidden" name="eventorganiser_download_events" value="true" />
90
+ <?php submit_button( __( 'Download Export File', 'eventorganiser' )." &raquo;", 'secondary', 'eventorganiser_download_events' ); ?>
91
+ </form>
92
+
93
+ <h3 class="title"><?php _e('Import Events', 'eventorganiser'); ?></h3>
94
+ <form method="post" action="" enctype="multipart/form-data">
95
+ <div class="inside">
96
+ <p><?php _e( 'Import an ICS file.', 'eventorganiser'); ?></p>
97
+ <label><input type="checkbox" name="eo_import_venue" value=1 /> <?php _e( 'Import venues', 'eventorganiser' ); ?></label>
98
+ <label><input type="checkbox" name="eo_import_cat" value=1 /> <?php _e( 'Import categories', 'eventorganiser' ); ?></label>
99
+ <p><input type="file" name="ics" /></p>
100
+ <?php wp_nonce_field('eventorganiser_import_events'); ?>
101
+ <input type="hidden" name="eventorganiser_import_events" value="true" />
102
+ <?php submit_button( __( 'Upload ICS file', 'eventorganiser' )." &raquo;", 'secondary', 'eventorganiser_import_events' ); ?>
103
+ </div>
104
+ </form>
105
+ <?php
106
+ }
107
+
108
+ /**
109
+ * Gets an ICAL file of events in the database, to be downloaded
110
+
111
+ * @since 1.0.0
112
+ */
113
+ public function get_export_file() {
114
+ $filename = urlencode( 'event-organiser_' . date('Y-m-d') . '.ics' );
115
+ $this->export_events( $filename, 'text/calendar' );
116
+ }
117
+
118
+ /**
119
+ * Creates an ICAL file of events in the database
120
+
121
+ * @since 1.0.0
122
+ * @param string filename - the name of the file to be created
123
+ * @param string filetype - the type of the file ('text/calendar')
124
+ */
125
+ public function export_events( $filename, $filetype ){
126
+ //Collect output
127
+ ob_start();
128
+
129
+ // File header
130
+ header( 'Content-Description: File Transfer' );
131
+ header( 'Content-Disposition: attachment; filename=' . $filename );
132
+ header('Content-type: text/calendar; charset=' . get_option('blog_charset').';');
133
+ header("Pragma: 0");
134
+ header("Expires: 0");
135
+ ?>
136
+ BEGIN:VCALENDAR
137
+ VERSION:2.0
138
+ PRODID:-//<?php get_bloginfo('name'); ?>//NONSGML Events //EN
139
+ CALSCALE:GREGORIAN
140
+ X-WR-CALNAME:<?php echo get_bloginfo('name');?> - Events
141
+ X-ORIGINAL-URL:<?php echo get_post_type_archive_link('event'); ?>
142
+
143
+ X-WR-CALDESC:<?php echo get_bloginfo('name');?> - Events
144
+ <?php
145
+
146
+ // Loop through events
147
+ if ( have_posts() ):
148
+ $now = new DateTime();
149
+ $dtstamp =$now->format('Ymd\THis\Z');
150
+ $UTC_tz = new DateTimeZone('UTC');
151
+
152
+ while( have_posts() ): the_post();
153
+ global $post;
154
+
155
+ //If event has no corresponding row in events table then skip it
156
+ if(!isset($post->event_id) || $post->event_id==-1)
157
+ continue;
158
+
159
+ $start = eo_get_the_start(DATETIMEOBJ);
160
+ $end = eo_get_the_end(DATETIMEOBJ);
161
+
162
+ $created_date = get_post_time('Ymd\THis\Z',true);
163
+ $modified_date = get_post_modified_time('Ymd\THis\Z',true);
164
+
165
+ $schedule_data = eo_get_event_schedule();
166
+
167
+ //Set up start and end date times
168
+ if( eo_is_all_day() ){
169
+ $format = 'Ymd';
170
+ $start_date = $start->format($format);
171
+ $end->modify('+1 minute');
172
+ $end_date = $end->format($format);
173
+ }else{
174
+ $format = 'Ymd\THis\Z';
175
+ $start->setTimezone($UTC_tz);
176
+ $start_date =$start->format($format);
177
+ $end->setTimezone($UTC_tz);
178
+ $end_date = $end->format($format);
179
+ }
180
+
181
+ //Get the reoccurrence rule in ICS format
182
+ $reoccurrence_rule = eventorganiser_generate_ics_rrule();
183
+
184
+ //Generate Event status
185
+ if( get_post_status(get_the_ID()) == 'publish' )
186
+ $status = 'CONFIRMED';
187
+ else
188
+ $status = 'TENTATIVE';
189
+
190
+ //Generate a globally unique UID:
191
+ $rand='';
192
+ $host = $_SERVER['SERVER_NAME'];
193
+ $base = 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPrRsStTuUvVxXuUvVwWzZ1234567890';
194
+ $start = 0;
195
+ $end = strlen( $base ) - 1;
196
+ $length = 6;
197
+
198
+ for( $p = 0; $p < $length; $p++ ):
199
+ $rand .= $base{mt_rand( $start, $end )};
200
+ endfor;
201
+
202
+ $uid = $now->format('Ymd\THiT').microtime(true).'-'.$rand.'-EO'.$post->ID.'@'.$host;
203
+
204
+ //Output event
205
+ ?>
206
+ BEGIN:VEVENT
207
+ UID:<?php echo $uid;?>
208
+
209
+ STATUS:<?php echo $status;?>
210
+
211
+ DTSTAMP:<?php echo $dtstamp;?>
212
+
213
+ CREATED:<?php echo $created_date;?>
214
+
215
+ LAST-MODIFIED:<?php echo $modified_date;?>
216
+
217
+ <?php if( eo_is_all_day() ): ?>
218
+ DTSTART;VALUE=DATE:<?php echo $start_date ; ?>
219
+
220
+ DTEND;VALUE=DATE:<?php echo $end_date; ?>
221
+ <?php else: ?>
222
+ DTSTART:<?php echo $start_date ; ?>
223
+
224
+ DTEND:<?php echo $end_date; ?>
225
+ <?php endif;?>
226
+
227
+ <?php if ($reoccurrence_rule):?>
228
+ RRULE:<?php echo $reoccurrence_rule;?>
229
+
230
+ <?php endif;?>
231
+ <?php if( !empty($schedule_data['exclude']) ):
232
+ $exclude_strings = array();
233
+ foreach ( $schedule_data['exclude'] as $exclude ){
234
+ if( !eo_is_all_day() ){
235
+ $vdate='';
236
+ $exclude->setTimezone($UTC_tz);
237
+ $exclude_strings[] = $exclude->format('Ymd\THis\Z');
238
+ }else{
239
+ $vdate=';VALUE=DATE';
240
+ $exclude_strings[] = $exclude->format('Ymd');
241
+ }
242
+ }?>
243
+ EXDATE<?php echo $vdate;?>:<?php echo implode(',',$exclude_strings);?>
244
+
245
+ <?php endif;?>
246
+ <?php if( !empty($schedule_data['include']) ):
247
+ $include_strings = array();
248
+ foreach ( $schedule_data['include'] as $include ){
249
+ if( !eo_is_all_day() ){
250
+ $vdate='';
251
+ $include->setTimezone($UTC_tz);
252
+ $include_strings[] = $include->format('Ymd\THis\Z');
253
+ }else{
254
+ $vdate=';VALUE=DATE';
255
+ $include_strings[] = $include->format('Ymd');
256
+ }
257
+ }?>
258
+
259
+ RDATE<?php echo $vdate;?>:<?php echo implode(',',$include_strings);?>
260
+
261
+ <?php endif; ?>
262
+ SUMMARY:<?php echo $this->escape_icalText(get_the_title_rss()); ?>
263
+
264
+ <?php
265
+ $excerpt = get_the_excerpt();
266
+ $excerpt = apply_filters('the_excerpt_rss', $excerpt);
267
+ if(!empty($excerpt)):
268
+ ?>
269
+ DESCRIPTION:<?php echo html_entity_decode($this->escape_icalText($excerpt));?>
270
+
271
+ <?php endif;
272
+
273
+ $cats = get_the_terms( get_the_ID(), 'event-category' );
274
+ if( $cats && !is_wp_error($cats) ):
275
+ $cat_names = wp_list_pluck($cats, 'name');
276
+ $cat_names = array_map(array($this,'parse_icalText'),$cat_names); ?>
277
+ CATEGORIES:<?php echo implode(',',$cat_names); ?>
278
+
279
+ <?php endif; ?>
280
+ <?php
281
+ if( eo_get_venue() ):
282
+ $venue =eo_get_venue_name(eo_get_venue());
283
+ ?>
284
+ LOCATION: <?php echo $this->escape_icalText($venue);;?>
285
+
286
+ <?php endif;
287
+ $author = get_the_author();
288
+ ?>
289
+ ORGANIZER: <?php echo $this->escape_icalText($author);?>
290
+
291
+ END:VEVENT
292
+ <?php
293
+ endwhile;
294
+
295
+ endif;
296
+ ?>
297
+ END:VCALENDAR
298
+ <?php
299
+
300
+ //Collect output and echo
301
+ $eventsical = ob_get_contents();
302
+ ob_end_clean();
303
+ echo $eventsical;
304
+ exit();
305
+ }
306
+
307
+
308
+ function escape_icalText($text){
309
+ $text = str_replace("\\", "\\\\", $text);
310
+ $text = str_replace(",", "\,", $text);
311
+ $text = str_replace(";", "\;", $text);
312
+ $text = str_replace("\n", "\n ", $text);
313
+
314
+ return $text;
315
+ }
316
+
317
+
318
+ /**
319
+ * Reads in an ICAL file into an array, then parses the array and inserts events into database
320
+
321
+ * @since 1.1.0
322
+ *
323
+ * @param string $cal_file - the file to import
324
+ */
325
+ function import_file($cal_file){
326
+ global $EO_Errors;
327
+
328
+ if ( ! current_user_can( 'manage_options' ) || ! current_user_can( 'edit_events' ))
329
+ wp_die( __('You do not have sufficient permissions to import events.','eventorganiser') );
330
+
331
+ //Returns the file as an array of lines
332
+ $file_array =$this->parse_file($cal_file);
333
+
334
+ //Go through array and import events and insert them into database.
335
+ $result = $this->import_events($file_array);
336
+ }
337
+
338
+ /* Reads an ICAL into an array ofline
339
+
340
+ * @since 1.1.0
341
+ *
342
+ * @param string $cal_file - the file to parse
343
+ */
344
+ function parse_file($cal_file){
345
+ global $EO_Errors;
346
+
347
+ $file_handle = @fopen($cal_file, "rb");
348
+ $lines = array();
349
+
350
+ if(!$file_handle)
351
+ return false;
352
+
353
+ //Feed lines into array
354
+ while (!feof($file_handle) ):
355
+ $line_of_text = fgets($file_handle, 4096);
356
+ $lines[]= $line_of_text;
357
+ endwhile;
358
+
359
+ fclose($file_handle);
360
+
361
+ return $lines;
362
+ }
363
+
364
+ /**
365
+ * Parses through an array of lines (of an ICAL file), creates events and inserts them as they are found.
366
+
367
+ * @since 1.1.0
368
+ *
369
+ * @param array - $lines, array of lines of an ICAL file
370
+ */
371
+ function import_events($lines){
372
+ global $EO_Errors;
373
+ $state = "NONE";
374
+ $error = false;
375
+
376
+ $error_count =0;
377
+ $event_count =0;
378
+
379
+ $event_array = array();
380
+ $event_array['event'] = array();
381
+ $event_array['event_post'] = array();
382
+ $event_array['event_meta'] = array();
383
+
384
+ //Get Blog timezone, set Calendar timezone to this by default
385
+ $blog_tz = eo_get_blog_timezone();
386
+ $cal_tz =$blog_tz;
387
+ $output="";
388
+
389
+ //Record number of venues / categories created
390
+ global $eventorganiser_venues_created,$eventorganiser_cats_created;
391
+ $eventorganiser_venues_created = 0;
392
+ $eventorganiser_cats_created = 0;
393
+
394
+ //Read through each line
395
+ for ( $n = 0; $n < count ( $lines ) && ! $error; $n++ ):
396
+ $buff = trim($lines[$n]);
397
+
398
+ if(!empty($buff)):
399
+ $line = explode(':',$buff,2);
400
+
401
+ //On the right side of the line we may have DTSTART;TZID= or DTSTART;VALUE=
402
+ $modifiers = explode (';', $line[0]);
403
+ $property =array_shift($modifiers);
404
+ $value = (isset($line[1]) ? trim($line[1]) : '');
405
+
406
+ //If we are in EVENT state
407
+ if ($state == "VEVENT") {
408
+
409
+ //If END:VEVENT, insert event into database
410
+ if($property=='END' && $value=='VEVENT'){
411
+ $state = "VCALENDAR";
412
+
413
+ //Insert new post from objects
414
+ $post_id = eo_insert_event($event_array['event_post'],$event_array['event']);
415
+
416
+ if(!$post_id || is_wp_error($post_id) ){
417
+ $error_count++;
418
+ }
419
+
420
+ }else{
421
+ //Otherwise, parse event property
422
+ try{
423
+ while( isset( $lines[$n+1] ) && $lines[$n+1][0] == ' ' ){
424
+ $value .= $lines[$n+1];
425
+ $n++;
426
+ }
427
+ $event_array = $this->parse_Event_Property($event_array,$property,$value,$modifiers,$blog_tz,$cal_tz);
428
+
429
+ }catch(Exception $e){
430
+ $error_count++;
431
+ $preamble= sprintf( __('Line: %1$d','eventorganiser'),$n+1);
432
+ $EO_Errors->add('eo_error', $preamble.' '.$e->getMessage());
433
+
434
+ //Abort parsing event
435
+ $state = "VCALENDAR";
436
+ }
437
+ }
438
+
439
+ // If we are in CALENDAR state
440
+ }elseif ($state == "VCALENDAR") {
441
+
442
+ //Begin event
443
+ if( $property=='BEGIN' && $value=='VEVENT'){
444
+ $state = "VEVENT";
445
+ $event_count++;
446
+ $event_array['event'] = array();
447
+ $event_array['event_post'] = array();
448
+ $event_array['event_meta'] = array();
449
+
450
+ }elseif ( $property=='END' && $value=='VCALENDAR'){
451
+ $state = "NONE";
452
+
453
+ }elseif($property=='X-WR-TIMEZONE'){
454
+ try{
455
+ $cal_tz = self::parse_TZID($value);
456
+ }catch(Exception $e){
457
+ $preamble= sprintf( __('Line: %1$d','eventorganiser'),$n+1);
458
+ $EO_Errors->add('eo_error', $preamble.' '.$e->getMessage());
459
+ break;
460
+ }
461
+ }
462
+
463
+ }elseif($state == "NONE" && $property=='BEGIN' && $value=='VCALENDAR') {
464
+ $state = "VCALENDAR";
465
+ }
466
+
467
+ endif; //If line is not empty
468
+ endfor; //For each line
469
+
470
+ //Display message
471
+ if($event_count ==0):
472
+ $EO_Errors->add('eo_error', __("No events were imported.",'eventorganiser'));
473
+ elseif($error_count >0):
474
+ $EO_Errors->add('eo_error',sprintf( __('There was an error with %1$d of %2$d events in the ical file'),$error_count, $event_count));
475
+ else:
476
+
477
+ if($event_count==1)
478
+ $message=__("1 event was successfully imported",'eventorganiser').".";
479
+ else
480
+ $message= sprintf( __("%d events were successfully imported",'eventorganiser'),$event_count).".";
481
+
482
+ if($eventorganiser_venues_created==1){
483
+ $message.= " ".__("1 venue was created",'eventorganiser').".";
484
+ }elseif($eventorganiser_venues_created>1){
485
+ $message .= " ".sprintf( __("%d venues were created",'eventorganiser'),$eventorganiser_venues_created).".";
486
+ }
487
+
488
+ if($eventorganiser_cats_created==1){
489
+ $message.= " ".__("1 category was created",'eventorganiser').".";
490
+ }elseif($eventorganiser_cats_created>1){
491
+ $message .= " ".sprintf( __("%d categories were created",'eventorganiser'),$eventorganiser_cats_created).".";
492
+ }
493
+
494
+ $EO_Errors->add('eo_notice',$message);
495
+ endif;
496
+ return true;
497
+ }
498
+
499
+ /**
500
+ * Returns the supplied array with the additional data parsed from the given property-value pair
501
+ * May require the blog and/or calendar time-zone in date manipulations
502
+ * May also require modifiers in date manipulations (e.g. TZID, VALUE)
503
+ *
504
+ * @since 1.1.0
505
+ *
506
+ * @param array $event_array - array of event details to be added to
507
+ * @param string $property - the property being parsed
508
+ * @param string $value - the value of the property being parsed
509
+ * @param string $modifiers - array of modifiers associated with the property
510
+ * @param DateTimeZone $blog_tz - blog's timezone
511
+ * @param DateTimeZone $cal_tzid - calendar's default timezone
512
+ * @return DateTimeZone - the timezone with the given identifier or false if it isn't recognised
513
+ */
514
+ function parse_Event_Property($event_array,$property,$value,$modifiers,$blog_tz,$cal_tz){
515
+ extract($event_array);
516
+
517
+ $import_venues = (isset($_POST['eo_import_venue']) ? true : false);
518
+ $import_cats = (isset($_POST['eo_import_cat']) ? true : false);
519
+
520
+ $date_tz="";
521
+
522
+ if(!empty($modifiers)):
523
+ foreach($modifiers as $modifier):
524
+ if (stristr($modifier, 'TZID')){
525
+ $date_tz = self::parse_TZID(substr($modifier, 5));
526
+
527
+ }elseif(stristr($modifier, 'VALUE')){
528
+ $meta = substr($modifier, 6);
529
+ }
530
+ endforeach;
531
+ endif;
532
+
533
+ //For dates - if there is not an associated timezone, use calendar default.
534
+ if(empty($date_tz))
535
+ $date_tz = $cal_tz;
536
+
537
+ switch($property):
538
+ //Date properties
539
+ case 'CREATED':
540
+ case 'DTSTART':
541
+ case 'DTEND':
542
+ if(isset($meta) && $meta=='DATE'):
543
+ $date = $this->parse_icalDate($value, $blog_tz);
544
+ $allday=1;
545
+ else:
546
+ $date = $this->parse_icalDateTime($value, $date_tz);
547
+ $allday=0;
548
+ endif;
549
+
550
+ if(empty($date))
551
+ break;
552
+
553
+ switch($property):
554
+ case'DTSTART':
555
+ $event['start']= $date;
556
+ $event['all_day']=$allday;
557
+ break;
558
+
559
+ case 'DTEND':
560
+ if($allday==1)
561
+ $date->modify('-1 second');
562
+ $event['end']= $date;
563
+ break;
564
+
565
+ case 'CREATED':
566
+ $date->setTimezone(new DateTimeZone('utc'));
567
+ $event_post['post_date_gmt']= $date->format('Y-m-d H:i:s');
568
+ break;
569
+
570
+ endswitch;
571
+ break;
572
+
573
+ case 'EXDATE':
574
+ case 'RDATE':
575
+ //The modifiers have been dealt with above. We do similiar to above, except for an array of dates...
576
+ $value_array = explode(',',$value);
577
+
578
+ //Note, we only consider the Date part and ignore the time
579
+ foreach($value_array as $val):
580
+ $date = $this->parse_icalDate($val, $blog_tz);
581
+ if( $property == 'EXDATE' ){
582
+ $event['exclude'][] = $date;
583
+ }else{
584
+ $event['include'][] = $date;
585
+ }
586
+ endforeach;
587
+
588
+ break;
589
+
590
+ //Reoccurrence rule properties
591
+ case 'RRULE':
592
+ $event += $this->parse_RRule($value);
593
+ break;
594
+
595
+ //The event's summary (AKA post title)
596
+ case 'SUMMARY':
597
+ $event_post['post_title']=$this->parse_icalText($value);
598
+ break;
599
+
600
+ //The event's description (AKA post content)
601
+ case 'DESCRIPTION':
602
+ $event_post['post_content']=$this->parse_icalText($value);
603
+ break;
604
+
605
+ //Event venues, assign to existing venue - or if set, create new one
606
+ case 'LOCATION':
607
+ $venue_ids = array();
608
+ if( !empty($value) ):
609
+ $venue_name = trim($value);
610
+ $venue = get_term_by('name',$venue_name,'event-venue');
611
+ if($venue){
612
+ $venue_ids[] = (int) $venue->term_id;
613
+
614
+ }elseif($import_venues){
615
+ //Create new venue, get ID. Count of venues created++
616
+ global $eventorganiser_venues_created;
617
+ $return = eo_insert_venue($venue_name);
618
+
619
+ if( !is_wp_error($return) && !$return ){
620
+ $venue_ids[] = (int) $return['term_id'];
621
+ $eventorganiser_venues_created++;
622
+ $event['venue']= $return['term_id']; //XXX This is depreciated
623
+ }
624
+ }
625
+ $venue_ids = array_filter($venue_ids);
626
+ if(!empty($venue_ids)){
627
+ $event_post['tax_input']['event-venue']=$venue_ids;
628
+ }
629
+ endif;
630
+ break;
631
+
632
+ //Event categories, assign to existing categories - or if set, create new ones
633
+ case 'CATEGORIES':
634
+ $cats=explode(',',$value);
635
+ $cat_ids = array();
636
+ if(!empty($cats)):
637
+ foreach ($cats as $cat_name):
638
+ $cat_name = trim($cat_name);
639
+ $cat = get_term_by('name',$cat_name,'event-category');
640
+ if($cat){
641
+ $cat_ids[] = (int) $cat->term_id;
642
+ }elseif($import_cats){
643
+ //Create new category, get ID. Count of cats created++
644
+ global $eventorganiser_cats_created;
645
+ $return = wp_insert_term($cat_name,'event-category',array());
646
+ if(!is_wp_error($return)){
647
+ $cat_ids[] = (int) $return['term_id'];
648
+ $eventorganiser_cats_created++;
649
+ }
650
+ }
651
+ endforeach;
652
+
653
+ $cat_ids = array_filter($cat_ids);
654
+ if(!empty($cat_ids))
655
+ $event_post['tax_input']['event-category']=$cat_ids;
656
+ endif;
657
+ break;
658
+
659
+ //The event's status
660
+ case 'STATUS':
661
+ switch($value):
662
+ case 'CONFIRMED':
663
+ $event_post['post_status'] = 'publish';
664
+ break;
665
+
666
+ case 'CANCELLED':
667
+ $event_post['post_status'] = 'trash';
668
+ break;
669
+
670
+ default:
671
+ $event_post['post_status'] = 'draft';
672
+ endswitch;
673
+ break;
674
+
675
+ //An url associated with the event
676
+ case 'URL':
677
+ $event_meta['eo_url']=$value;
678
+ break;
679
+
680
+ endswitch;
681
+
682
+ $event_array= array('event'=>$event,'event_post'=>$event_post,'event_meta'=>$event_meta);
683
+
684
+ return $event_array;
685
+ }
686
+
687
+ /**
688
+ * Takes escaped text and returns the text unescaped.
689
+
690
+ * @since 1.1.0
691
+ *
692
+ * @param string $text - the escaped test
693
+ * @return string $text - the text, unescaped.
694
+ */
695
+ function parse_icalText($text){
696
+ //Get rid of carriage returns:
697
+ $text = str_replace("\r\n","\n",$text);
698
+ $text = str_replace("\r","\n",$text);
699
+
700
+ //Some calendar apps break up text
701
+ $text = str_replace("\n ","",$text);
702
+ $text = str_replace("\r ","",$text);
703
+
704
+ //Any intended carriage returns/new-lines converted to HTML
705
+ $text = str_replace("\\r\\n","",$text);
706
+ $text = str_replace("\\n","</br>",$text);
707
+ $text = stripslashes($text);
708
+ return $text;
709
+ }
710
+
711
+ /**
712
+ * Takes a date-time in ICAL and returns a datetime object
713
+
714
+ * @since 1.1.0
715
+ *
716
+ * @param string $tzid - the value of the ICAL TZID property
717
+ * @return DateTimeZone - the timezone with the given identifier or false if it isn't recognised
718
+ */
719
+ function parse_TZID($tzid){
720
+ $tzid = str_replace('-','/',$tzid);
721
+ $tz = new DateTimeZone($tzid);
722
+ return $tz;
723
+ }
724
+
725
+ /**
726
+ * Takes a date in ICAL and returns a datetime object
727
+
728
+ * @since 1.1.0
729
+ *
730
+ * @param string $ical_date - date in ICAL format
731
+ * @param DateTimeZone $blog_tz - Blog timezone object
732
+ * @return DateTime - the $ical_date as DateTime object
733
+ */
734
+ function parse_icalDate($ical_date, $blog_tz){
735
+ //Expects: YYYYMMDD;
736
+ preg_match('/^(\d{8})*/', $ical_date, $matches);
737
+
738
+ if(count($matches)!=2){
739
+ throw new Exception(__('Invalid date. Date expected in YYYYMMDD format.','eventorganiser'));
740
+ }
741
+
742
+ $datetime = new DateTime($matches[1],$blog_tz);
743
+
744
+ return $datetime;
745
+ }
746
+
747
+ /**
748
+ * Takes a date-time in ICAL and returns a datetime object
749
+
750
+ * @since 1.1.0
751
+ *
752
+ * @param string $ical_date - date-time in ICAL format
753
+ * @param DateTimeZone $blog_tz - Blog timezone object
754
+ * @return DateTime - the $ical_date as DateTime object
755
+ */
756
+ function parse_icalDateTime($ical_date,$tz){
757
+ /*
758
+ Expects
759
+ utc: YYYYMMDDTHHiissZ
760
+ local: YYYYMMDDTHHiiss
761
+ */
762
+ preg_match('/^((\d{8}T\d{6})(Z)?)/', $ical_date, $matches);
763
+
764
+ if(count($matches)==3){
765
+ //floating / local date
766
+
767
+ }elseif(count($matches)==4){
768
+ $tz = new DateTimeZone('UTC');
769
+
770
+ }else{
771
+ throw new Exception(__('Invalid datetime. Date expected in YYYYMMDDTHHiissZ or YYYYMMDDTHHiiss format.','eventorganiser'));
772
+ return false;
773
+ }
774
+
775
+ $datetime = new DateTime($matches[2],$tz);
776
+
777
+ return $datetime;
778
+ }
779
+
780
+ /**
781
+ * Takes a date-time in ICAL and returns a datetime object
782
+
783
+ * @since 1.1.0
784
+ *
785
+ * @param string $RRule - the value of the ICAL RRule property
786
+ * @return array - a reoccurrence rule array as understood by Event Organiser
787
+ */
788
+ function parse_RRule($RRule){
789
+ //RRule is a sequence of rule parts seperated by ';'
790
+ $rule_parts = explode(';',$RRule);
791
+
792
+ foreach ($rule_parts as $rule_part):
793
+
794
+ //Each rule part is of the form PROPERTY=VALUE
795
+ $prop_value = explode('=',$rule_part, 2);
796
+ $property = $prop_value[0];
797
+ $value = $prop_value[1];
798
+
799
+ switch($property):
800
+ case 'FREQ':
801
+ $rule_array['schedule'] =strtolower($value);
802
+ break;
803
+
804
+ case 'INTERVAL':
805
+ $rule_array['frequency'] =intval($value);
806
+ break;
807
+
808
+ case 'UNTIL':
809
+ //Is the scheduled end a date-time or just a date?
810
+ if(preg_match('/^((\d{8}T\d{6})(Z)?)/', $value))
811
+ $date = $this->parse_icalDateTime($value, new DateTimeZone('UTC'));
812
+ else
813
+ $date = $this->parse_icalDate($value, new DateTimeZone('UTC'));
814
+
815
+ $rule_array['schedule_last'] = $date;
816
+ break;
817
+
818
+ case 'BYDAY':
819
+ $byday = $value;
820
+ break;
821
+
822
+ case 'BYMONTHDAY':
823
+ $bymonthday = $value;
824
+ break;
825
+ endswitch;
826
+
827
+ endforeach;
828
+
829
+ //Meta-data for Weekly and Monthly schedules
830
+ if($rule_array['schedule']=='monthly'):
831
+ if(isset($byday)){
832
+ preg_match('/(\d+)([a-zA-Z]+)/', $byday, $matches);
833
+ $rule_array['schedule_meta'] ='BYDAY='.$matches[1].$matches[2];
834
+
835
+ }elseif(isset($bymonthday)){
836
+ $rule_array['schedule_meta'] ='BYMONTHDAY='.$bymonthday;
837
+
838
+ }else{
839
+ throw new Exception('Incomplete scheduling information');
840
+ }
841
+
842
+ elseif($rule_array['schedule']=='weekly'):
843
+ preg_match('/([a-zA-Z,]+)/', $byday, $matches);
844
+ $rule_array['schedule_meta'] =explode(',',$matches[1]);
845
+
846
+ endif;
847
+
848
+ return $rule_array;
849
+ }
850
+
851
+
852
+ } // end class
includes/debug.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Checks if wp_footer() is called.
4
+ */
5
+ class EventOrganiser_No_WP_Footer
6
+ {
7
+
8
+ /**
9
+ * Instance
10
+ * @static
11
+ * @access private
12
+ * @var object
13
+ */
14
+ private static $instance;
15
+
16
+ /**
17
+ * Was `wp_footer()` fired?
18
+ * @static
19
+ * @var bool
20
+ */
21
+ static $footer_fired = false;
22
+
23
+ /**
24
+ * Creates a new static instance
25
+ * @static
26
+ * @return void
27
+ */
28
+ public static function get_instance() {
29
+ if ( ! self::$instance ) {
30
+ self::$instance = new self();
31
+ }
32
+ return self::$instance;
33
+ }
34
+
35
+ /**
36
+ * Hook the functions.
37
+ * Check for wp_footer() only on admin pages (via shutdown).
38
+ * Display notice only if the option 'sh_no_wp_footer' is present in the database, but is not set to 'suppress'.
39
+ */
40
+ private function __construct() {
41
+ add_action( 'shutdown', array( __CLASS__, 'did_footer' ) );
42
+ }
43
+
44
+ /**
45
+ * Checks if wp_footer() was fired.
46
+ * Hooked onto shutdown.
47
+ * If wp_footer() wasn't fired it sets 'sh_no_wp_footer' option to true, unless it already set to 'supress'
48
+ * If wp_footer() was fired it removes the 'sh_no_wp_footer' option.
49
+ */
50
+ static function did_footer(){
51
+ if( !did_action( 'wp_footer' ) )
52
+ update_option( 'eo_wp_footer_present', -1 );
53
+ elseif( did_action( 'wp_footer' ) )
54
+ update_option( 'eo_wp_footer_present', 1 );
55
+ }
56
+ }
57
+ $eo_no_wp_footer = EventOrganiser_No_WP_Footer::get_instance();
includes/deprecated.php ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ *@package deprecated
4
+ */
5
+ /**
6
+ * Returns an array of DateTime objects for each start date of occurrence
7
+ * @since 1.0.0
8
+ * @deprecated 1.5
9
+ * @see eo_get_the_occurrences_of()
10
+ *
11
+ * @param int $post_id Optional, the event (post) ID,
12
+ * @return array|false Array of DateTime objects of the start date-times of occurences. False if none exist.
13
+ */
14
+ function eo_get_the_occurrences($post_id=0){
15
+ //_deprecated_function( __FUNCTION__, '1.5', 'eo_get_the_occurrences_of()' );
16
+ $occurrences = eo_get_the_occurrences_of($post_id);
17
+ if( $occurrences )
18
+ return wp_list_pluck($occurrences, 'start');
19
+ return false;
20
+ }
21
+
22
+ /**
23
+ * Return true is the event is an all day event.
24
+ * @since 1.2
25
+ * @deprecated 1.5
26
+ * @see eo_is_all_day()
27
+ *
28
+ * @param int $post_id Optional, the event series (post) ID,
29
+ * @return bool True if event runs all day, or false otherwise
30
+ */
31
+ function eo_is_allday($post_id=0){
32
+ _deprecated_function( __FUNCTION__, '1.5', 'eo_is_all_day()' );
33
+ return eo_is_all_day($post_id);
34
+ }
35
+
36
+ /**
37
+ * Returns the formated date of the last occurrence of an event
38
+ * @since 1.0.0
39
+ * @deprecated 1.5 use eo_get_schedule_last
40
+ * @see eo_get_schedule_last
41
+ *
42
+ * @param string $format the format to use, using PHP Date format
43
+ * @param int $post_id Optional, the event (post) ID,
44
+ * @return string the formatted date
45
+ */
46
+ function eo_get_schedule_end($format='d-m-Y',$post_id=0){
47
+ return eo_get_schedule_last($format,$post_id);
48
+ }
49
+
50
+ /**
51
+ * Prints the formated date of the last occurrence of an event
52
+ * @since 1.0.0
53
+ * @deprecated 1.5 use eo_schedule_last
54
+ * @see eo_schedule_last
55
+ *
56
+ * @param string $format the format to use, using PHP Date format
57
+ * @param int $post_id Optional, the event (post) ID,
58
+ */
59
+ function eo_schedule_end($format='d-m-Y',$post_id=0){
60
+ echo eo_get_schedule_last($format,$post_id);
61
+ }
62
+
63
+
64
+ /**
65
+ * Returns an array with details of the event's reoccurences
66
+ * @since 1.0.0
67
+ * @deprecated 1.6
68
+ * @see eo_get_event_schedule()
69
+ *
70
+ * @param int $post_id Optional, the event (post) ID,
71
+ * @return array Schedule information
72
+ */
73
+ function eo_get_reoccurrence($post_id=0){
74
+ return eo_get_reoccurence($post_id);
75
+ }
76
+
77
+
78
+ /**
79
+ * Returns an array with details of the event's reoccurences.
80
+ * Note this is is identical to eo_get_reoccurrence() which corrects a spelling error.
81
+ *
82
+ * @param int Optional, the event (post) ID,
83
+ * @since 1.0.0
84
+ * @deprecated 1.6
85
+ * @see eo_get_event_schedule()
86
+ *
87
+ * @param int $post_id Optional, the event (post) ID,
88
+ * @return array Schedule information
89
+ */
90
+ function eo_get_reoccurence($post_id=0){
91
+ _deprecated_function( __FUNCTION__, '1.5', 'eo_get_event_schedule()' );
92
+ $post_id = (int) ( empty($post_id) ? get_the_ID() : $post_id);
93
+
94
+ if( empty($post_id) || 'event' != get_post_type($post_id) )
95
+ return false;
96
+
97
+ $return = eo_get_event_schedule( $post_id );
98
+
99
+ if ( !$return )
100
+ return false;
101
+
102
+ $return['reoccurrence'] =$return['schedule'];
103
+ $return['meta'] = $return['schedule_meta'];
104
+ $return['end'] = $return['schedule_last'];
105
+ return $return;
106
+ }
107
+
108
+
109
+ /**
110
+ * Returns the colour of a category associated with the event
111
+ * @since 1.3.3
112
+ * @deprecated 1.6
113
+ * @see eo_get_event_color()
114
+ *
115
+ * @param int $post_id The event (post) ID
116
+ * @return string The colour of the category in HEX format
117
+ */
118
+ function eo_event_color($post_id=0){
119
+ _deprecated_function( __FUNCTION__, '1.6', 'eo_get_event_color()' );
120
+ return eo_get_event_color($post_id);
121
+ }
122
+
123
+
124
+ /**
125
+ * Retrieve array of venues. Acts as a wrapper for get_terms, except hide_empty defaults to false.
126
+ * @since 1.0.0
127
+ * @deprecated 1.6
128
+ * @see eo_get_venues()
129
+ *
130
+ * @param string|array $args The values of what to search for when returning venues
131
+ * @return array List of Term (venue) Objects
132
+ */
133
+ function eo_get_the_venues($args=array()){
134
+ _deprecated_function( __FUNCTION__, '1.6', 'eo_get_venues()' );
135
+ return eo_get_venues($args);
136
+ }
137
+
138
+ /**
139
+ * Deletes the event data associated with post. Should be called when an event is being deleted.
140
+ * This does not delete the post.
141
+ * @since 1.0.0
142
+ * @deprecated 1.6
143
+ * @see eo_delete_event_occurrences()
144
+ *
145
+ * @param int $post_id The event (post) ID
146
+ */
147
+ function eventorganiser_event_delete($post_id){
148
+ eo_delete_event_occurrences($post_id);
149
+ }
150
+
151
+ ?>
includes/event-organiser-ajax.php ADDED
@@ -0,0 +1,596 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Deals with the plug-in's AJAX requests
4
+ */
5
+
6
+ add_action( 'wp_ajax_eventorganiser-fullcal', 'eventorganiser_public_fullcalendar' );
7
+ add_action( 'wp_ajax_nopriv_eventorganiser-fullcal', 'eventorganiser_public_fullcalendar' );
8
+ add_action( 'wp_ajax_event-admin-cal', 'eventorganiser_admin_calendar' );
9
+ add_action( 'wp_ajax_eofc-format-time', 'eventorganiser_admin_cal_time_format' );
10
+ add_action( 'wp_ajax_eo-search-venue', 'eventorganiser_search_venues' );
11
+ add_action( 'wp_ajax_nopriv_eo_widget_agenda', 'eventorganiser_widget_agenda' );
12
+ add_action( 'wp_ajax_eo_widget_agenda', 'eventorganiser_widget_agenda' );
13
+ add_action( 'wp_ajax_nopriv_eo_widget_cal', 'eventorganiser_widget_cal' );
14
+ add_action( 'wp_ajax_eo_widget_cal', 'eventorganiser_widget_cal' );
15
+
16
+ /**
17
+ * Ajax response for the public full calendar. This returns events to be displayed on the front-end full calendar
18
+ *
19
+ *@since 1.3
20
+ *@access private
21
+ *@ignore
22
+ */
23
+ function eventorganiser_public_fullcalendar() {
24
+ $request = array(
25
+ 'event_start_before'=>$_GET['end'],
26
+ 'event_end_after'=>$_GET['start'],
27
+ );
28
+
29
+ $time_format = !empty($_GET['timeformat']) ? $_GET['timeformat'] : get_option('time_format');
30
+
31
+ //Restrict by category and/or venue
32
+ if( !empty($_GET['category']) ){
33
+ $cats = explode(',',esc_attr($_GET['category']));
34
+ $request['tax_query'][] = array(
35
+ 'taxonomy' => 'event-category',
36
+ 'field' => 'slug',
37
+ 'terms' => $cats,
38
+ 'operator' => 'IN'
39
+ );
40
+ }
41
+
42
+ if( !empty($_GET['venue']) ){
43
+ $venues = explode(',',esc_attr($_GET['venue']));
44
+ $request['tax_query'][] = array(
45
+ 'taxonomy' => 'event-venue',
46
+ 'field' => 'slug',
47
+ 'terms' => $venues,
48
+ 'operator' => 'IN'
49
+ );
50
+ }
51
+
52
+ $presets = array('numberposts'=>-1, 'group_events_by'=>'','showpastevents'=>true);
53
+
54
+ //Retrieve events
55
+ $query = array_merge($request,$presets,array($time_format));
56
+ $key = 'eo_fc_'.md5(serialize($query));
57
+ $calendar = get_transient('eo_full_calendar_public');
58
+ if( $calendar && is_array($calendar) && isset($calendar[$key]) ){
59
+ echo json_encode($calendar[$key]);
60
+ exit;
61
+ }
62
+
63
+ $events = eo_get_events($query);
64
+ $eventsarray = array();
65
+
66
+ //Blog timezone
67
+ $tz = eo_get_blog_timezone();
68
+
69
+ //Loop through events
70
+ global $post;
71
+ if ($events) :
72
+ foreach ($events as $post) :
73
+ setup_postdata( $post );
74
+ $event=array();
75
+ $event['className']=array('eo-event');
76
+
77
+ //Title and url
78
+ $event['title']=html_entity_decode(get_the_title($post->ID),ENT_QUOTES,'UTF-8');
79
+ $link = esc_js(get_permalink( $post->ID));
80
+ $event['url'] = apply_filters('eventorganiser_calendar_event_link',$link,$post->ID,$post->occurrence_id);
81
+
82
+ //All day or not?
83
+ $event['allDay'] = eo_is_all_day();
84
+
85
+ //Get Event Start and End date, set timezone to the blog's timzone
86
+ $event_start = new DateTime($post->StartDate.' '.$post->StartTime, $tz);
87
+ $event_end = new DateTime($post->EndDate.' '.$post->FinishTime, $tz);
88
+ $event['start']= $event_start->format('Y-m-d\TH:i:s\Z');
89
+ $event['end']= $event_end->format('Y-m-d\TH:i:s\Z');
90
+
91
+ //Don't use get_the_excerpt as this adds a link
92
+ $excerpt_length = apply_filters('excerpt_length', 55);
93
+
94
+ $description = wp_trim_words( strip_shortcodes(get_the_content()), $excerpt_length, '...' );
95
+
96
+ if( $event_start->format('Y-m-d') != $event_end->format('Y-m-d') ){
97
+ //Start & ends on different days
98
+
99
+ if( !eo_is_all_day() ){
100
+ //Not all day, include time
101
+ $date = eo_format_datetime($event_start,'F j '.$time_format).' - '.eo_format_datetime($event_end,'F j '.$time_format);
102
+ }else{
103
+ //All day, don't include date
104
+ if( $event_start->format('Y-m') == $event_end->format('Y-m') ){
105
+ //Same month
106
+ $date = eo_format_datetime($event_start,'F j').' - '.eo_format_datetime($event_end,'j, Y');
107
+ }else{
108
+ //Different month
109
+ $date = eo_format_datetime($event_start,'F j').' - '.eo_format_datetime($event_end,'F j');
110
+ }
111
+ }
112
+
113
+ }else{
114
+ //Start and end on the same day
115
+
116
+ if( !eo_is_all_day() ){
117
+ //Not all day, include time
118
+ $date = eo_format_datetime($event_start,$format="F j, Y $time_format").' - '.eo_format_datetime($event_end,$time_format);
119
+ }else{
120
+ //All day
121
+ $date = eo_format_datetime($event_start,$format='F j, Y');
122
+ }
123
+ }
124
+ $description = $date.'</br></br>'.$description;
125
+
126
+ $event['description'] = apply_filters('eventorganiser_event_tooltip', $description, $post->ID,$post->occurrence_id,$post);
127
+
128
+ //Colour past events
129
+ $now = new DateTime(null,$tz);
130
+ if($event_start <= $now)
131
+ $event['className'][] = 'eo-past-event';
132
+ else
133
+ $event['className'][] = 'eo-future-event';
134
+
135
+ //Add class if event is all day
136
+ if( eo_is_all_day() )
137
+ $event['className'][] = 'eo-all-day';
138
+
139
+ //Include venue if this is set
140
+ $venue = eo_get_venue($post->ID);
141
+
142
+ if($venue && !is_wp_error($venue)){
143
+ $event['className'][]= 'venue-'.eo_get_venue_slug($post->ID);
144
+ $event['venue']=$venue;
145
+ }
146
+
147
+ //Event categories
148
+ $terms = get_the_terms( $post->ID, 'event-category' );
149
+ $event['category']=array();
150
+ if($terms):
151
+ foreach ($terms as $term):
152
+ $event['category'][]= $term->slug;
153
+ $event['className'][]='category-'.$term->slug;
154
+ endforeach;
155
+ endif;
156
+
157
+ //Event colour
158
+ if( eo_get_event_color() )
159
+ $event['color'] = eo_get_event_color();
160
+
161
+ //Add event to array
162
+ $event = apply_filters('eventorganiser_fullcalendar_event',$event, $post->ID,$post->occurrence_id);
163
+ if( $event )
164
+ $eventsarray[]=$event;
165
+
166
+ endforeach;
167
+ wp_reset_postdata();
168
+ endif;
169
+
170
+ if( !$calendar || !is_array($calendar) )
171
+ $calendar = array();
172
+
173
+ $calendar[$key] = $eventsarray;
174
+
175
+ set_transient('eo_full_calendar_public',$calendar, 60*60*24);
176
+
177
+ //Echo result and exit
178
+ echo json_encode($eventsarray);
179
+ exit;
180
+ }
181
+
182
+
183
+ /**
184
+ * Ajax response for the admin full calendar.
185
+ *
186
+ * This gets events and generates summaries for events to be displayed.
187
+ * in the admin 'calendar view'.
188
+ * Applies 'eventorganiser_admin_cal_summary' to event summary
189
+ * Applies eventorganiser_admin_calendar to the event array
190
+ *
191
+ *@since 1.0
192
+ *@access private
193
+ *@ignore
194
+ */
195
+ function eventorganiser_admin_calendar() {
196
+ //request
197
+ $request = array(
198
+ 'event_end_after'=>$_GET['start'],
199
+ 'event_start_before'=>$_GET['end']
200
+ );
201
+
202
+ //Presets
203
+ $presets = array(
204
+ 'posts_per_page'=>-1,
205
+ 'post_type'=>'event',
206
+ 'group_events_by'=>'',
207
+ 'perm' => 'readable');
208
+
209
+ $calendar = get_transient('eo_full_calendar_admin');
210
+ $key = $_GET['start'].'--'.$_GET['end'];
211
+
212
+ if( ( !defined('WP_DEBUG') || !WP_DEBUG ) && $calendar && is_array($calendar) && isset($calendar[$key]) ){
213
+ echo json_encode($calendar[$key]);
214
+ exit;
215
+ }
216
+
217
+ //Create query
218
+ $query_array = array_merge($presets, $request);
219
+ $query = new WP_Query($query_array );
220
+
221
+ //Retrieve events
222
+ $query->get_posts();
223
+ $eventsarray = array();
224
+
225
+ //Blog timezone
226
+ $tz = eo_get_blog_timezone();
227
+
228
+ //Loop through events
229
+ global $post;
230
+ if ( $query->have_posts() ) :
231
+ while ( $query->have_posts() ) : $query->the_post();
232
+ $event=array();
233
+ $colour='';
234
+ //Get title, append status if applicable
235
+ $title = get_the_title();
236
+ if(!empty($post->post_password)){
237
+ $title.=' - '.__('Protected');
238
+ }elseif($post->post_status=='private'){
239
+ $title.=' - '.__('Private');
240
+ }elseif ($post->post_status=='draft'){
241
+ $title.=' - '.__('Draft');
242
+ }
243
+ $event['title']= html_entity_decode ($title,ENT_QUOTES,'UTF-8');
244
+
245
+ $schedule = eo_get_event_schedule($post->ID);
246
+
247
+ //Check if all day, set format accordingly
248
+ if( $schedule['all_day'] ){
249
+ $event['allDay'] = true;
250
+ $format = get_option('date_format');
251
+ }else{
252
+ $event['allDay'] = false;
253
+ $format = get_option('date_format').' '.get_option('time_format');
254
+ }
255
+
256
+ //Get author (or organiser)
257
+ $organiser = get_userdata( $post->post_author)->display_name;
258
+
259
+ //Get Event Start and End date, set timezone to the blog's timzone
260
+ $event_start = new DateTime($post->StartDate.' '.$post->StartTime, $tz);
261
+ $event_end = new DateTime($post->EndDate.' '.$post->FinishTime, $tz);
262
+
263
+ $event['start']= $event_start->format('Y-m-d\TH:i:s\Z');
264
+ $event['end']= $event_end->format('Y-m-d\TH:i:s\Z');
265
+
266
+ //Produce summary of event
267
+ $summary= "<table class='form-table' >"
268
+ ."<tr><th> ".__('Start','eventorganiser').": </th><td> ".eo_format_datetime($event_start,$format)."</td></tr>"
269
+ ."<tr><th> ".__('End','eventorganiser').": </th><td> ".eo_format_datetime($event_end, $format)."</td></tr>"
270
+ ."<tr><th> ".__('Organiser','eventorganiser').": </th><td>".$organiser."</td></tr>";
271
+
272
+ $event['className']=array('event');
273
+
274
+ $now = new DateTime(null,$tz);
275
+ if($event_start <= $now)
276
+ $event['className'][]='past-event';
277
+
278
+ //Include venue if this is set
279
+ $venue = eo_get_venue($post->ID);
280
+
281
+ if($venue && !is_wp_error($venue)){
282
+ $summary .="<tr><th>".__('Where','eventorganiser').": </th><td>".eo_get_venue_name($venue)."</td></tr>";
283
+ $event['className'][]= 'venue-'.eo_get_venue_slug($post->ID);
284
+ $event['venue']=$venue;
285
+ }
286
+
287
+ $summary = apply_filters('eventorganiser_admin_cal_summary',$summary,$post->ID,$post->occurrence_id,$post);
288
+
289
+ $summary .= "</table><p>";
290
+
291
+ //Include schedule summary if event reoccurrs
292
+
293
+ if( $schedule['schedule'] != 'once' )
294
+ $summary .='<em>'.__('This event reoccurs','eventorganiser').' '.eo_get_schedule_summary().'</em>';
295
+ $summary .='</p>';
296
+
297
+ //Include edit link in summary if user has permission
298
+ if (current_user_can('edit_event', $post->ID)){
299
+ $edit_link = get_edit_post_link( $post->ID,'');
300
+ $summary .= "<span class='edit'><a title='Edit this item' href='".$edit_link."'> ".__('Edit Event','eventorganiser')."</a></span>";
301
+ $event['url']= $edit_link;
302
+ }
303
+
304
+ //Include a delete occurrence link in summary if user has permission
305
+ if (current_user_can('delete_event', $post->ID)){
306
+ $admin_url = admin_url('edit.php');
307
+
308
+ $delete_url = add_query_arg(array(
309
+ 'post_type'=>'event',
310
+ 'page'=>'calendar',
311
+ 'series'=>$post->ID,
312
+ 'event'=>$post->occurrence_id,
313
+ 'action'=>'delete_occurrence'
314
+ ),$admin_url);
315
+
316
+ $delete_url = wp_nonce_url( $delete_url , 'eventorganiser_delete_occurrence_'.$post->occurrence_id);
317
+
318
+ $summary .= "<span class='delete'>
319
+ <a class='submitdelete' style='color:red;float:right' title='".__('Delete this occurrence','eventorganiser')."' href='".$delete_url."'> ".__('Delete this occurrence','eventorganiser')."</a>
320
+ </span>";
321
+
322
+ if( $schedule['schedule'] !='once'){
323
+ $break_url = add_query_arg(array(
324
+ 'post_type'=>'event',
325
+ 'page'=>'calendar',
326
+ 'series'=>$post->ID,
327
+ 'event'=>$post->occurrence_id,
328
+ 'action'=>'break_series'
329
+ ),$admin_url);
330
+ $break_url = wp_nonce_url( $break_url , 'eventorganiser_break_series_'.$post->occurrence_id);
331
+
332
+ $summary .= "<span class='break'>
333
+ <a class='submitbreak' style='color:red;float:right;padding-right: 2em;' title='".__('Break this series','eventorganiser')."' href='".$break_url."'> ".__('Break this series','eventorganiser')."</a>
334
+ </span>";
335
+ }
336
+
337
+ }
338
+
339
+ //Event categories
340
+ $terms = get_the_terms( $post->ID, 'event-category' );
341
+ $event['category']=array();
342
+ if($terms):
343
+ foreach ($terms as $term):
344
+ $event['category'][]= $term->slug;
345
+ $event['className'][]='category-'.$term->slug;
346
+ endforeach;
347
+ endif;
348
+
349
+ //Event colour
350
+ if( eo_get_event_color() )
351
+ $event['color'] = eo_get_event_color();
352
+
353
+ //Event summary
354
+ $event['summary'] = '<div id="eo-cal-meta">'.$summary.'</div>';
355
+
356
+ //Filter the event array
357
+ $event = apply_filters('eventorganiser_admin_calendar',$event, $post);
358
+
359
+ //Add event to array
360
+ $eventsarray[]=$event;
361
+ endwhile;
362
+ endif;
363
+
364
+ if( !$calendar || !is_array($calendar) )
365
+ $calendar = array();
366
+
367
+ $calendar[$key] = $eventsarray;
368
+
369
+ set_transient('eo_full_calendar_admin',$calendar, 60*60*24);
370
+
371
+ //Echo result and exit
372
+ echo json_encode($eventsarray);
373
+ exit;
374
+ }
375
+
376
+
377
+ /**
378
+ * Ajax response for the widget calendar
379
+ *
380
+ * This gets the month being requested and generates the
381
+ * html code to view that month and its events.
382
+ *
383
+ *@since 1.0
384
+ *@access private
385
+ *@ignore
386
+ */
387
+ function eventorganiser_widget_cal() {
388
+
389
+ /*Retrieve the month we are after. $month must be a
390
+ DateTime object of the first of that month*/
391
+ if(isset($_GET['eo_month'])){
392
+ $month = new DateTime($_GET['eo_month'].'-01');
393
+ }else{
394
+ $month = new DateTime('now');
395
+ $month = date_create($month->format('Y-m-1'));
396
+ }
397
+
398
+ $args = array();
399
+
400
+ //Restrict by category and/or venue
401
+ foreach( array('event-venue','event-category') as $tax ){
402
+ if( empty($_GET[$tax]) )
403
+ continue;
404
+
405
+ $terms = explode(',',trim($_GET[$tax]));
406
+
407
+ $args['tax_query'][] = array(
408
+ 'taxonomy' => $tax,
409
+ 'field' => 'slug',
410
+ 'terms' => $terms,
411
+ 'operator' => 'IN'
412
+ );
413
+ }
414
+
415
+ //Options for the calendar
416
+ $args['showpastevents'] = (empty($_GET['showpastevents']) ? 0 : 1);
417
+
418
+ echo json_encode(EO_Calendar_Widget::generate_output($month,$args));
419
+ exit;
420
+ }
421
+
422
+
423
+ /**
424
+ * Ajax response for the agenda widget
425
+ *
426
+ * This gets the month being viewed and generates the
427
+ *
428
+ *@since 1.0
429
+ *@access private
430
+ *@ignore
431
+ */
432
+ function eventorganiser_widget_agenda() {
433
+ global $wpdb;
434
+
435
+ $number = (int) $_GET['instance_number'];
436
+ $wid = new EO_Events_Agenda_Widget();
437
+ $settings = $wid->get_settings();
438
+ $instance = $settings[$number];
439
+ $today= new DateTime('now', eo_get_blog_timezone());
440
+ $query=array();
441
+ $return_array = array();
442
+
443
+ $query['mode'] = !empty($instance['mode']) ? $instance['mode'] : 'day';
444
+ $query['direction'] = intval($_GET['direction']);
445
+ $query['date'] = ($query['direction'] <1? $_GET['start'] : $_GET['end']);
446
+ $query['order'] = ($query['direction'] <1? 'DESC' : 'ASC');
447
+
448
+ $key = 'eo_ag_'.md5(serialize($query)).get_locale();
449
+ $agenda = get_transient('eo_widget_agenda');
450
+ if( $agenda && is_array($agenda) && isset($agenda[$key]) ){
451
+ echo json_encode($agenda[$key]);
452
+ exit;
453
+ }
454
+
455
+ if( 'day' == $query['mode'] ){
456
+ //Day mode
457
+ $selectDates="SELECT DISTINCT StartDate FROM {$wpdb->eo_events}";
458
+ $whereDates = " WHERE {$wpdb->eo_events}.StartDate".( $query['order']=='ASC' ? " >= " : " <= ")."%s ";
459
+ $whereDates .= " AND {$wpdb->eo_events}.StartDate >= %s ";
460
+ $orderlimit = "ORDER BY {$wpdb->eo_events}.StartDate {$query['order']} LIMIT 4";
461
+ $dates = $wpdb->get_col($wpdb->prepare($selectDates.$whereDates.$orderlimit, $query['date'],$today->format('Y-m-d')));
462
+
463
+ if(!$dates)
464
+ return false;
465
+
466
+ $query['date1'] = min($dates[0],$dates[count($dates)-1]);
467
+ $query['date2'] = max($dates[0],$dates[count($dates)-1]);
468
+
469
+ }elseif( 'week' == $query['mode'] ){
470
+ //Week mode - find the week of the next/previous event
471
+ $selectDates="SELECT DISTINCT StartDate FROM {$wpdb->eo_events}";
472
+ $whereDates = " WHERE {$wpdb->eo_events}.StartDate".( $query['order']=='ASC' ? " > " : " < ")."%s ";
473
+ $whereDates .= " AND {$wpdb->eo_events}.StartDate >= %s ";
474
+ $orderlimit = "ORDER BY {$wpdb->eo_events}.StartDate {$query['order']} LIMIT 1";
475
+ $date = $wpdb->get_row($wpdb->prepare($selectDates.$whereDates.$orderlimit, $query['date'],$today->format('Y-m-d')));
476
+
477
+ if(!$date)
478
+ return false;
479
+
480
+ $datetime = new DateTime($date->StartDate, eo_get_blog_timezone());
481
+
482
+ //Get the week day, and the start of the week
483
+ $week_start_day = (int) get_option('start_of_week');
484
+ $event_day = (int) $datetime->format('w');
485
+ $offset_from_week_start = ($event_day - $week_start_day +7)%7;
486
+ $week_start_date = clone $datetime;
487
+ $week_start_date->modify('- '.$offset_from_week_start.' days');
488
+ $week_end_date = clone $week_start_date;
489
+ $week_end_date->modify('+6 days');//Query is inclusive.
490
+
491
+ $query['date1'] = $week_start_date->format('Y-m-d');
492
+ $query['date2'] = $week_end_date->format('Y-m-d');
493
+
494
+ }else{
495
+ //Month mode - find the month of the next date
496
+ $selectDates="SELECT DISTINCT StartDate FROM {$wpdb->eo_events}";
497
+ $whereDates = " WHERE {$wpdb->eo_events}.StartDate".( $query['order']=='ASC' ? " > " : " < ")."%s ";
498
+ $whereDates .= " AND {$wpdb->eo_events}.StartDate >= %s ";
499
+ $orderlimit = "ORDER BY {$wpdb->eo_events}.StartDate {$query['order']} LIMIT 1";
500
+ $date = $wpdb->get_row($wpdb->prepare($selectDates.$whereDates.$orderlimit, $query['date'],$today->format('Y-m-d')));
501
+
502
+ if(!$date)
503
+ return false;
504
+
505
+ $datetime = new DateTime($date->StartDate, eo_get_blog_timezone());
506
+ $query['date1'] = $datetime->format('Y-m-01');
507
+ $query['date2'] = $datetime->format('Y-m-t');
508
+ }
509
+
510
+ $events = eo_get_events(array(
511
+ 'event_start_after'=>$query['date1'],
512
+ 'event_start_before'=>$query['date2']
513
+ ));
514
+
515
+ global $post;
516
+ foreach ($events as $post):
517
+ $return_array[] = array(
518
+ 'StartDate'=>$post->StartDate,
519
+ 'display'=>eo_get_the_start($instance['group_format']),
520
+ 'time'=> ( ($instance['mode']=='day' && eo_is_all_day()) ? __('All Day','eventorganiser') : eo_get_the_start($instance['item_format']) ),
521
+ 'post_title'=>get_the_title(),
522
+ 'color'=>eo_get_event_color(),
523
+ 'event_url'=>get_permalink(),
524
+ 'link'=>'<a href="'.get_permalink().'">'.__('View','eventorganiser').'</a>',
525
+ 'Glink'=>'<a href="'.eo_get_the_GoogleLink().'" target="_blank">'.__('Add To Google Calendar','eventorganiser').'</a>'
526
+ );
527
+ endforeach;
528
+
529
+ if( !$agenda || !is_array($agenda) )
530
+ $agenda = array();
531
+
532
+ $agenda[$key] = $return_array;
533
+
534
+ set_transient('eo_widget_agenda',$agenda, 60*60*24);
535
+
536
+ echo json_encode($return_array);
537
+ exit;
538
+ }
539
+
540
+
541
+ /**
542
+ * Ajax response for searcheing venues. Searches by venue name.
543
+ *
544
+ *@since 1.0
545
+ *@access private
546
+ *@ignore
547
+ */
548
+ function eventorganiser_search_venues() {
549
+
550
+ // Query the venues with the given term
551
+ $value = trim(esc_attr($_GET["term"]));
552
+ $venues = eo_get_venues(array('search'=>$value));
553
+
554
+ foreach($venues as $venue){
555
+ $venue_id = (int) $venue->term_id;
556
+
557
+ if( !isset($term->venue_address) ){
558
+ /* This is all deprecated - use the API {@link http://wp-event-organiser.com/documentation/function/eo_get_venue_address/} */
559
+ $address = eo_get_venue_address($venue_id);
560
+ $venue->venue_address = isset($address['address']) ? $address['address'] : '';
561
+ $venue->venue_postal = isset($address['postcode']) ? $address['postcode'] : '';
562
+ $venue->venue_postcode = isset($address['postcode']) ? $address['postcode'] : '';
563
+ $venue->venue_city = isset($address['city']) ? $address['city'] : '';
564
+ $venue->venue_country = isset($address['country']) ? $address['country'] : '';
565
+ $venue->venue_state = isset($address['state']) ? $address['state'] : '';
566
+ }
567
+
568
+ if( !isset($venue->venue_lat) || !isset($venue->venue_lng) ){
569
+ $venue->venue_lat = number_format(floatval(eo_get_venue_lat($venue_id)), 6);
570
+ $venue->venue_lng = number_format(floatval(eo_get_venue_lng($venue_id)), 6);
571
+ }
572
+ }
573
+
574
+ $novenue = array('term_id'=>0,'name'=>__('No Venue','eventorganiser'));
575
+ $venues =array_merge (array($novenue),$venues);
576
+
577
+ //echo JSON to page
578
+ $response = $_GET["callback"] . "(" . json_encode($venues) . ")";
579
+ echo $response;
580
+ exit;
581
+ }
582
+
583
+ /**
584
+ * Saves the ajax request to update the 24/12 hour setting of admin calendar
585
+ *
586
+ *@since 1.5
587
+ *@access private
588
+ *@ignore
589
+ */
590
+ function eventorganiser_admin_cal_time_format(){
591
+ $is24 = ( $_POST['is24'] == 'false' ? 1: 0 );
592
+ $user =wp_get_current_user();
593
+ $is12hour = update_user_meta($user->ID,'eofc_time_format',$is24);
594
+ exit();
595
+ }
596
+ ?>
includes/event-organiser-archives.php ADDED
@@ -0,0 +1,517 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Handles the query manipulation of events
4
+ */
5
+
6
+ /**
7
+ * Registers our custom query variables
8
+ *
9
+ * Hooked onto query_vars
10
+ * @since 1.0.0
11
+ * @access private
12
+ * @ignore;
13
+ *
14
+ * @param array $qvars Query variables
15
+ * @param array Query variables with plug-in added variables
16
+ */
17
+ function eventorganiser_register_query_vars( $qvars ){
18
+ //Add these query variables
19
+ $qvars[] = 'venue';//Depreciated
20
+ $qvars[] = 'ondate';
21
+ $qvars[] = 'showrepeats';
22
+ $qvars[] = 'eo_interval';
23
+ $qvars[] = 'event_start_before';
24
+ $qvars[] = 'event_start_after';
25
+ $qvars[] = 'event_end_before';
26
+ $qvars[] = 'event_after_after';
27
+ $qvars[] = 'event_series';
28
+ return $qvars;
29
+ }
30
+ add_filter('query_vars', 'eventorganiser_register_query_vars' );
31
+
32
+
33
+ /**
34
+ * Parses event queries and alters the WP_Query object appropriately
35
+ *
36
+ * Parse's the query, and sets date range and other event specific query variables.
37
+ * If query is for 'event' post type - the posts_* filters are added.
38
+ *
39
+ * Hooked onto pre_get_posts
40
+ * @since 1.0.0
41
+ * @access private
42
+ * @ignore;
43
+ *
44
+ * @param WP_Query $query The query
45
+ */
46
+ function eventorganiser_pre_get_posts( $query ) {
47
+
48
+ //Deprecated, use event-venue instead.
49
+ if( !empty($query->query_vars['venue']) ){
50
+ $venue = $query->get('venue');
51
+ $query->set('event-venue',$venue);
52
+ $query->set( 'post_type', 'event' );
53
+ }
54
+
55
+ //If the query is for eo-events feed, set post type
56
+ if( $query->is_feed( 'eo-events' ) ){
57
+ $query->set( 'post_type', 'event' );
58
+ }
59
+
60
+ //If querying for all events starting on given date, set the date parameters
61
+ if( !empty($query->query_vars['ondate']) ) {
62
+
63
+ $ondate_start = str_replace('/','-',$query->query_vars['ondate']);
64
+ $ondate_end = str_replace('/','-',$query->query_vars['ondate']);
65
+
66
+ $parts = count(explode('-',$ondate_start));
67
+ if( $parts == 1 && is_numeric($ondate_start) ){
68
+ //Numeric - interpret as year
69
+ $ondate_start .= '-01-01';
70
+ $ondate_end .= '-12-31';
71
+ }elseif( $parts == 2 ){
72
+ // 2012-01 format: interpret as month
73
+ $ondate_start .= '-01';
74
+ try{
75
+ $end = new DateTime($ondate_start);
76
+ $ondate_end = $end->format('Y-m-t');
77
+ }catch( Exception $e){
78
+ $query->set('ondate',false);
79
+ break;
80
+ }
81
+ }
82
+
83
+ $query->set( 'post_type', 'event' );
84
+ $query->set( 'event_start_before', $ondate_end );
85
+ $query->set( 'event_end_after', $ondate_start );
86
+ }
87
+
88
+ //If not on event, stop here.
89
+ if( ! eventorganiser_is_event_query( $query, true ) )
90
+ return $query;
91
+
92
+ $blog_now = new DateTime(null, eo_get_blog_timezone());
93
+
94
+ //Determine whether or not to show past events and each occurrence. //If not set, use options
95
+ if( !is_admin() && !is_single() && !$query->is_feed('eo-events') && !isset($query->query_vars['showpastevents']) ){
96
+ //If showpastevents is not set - use options (except for admin / single pages.
97
+ $query->set('showpastevents', eventorganiser_get_option('showpast') );
98
+ }
99
+
100
+ //Deprecated: showrepeats - use group_events_by instead
101
+ if( isset($query->query_vars['showrepeats']) && !isset($query->query_vars['group_events_by']) ){
102
+ if( !$query->query_vars['showrepeats'] )
103
+ $query->set('group_events_by','series');
104
+ }
105
+
106
+ //Determine how to group events: by series or show each occurrence
107
+ if( !isset($query->query_vars['group_events_by']) ){
108
+
109
+ //Group by isn't set - default depends on context:
110
+ if( $query->is_main_query() && (is_admin() || is_single() || $query->is_feed('eo-events') ) ){
111
+
112
+ //If in admin or single page - we probably don't want to see duplicates of (recurrent) events - unless specified otherwise.
113
+ $query->set('group_events_by','series');
114
+
115
+ }elseif( eventorganiser_get_option('group_events') == 'series' ){
116
+ //In other instances (archives, shortcode listing) if showrepeats option is false display only the next event.
117
+ $query->set('group_events_by','series');
118
+ }else{
119
+ $query->set('group_events_by','occurrence');
120
+ }
121
+ }
122
+
123
+ //Parse user input as date-time objects
124
+ $date_objs = array('event_start_after'=>'','event_start_before'=>'','event_end_after'=>'','event_end_before'=>'');
125
+ foreach($date_objs as $prop => $value):
126
+ $date = $query->get($prop);
127
+ try{
128
+ $date = ( empty($date) ? false : new DateTime($date, eo_get_blog_timezone()) );
129
+ }catch( Exception $e){
130
+ $date = false;
131
+ }
132
+ $date_objs[$prop] = $date;
133
+ $query->set($prop, $date);
134
+
135
+ endforeach;
136
+
137
+ //If eo_interval is set, determine date ranges
138
+ if( !empty($query->query_vars['eo_interval']) ){
139
+ switch($query->get('eo_interval')):
140
+ case 'expired':
141
+ $meta_query = (array) $query->get('meta_query');
142
+ $meta_query[] =array(
143
+ 'key' => '_eventorganiser_schedule_last_finish',
144
+ 'value' => $blog_now->format('Y-m-d H:i:s'),
145
+ 'compare' => '<='
146
+ );
147
+ $query->set('meta_query',$meta_query) ;
148
+ break;
149
+
150
+ case 'future':
151
+ $meta_query = $query->get('meta_query');
152
+ $meta_query = empty($meta_query) ? array() : $meta_query;
153
+ $meta_query[] =array(
154
+ 'key' => '_eventorganiser_schedule_last_start',
155
+ 'value' => $blog_now->format('Y-m-d H:i:s'),
156
+ 'compare' => '>='
157
+ );
158
+ $query->set('meta_query',$meta_query) ;
159
+ break;
160
+
161
+ case 'P1D':
162
+ case 'P1W':
163
+ case 'P1M':
164
+ case 'P6M':
165
+ case 'P1Y':
166
+ //I hate you php5.2
167
+ $intervals = array('P1D'=>'+1 day', 'P1W'=>'+1 week','P1M'=>'+1 month','P6M'=>'+6 month','P1Y'=>'+1 Year');
168
+ $cutoff = clone $blog_now;
169
+ $cutoff->modify($intervals[$query->query_vars['eo_interval']]);
170
+
171
+ if( is_admin() && 'series' == $query->get('group_events_by') ){
172
+ //On admin we want to show the **first** occurrence of a recurring event which has an occurrence in the interval
173
+ global $wpdb;
174
+ $post_ids = $wpdb->get_results($wpdb->prepare(
175
+ "SELECT DISTINCT post_id FROM {$wpdb->eo_events}
176
+ WHERE {$wpdb->eo_events}.StartDate <= %s
177
+ AND {$wpdb->eo_events}.EndDate >= %s",
178
+ $cutoff->format('Y-m-d'),$blog_now->format('Y-m-d')));
179
+
180
+ if($post_ids)
181
+ $query->set('post__in',wp_list_pluck($post_ids, 'post_id'));
182
+
183
+ }else{
184
+ if( empty($date_objs['event_start_before']) || $cutoff < $date_objs['event_start_before'] ){
185
+ $date_objs['event_start_before'] = $cutoff;
186
+ }
187
+ if( empty($date_objs['event_end_after']) || $blog_now > $date_objs['event_end_after'] ){
188
+ $date_objs['event_end_after'] = $blog_now;
189
+ }
190
+ }
191
+ endswitch;
192
+ }//Endif interval set
193
+
194
+ $running_event_is_past= ( eventorganiser_get_option('runningisnotpast') ? true : false);
195
+
196
+ //Set date range according to whether we show past events
197
+ if(isset($query->query_vars['showpastevents'])&& !$query->query_vars['showpastevents'] ){
198
+ //Showing only future events
199
+
200
+ //Running event is past - Get events which start in the future
201
+ //A current event is not past - Get events which finish in the future
202
+ $key = ( $running_event_is_past ? 'event_start_after' : 'event_end_after');
203
+
204
+ //If current queried date is not set or before now, set the queried date to now
205
+ $date_objs[$key] = (empty($date_objs[$key]) || $blog_now > $date_objs[$key]) ? $blog_now : $date_objs[$key];
206
+ }
207
+
208
+
209
+ //Set event dates to 'Y-m-d H:i:s' format.
210
+ foreach ($date_objs as $prop => $datetime ){
211
+ if( !empty($datetime) )
212
+ $query->set($prop, $datetime->format('Y-m-d H:i:s'));
213
+ }
214
+
215
+ if( $query->is_feed('eo-events') ){
216
+ //Posts per page for feeds bug http://core.trac.wordpress.org/ticket/17853
217
+ add_filter('post_limits','wp17853_eventorganiser_workaround');
218
+ $query->set('posts_per_page',-1);
219
+ }
220
+
221
+ //Add the posts_* filters to modify the query
222
+ add_filter('posts_fields', 'eventorganiser_event_fields',10,2);
223
+ add_filter('posts_join', 'eventorganiser_join_tables',10,2);
224
+ add_filter('posts_where','eventorganiser_events_where',10,2);
225
+ add_filter('posts_orderby','eventorganiser_sort_events',10,2);
226
+ add_filter('posts_groupby', 'eventorganiser_event_groupby',10,2);
227
+ }
228
+ add_action( 'pre_get_posts', 'eventorganiser_pre_get_posts', 11 );
229
+
230
+ //Workaround for https://github.com/stephenharris/Event-Organiser/issues/55,
231
+ add_action( 'pre_get_posts', '__return_false', 10 );
232
+
233
+
234
+ /**
235
+ * A work around for a bug that posts_per_page is over-ridden by the posts_per_rss option. http://core.trac.wordpress.org/ticket/17853
236
+
237
+ * posts_per_rss option overirdes posts_per_page and nopaging is also set to 'false'.
238
+ * For ics feeds nopaging should be true and post_per_page should be -1. We intercept the LIMIT part of the query and remove it.
239
+ * We return '' so there is no LIMIT part to the query.
240
+ * Hooked on in eventorganiser_pre_get_posts
241
+ * @since 1.5.7
242
+ * @access private
243
+ * @ignore;
244
+ *
245
+ *@param string $limit LIMIT part of the SQL statement
246
+ *@return string Empty string
247
+ */
248
+ function wp17853_eventorganiser_workaround( $limit ){
249
+ remove_filter(current_filter(),__FUNCTION__);
250
+ return '';
251
+ }
252
+
253
+
254
+ /**
255
+ * SELECT only date fields from events and venue table for events
256
+ * All other fields deprecated and stored in post meta since 1.5
257
+ * Hooked onto posts_fields
258
+ *
259
+ *@since 1.0.0
260
+ *@access private
261
+ *@ignore
262
+ *@param string $select SELECT part of the SQL statement
263
+ *@param string $query WP_Query
264
+ *@return string
265
+ */
266
+ function eventorganiser_event_fields( $select, $query ){
267
+ global $wpdb;
268
+
269
+ if( eventorganiser_is_event_query( $query, true ) ){
270
+ $et =$wpdb->eo_events;
271
+ /* Include 'event_occurrence' for backwards compatibility. Will eventually be removed. */
272
+ /* Renaming event_id as occurrence id. Keep event_id for backwards compatibility */
273
+ if( 'series'== $query->get('group_events_by') ) {
274
+ //Work-around for group_events_by series.
275
+ $select = "{$et}.event_id, {$et}.event_id AS occurrence_id, {$et}.StartTime, min({$et}.StartDate) as StartDate, min({$et}.EndDate) as EndDate, {$et}.FinishTime, {$et}.event_occurrence, ".$select;
276
+ }else{
277
+ $select = "{$et}.event_id, {$et}.event_id AS occurrence_id, {$et}.StartDate, {$et}.StartTime, {$et}.EndDate, {$et}.FinishTime, {$et}.event_occurrence, ".$select;
278
+ }
279
+ }
280
+ return $select;
281
+ }
282
+
283
+
284
+ /**
285
+ * GROUP BY Event (occurrence) ID
286
+ * Event posts do not want to be grouped by post, but by occurrence - unless otherwise specified.
287
+ * Hooked onto posts_groupby
288
+ *
289
+ *@since 1.0.0
290
+ *@access private
291
+ *@ignore
292
+ *@param string $groupby GROUP BY part of the SQL statement
293
+ *@param string $query WP_Query
294
+ *@return string
295
+ */
296
+ function eventorganiser_event_groupby( $groupby, $query ){
297
+ global $wpdb;
298
+
299
+ if(!empty($query->query_vars['group_events_by']) && $query->query_vars['group_events_by'] == 'series'){
300
+ return "{$wpdb->eo_events}.post_id";
301
+ }
302
+
303
+ if( eventorganiser_is_event_query( $query, true ) ):
304
+ if(empty($groupby))
305
+ return $groupby;
306
+
307
+ return "{$wpdb->eo_events}.event_id";
308
+ endif;
309
+
310
+ return $groupby;
311
+ }
312
+
313
+
314
+ /**
315
+ * LEFT JOIN all EVENTS.
316
+ * Joins events table when querying for events
317
+ * Hooked onto posts_join
318
+ *
319
+ *@since 1.0.0
320
+ *@access private
321
+ *@ignore
322
+ *@param string $join JOIN part of the SQL statement
323
+ *@param string $query WP_Query
324
+ *@return string
325
+ */
326
+ function eventorganiser_join_tables( $join, $query ){
327
+ global $wpdb;
328
+
329
+ if( eventorganiser_is_event_query( $query, true ) ){
330
+ $join .=" LEFT JOIN $wpdb->eo_events ON $wpdb->posts.id = {$wpdb->eo_events}.post_id ";
331
+ }
332
+ return $join;
333
+ }
334
+
335
+
336
+ function eventorganiser_is_event_query( $query, $exclusive = false ){
337
+
338
+ $post_types = $query->get( 'post_type' );
339
+ if( 'any' == $post_types )
340
+ $post_types = get_post_types( array('exclude_from_search' => false) );
341
+
342
+ if( $post_types == 'event' ){
343
+ $bool = true;
344
+
345
+ }elseif( ( $query && $query->is_feed('eo-events') ) || is_feed( 'eo-events' ) ){
346
+ $bool = true;
347
+
348
+ }elseif( empty( $post_types ) && eo_is_event_taxonomy( $query ) ){
349
+
350
+ //Querying by taxonomy - check if 'event' is the only post type
351
+ $post_types = array();
352
+ $taxonomies = wp_list_pluck( $query->tax_query->queries, 'taxonomy' );
353
+
354
+ foreach ( get_post_types() as $pt ) {
355
+
356
+ if( version_compare( '3.4', get_bloginfo( 'version' ) ) <= 0 ){
357
+ $object_taxonomies = $pt === 'attachment' ? get_taxonomies_for_attachments() : get_object_taxonomies( $pt );
358
+ }else{
359
+ //Backwards compat for 3.3
360
+ $object_taxonomies = $pt === 'attachment' ? array() : get_object_taxonomies( $pt );
361
+ }
362
+
363
+ if ( array_intersect( $taxonomies, $object_taxonomies ) )
364
+ $post_types[] = $pt;
365
+ }
366
+
367
+ if( in_array( 'event', $post_types ) ){
368
+ if( $exclusive && 1 == count( $post_types ) ){
369
+ $query->set( 'post_type', 'event' );
370
+ $bool = true;
371
+ }elseif( !$exclusive ){
372
+ $bool = true;
373
+ }else{
374
+ $bool = false;
375
+ }
376
+ }else{
377
+ $bool = false;
378
+ }
379
+
380
+ }elseif( $exclusive ){
381
+ $bool = false;
382
+
383
+ }elseif( ( is_array( $post_types ) && in_array( 'event', $post_types ) ) ){
384
+ $bool = true;
385
+
386
+ }else{
387
+ $bool = false;
388
+
389
+ }
390
+
391
+ return apply_filters( 'eventorganiser_is_event_query', $bool, $query, $exclusive );
392
+ }
393
+
394
+ /**
395
+ * Selects posts which satisfy custom WHERE statements
396
+ * Hooked onto posts_where
397
+ *
398
+ *@since 1.0.0
399
+ *@access private
400
+ *@ignore
401
+ *@param string $where WHERE part of the SQL statement
402
+ *@param string $query WP_Query
403
+ *@return string
404
+ */
405
+ function eventorganiser_events_where( $where, $query ){
406
+ global $wpdb;
407
+
408
+ //Only alter event queries
409
+ if( eventorganiser_is_event_query( $query, true ) ):
410
+
411
+ //If we only want events (or occurrences of events) that belong to a particular 'event'
412
+ if(isset($query->query_vars['event_series'])):
413
+ $series_id =$query->query_vars['event_series'];
414
+ $where .= $wpdb->prepare(" AND {$wpdb->eo_events}.post_id =%d ",$series_id);
415
+ endif;
416
+
417
+ if(isset($query->query_vars['event_occurrence_id'])):
418
+ $occurrence_id =$query->query_vars['event_occurrence_id'];
419
+ $where .= $wpdb->prepare(" AND {$wpdb->eo_events}.event_id=%d ",$occurrence_id);
420
+ endif;
421
+
422
+ //Check date ranges were are interested in.
423
+ $date_queries = array(
424
+ 'event_start_after'=>array(
425
+ 'notstrict' =>" AND {$wpdb->eo_events}.StartDate >= %s ",
426
+ 'strict' => " AND ({$wpdb->eo_events}.StartDate > %s OR ({$wpdb->eo_events}.StartDate = %s AND {$wpdb->eo_events}.StartTime > %s)) "
427
+ ),
428
+ 'event_start_before'=>array(
429
+ 'notstrict' =>" AND {$wpdb->eo_events}.StartDate <= %s ",
430
+ 'strict' => " AND ({$wpdb->eo_events}.StartDate < %s OR ({$wpdb->eo_events}.StartDate = %s AND {$wpdb->eo_events}.StartTime < %s)) "
431
+ ),
432
+ 'event_end_after'=>array(
433
+ 'notstrict' =>" AND {$wpdb->eo_events}.EndDate >= %s ",
434
+ 'strict' => " AND ({$wpdb->eo_events}.EndDate > %s OR ({$wpdb->eo_events}.EndDate = %s AND {$wpdb->eo_events}.FinishTime > %s)) "
435
+ ),
436
+ 'event_end_before'=>array(
437
+ 'notstrict' =>" AND {$wpdb->eo_events}.EndDate <= %s ",
438
+ 'strict' => " AND ({$wpdb->eo_events}.EndDate < %s OR ({$wpdb->eo_events}.EndDate = %s AND {$wpdb->eo_events}.FinishTime < %s)) "
439
+ )
440
+ );
441
+
442
+ //Construct sql query.
443
+ foreach ( $date_queries as $prop => $_sql ){
444
+ $datetime = $query->get($prop);
445
+ if( !empty( $datetime) ) {
446
+ $date = eo_format_date($datetime,'Y-m-d');
447
+ $time = eo_format_date($datetime,'H:i:s');
448
+ if( $time == '00:00:00' ){
449
+ $sql = $_sql['notstrict'];
450
+ $where .= $wpdb->prepare($sql, $date);
451
+ }else{
452
+ $sql = $_sql['strict'];
453
+ $where .= $wpdb->prepare($sql, $date, $date, $time);
454
+ }
455
+ }
456
+ }
457
+ endif;
458
+
459
+ return $where;
460
+ }
461
+
462
+ /**
463
+ * Alter the order of posts.
464
+ * This function allows to sort our events by a custom order (venue, date etc)
465
+ * Hooked onto posts_orderby
466
+ *
467
+ *@since 1.0.0
468
+ *@access private
469
+ *@ignore
470
+ *@param string $orderby ORDER BY part of the SQL statement
471
+ *@param string $query WP_Query
472
+ *@return string
473
+ */
474
+ function eventorganiser_sort_events( $orderby, $query ){
475
+ global $wpdb;
476
+
477
+ if( !empty($query->query_vars['orderby']) ){
478
+ //If the query sets an orderby return what to do if it is one of our custom orderbys
479
+
480
+ $order_crit= $query->query_vars['orderby'];
481
+ $order_dir = $query->query_vars['order'];
482
+
483
+ switch($order_crit):
484
+ case 'eventstart':
485
+ return " {$wpdb->eo_events}.StartDate $order_dir, {$wpdb->eo_events}.StartTime $order_dir";
486
+ break;
487
+
488
+ case 'eventend':
489
+ return " {$wpdb->eo_events}.EndDate $order_dir, {$wpdb->eo_events}.FinishTime $order_dir";
490
+ break;
491
+
492
+ default:
493
+ return $orderby;
494
+ endswitch;
495
+
496
+ }elseif( eventorganiser_is_event_query( $query, true ) ){
497
+ //If no orderby is set, but we are querying events, return the default order for events;
498
+ $orderby = " {$wpdb->eo_events}.StartDate ASC, {$wpdb->eo_events}.StartTime ASC";
499
+ }
500
+ return $orderby;
501
+ }
502
+
503
+
504
+
505
+
506
+ /**
507
+ * Checks if the main query is for a venue
508
+ * This will be deprecated shortly
509
+ *@since 1.0.0
510
+ *@ignore
511
+ *@return bool True if the main query is for a venue, false otherwise.
512
+ */
513
+ function eo_is_venue(){
514
+ global $wp_query;
515
+ return (isset($wp_query->query_vars['venue'] ) || is_tax('event-venue'));
516
+ }
517
+ ?>
includes/event-organiser-cpt.php ADDED
@@ -0,0 +1,1057 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Registers the event taxonomies: event-venue, event-category and optinally event-tag
5
+ * Hooked onto init
6
+ *
7
+ * @ignore
8
+ * @access private
9
+ * @since 1.0
10
+ */
11
+ function eventorganiser_create_event_taxonomies() {
12
+
13
+ if( !eventorganiser_get_option('prettyurl') ){
14
+ $cat_rewrite =$tag_rewrite=$venue_rewrite= false;
15
+
16
+ }else{
17
+ $cat_slug = trim(eventorganiser_get_option('url_cat','events/category'), "/");
18
+ $cat_rewrite = array( 'slug' => $cat_slug, 'with_front' => false );
19
+
20
+ $tag_slug = trim(eventorganiser_get_option('url_tag','events/tag'), "/");
21
+ $tag_rewrite = array( 'slug' => $tag_slug, 'with_front' => false );
22
+
23
+ $venue_slug = trim(eventorganiser_get_option('url_venue','events/venue'), "/");
24
+ $venue_rewrite = array( 'slug' => $venue_slug, 'with_front' => false );
25
+ }
26
+
27
+ $venue_labels = array(
28
+ 'name' => __('Event Venues','eventorganiser'),
29
+ 'singular_name' => _x( 'Venues', 'taxonomy singular name'),
30
+ 'search_items' => __( 'Search Venues'),
31
+ 'all_items' => __( 'All Venues'),
32
+ 'edit_item' => __( 'Edit Venue'),
33
+ 'update_item' => __( 'Update Venue'),
34
+ 'add_new_item' => __( 'Add New Venue'),
35
+ 'new_item_name' => __( 'New Venue Name'),
36
+ 'not_found' => __('No venues found'),
37
+ 'add_or_remove_items' => __( 'Add or remove venues' ),
38
+ 'separate_items_with_commas' => __( 'Separate venues with commas' )
39
+ );
40
+
41
+ register_taxonomy('event-venue',array('event'), array(
42
+ 'hierarchical' => false,
43
+ 'labels' => $venue_labels,
44
+ 'public'=> true,
45
+ 'show_in_nav_menus'=>false,
46
+ 'show_ui' => false,//Use custom UI
47
+ 'update_count_callback' => '_update_post_term_count',
48
+ 'query_var' => true,
49
+ 'capabilities'=>array(
50
+ 'manage_terms' => 'manage_venues',
51
+ 'edit_terms' => 'manage_venues',
52
+ 'delete_terms' => 'manage_venues',
53
+ 'assign_terms' =>'edit_events'),
54
+ 'rewrite' => $venue_rewrite
55
+ ));
56
+
57
+ // Add new taxonomy, make it hierarchical (like categories)
58
+ $category_labels = array(
59
+ 'name' => __('Event Categories', 'eventorganiser'),
60
+ 'singular_name' => _x( 'Category', 'taxonomy singular name'),
61
+ 'search_items' => __( 'Search Categories' ),
62
+ 'all_items' => __( 'All Categories' ),
63
+ 'parent_item' => __( 'Parent Category' ),
64
+ 'parent_item_colon' => __( 'Parent Category' ).':',
65
+ 'edit_item' => __( 'Edit Category' ),
66
+ 'update_item' => __( 'Update Category' ),
67
+ 'add_new_item' => __( 'Add New Category' ),
68
+ 'new_item_name' => __( 'New Category Name' ),
69
+ 'not_found' => __('No categories found'),
70
+ 'menu_name' => __( 'Categories' ),
71
+ );
72
+
73
+ register_taxonomy('event-category',array('event'), array(
74
+ 'hierarchical' => true,
75
+ 'labels' => $category_labels,
76
+ 'show_ui' => true,
77
+ 'update_count_callback' => '_update_post_term_count',
78
+ 'query_var' => true,
79
+ 'capabilities'=>array(
80
+ 'manage_terms' => 'manage_event_categories',
81
+ 'edit_terms' => 'manage_event_categories',
82
+ 'delete_terms' => 'manage_event_categories',
83
+ 'assign_terms' =>'edit_events'),
84
+ 'public'=> true,
85
+ 'rewrite' => $cat_rewrite
86
+ ));
87
+
88
+ if( eventorganiser_get_option('eventtag') ):
89
+ // Add new taxonomy, make it non-hierarchical (like tags)
90
+ $tag_labels = array(
91
+ 'name' => __('Event Tags','eventorganiser'),
92
+ 'singular_name' => _x( 'Tag', 'taxonomy singular name'),
93
+ 'search_items' => __( 'Search Tags'),
94
+ 'all_items' => __( 'All Tags'),
95
+ 'popular_items' => __( 'Popular Tags'),
96
+ 'parent_item' => null,
97
+ 'parent_item_colon' => null,
98
+ 'edit_item' => __( 'Edit Tag'),
99
+ 'update_item' => __( 'Update Tag'),
100
+ 'add_new_item' => __( 'Add New Tag'),
101
+ 'new_item_name' => __( 'New Tag Name'),
102
+ 'not_found' => __('No tags found'),
103
+ 'choose_from_most_used' => __( 'Choose from the most used tags' ),
104
+ 'menu_name' => __( 'Tags' ),
105
+ 'add_or_remove_items' => __( 'Add or remove tags' ),
106
+ 'separate_items_with_commas' => __( 'Separate tags with commas' )
107
+ );
108
+
109
+ register_taxonomy('event-tag',array('event'), array(
110
+ 'hierarchical' => false,
111
+ 'labels' => $tag_labels,
112
+ 'show_ui' => true,
113
+ 'update_count_callback' => '_update_post_term_count',
114
+ 'query_var' => true,
115
+ 'capabilities'=>array(
116
+ 'manage_terms' => 'manage_event_categories',
117
+ 'edit_terms' => 'manage_event_categories',
118
+ 'delete_terms' => 'manage_event_categories',
119
+ 'assign_terms' =>'edit_events'),
120
+ 'public'=> true,
121
+ 'rewrite' => $tag_rewrite
122
+ ));
123
+ endif;
124
+ }
125
+ add_action( 'init', 'eventorganiser_create_event_taxonomies', 10 );
126
+
127
+
128
+ /**
129
+ * Registers the event custom post type
130
+ * Hooked onto init
131
+ *
132
+ * @ignore
133
+ * @access private
134
+ * @since 1.0
135
+ */
136
+ function eventorganiser_cpt_register() {
137
+
138
+ $labels = array(
139
+ 'name' => __('Events','eventorganiser'),
140
+ 'singular_name' => __('Event','eventorganiser'),
141
+ 'add_new' => _x('Add New','post'),
142
+ 'add_new_item' => __('Add New Event','eventorganiser'),
143
+ 'edit_item' => __('Edit Event','eventorganiser'),
144
+ 'new_item' => __('New Event','eventorganiser'),
145
+ 'all_items' =>__('All events','eventorganiser'),
146
+ 'view_item' =>__('View Event','eventorganiser'),
147
+ 'search_items' =>__('Search events','eventorganiser'),
148
+ 'not_found' => __('No events found','eventorganiser'),
149
+ 'not_found_in_trash' => __('No events found in Trash','eventorganiser'),
150
+ 'parent_item_colon' => '',
151
+ 'menu_name' => __('Events','eventorganiser'),
152
+ );
153
+
154
+ $exclude_from_search = (eventorganiser_get_option('excludefromsearch')==0) ? false : true;
155
+
156
+ if( !eventorganiser_get_option('prettyurl') ){
157
+ $event_rewrite = false;
158
+ $events_slug = true;
159
+ }else{
160
+ $event_slug = trim(eventorganiser_get_option('url_event','events/event'), "/");
161
+ $events_slug = trim(eventorganiser_get_option('url_events','events/event'), "/");
162
+ $event_rewrite = array( 'slug' => $event_slug, 'with_front' => false,'feeds'=> true,'pages'=> true );
163
+
164
+ /* Workaround for http://core.trac.wordpress.org/ticket/19871 */
165
+ global $wp_rewrite;
166
+ $wp_rewrite->add_rewrite_tag('%event_ondate%','([0-9]{4}(?:/[0-9]{2}(?:/[0-9]{2})?)?)','post_type=event&ondate=');
167
+ add_permastruct('event_archive', $events_slug.'/on/%event_ondate%', array( 'with_front' => false ) );
168
+ }
169
+
170
+ $args = array(
171
+ 'labels' => $labels,
172
+ 'public' => true,
173
+ 'publicly_queryable' => true,
174
+ 'exclude_from_search'=>$exclude_from_search,
175
+ 'show_ui' => true,
176
+ 'show_in_menu' => true,
177
+ 'query_var' => true,
178
+ 'capability_type' => 'event',
179
+ 'rewrite' => $event_rewrite,
180
+ 'capabilities' => array(
181
+ 'publish_posts' => 'publish_events',
182
+ 'edit_posts' => 'edit_events',
183
+ 'edit_others_posts' => 'edit_others_events',
184
+ 'delete_posts' => 'delete_events',
185
+ 'delete_others_posts' => 'delete_others_events',
186
+ 'read_private_posts' => 'read_private_events',
187
+ 'edit_post' => 'edit_event',
188
+ 'delete_post' => 'delete_event',
189
+ 'read_post' => 'read_event',
190
+ ),
191
+ 'has_archive' => $events_slug,
192
+ 'hierarchical' => false,
193
+ 'menu_icon' => EVENT_ORGANISER_URL.'css/images/eoicon-16.png',
194
+ 'menu_position' => apply_filters('eventorganiser_menu_position',5),
195
+ 'supports' => eventorganiser_get_option('supports'),
196
+ );
197
+
198
+ register_post_type('event',$args);
199
+ }
200
+ add_action('init', 'eventorganiser_cpt_register');
201
+
202
+
203
+ /**
204
+ * Sets the messages that appear when an event is updated / saved.
205
+ * Hooked onto post_updated_messages
206
+ *
207
+ * @ignore
208
+ * @access private
209
+ * @since 1.0
210
+ */
211
+ function eventorganiser_messages( $messages ) {
212
+ global $post, $post_ID;
213
+
214
+ $messages['event'] = array(
215
+ 0 => '', // Unused. Messages start at index 1.
216
+ 1 => sprintf( __('Event updated. <a href="%s">View event</a>','eventorganiser'), esc_url( get_permalink($post_ID) ) ),
217
+ 2 => __('Custom field updated.'),
218
+ 3 => __('Custom field deleted.'),
219
+ 4 => __('Event updated.','eventorganiser'),
220
+ /* translators: %s: date and time of the revision */
221
+ 5 => isset($_GET['revision']) ? sprintf( __('Event restored to revision from %s','eventorganiser'), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
222
+ 6 => sprintf( __('Event published. <a href="%s">View event</a>','eventorganiser'), esc_url( get_permalink($post_ID) ) ),
223
+ 7 => __('Event saved.'),
224
+ 8 => sprintf( __('Event submitted. <a target="_blank" href="%s">Preview event</a>','eventorganiser'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
225
+ 9 => sprintf( __('Event scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview event</a>','eventorganiser'),
226
+ // translators: Publish box date format, see http://php.net/date
227
+ date_i18n( __( 'M j, Y @ G:i' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post_ID) ) ),
228
+ 10 => sprintf( __('Event draft updated. <a target="_blank" href="%s">Preview event</a>','eventorganiser'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
229
+ );
230
+ return $messages;
231
+ }
232
+ add_filter('post_updated_messages', 'eventorganiser_messages');
233
+
234
+
235
+ /**
236
+ * Maps meta capabilities to primitve ones for event post type
237
+ *
238
+ * @ignore
239
+ * @access private
240
+ * @since 1.0
241
+ */
242
+ function eventorganiser_event_meta_cap( $caps, $cap, $user_id, $args ) {
243
+
244
+ /* If editing, deleting, or reading a event, get the post and post type object. */
245
+ if ( 'edit_event' == $cap || 'delete_event' == $cap || 'read_event' == $cap ) {
246
+ $post = get_post( $args[0] );
247
+ $post_type = get_post_type_object( $post->post_type );
248
+
249
+ /* Set an empty array for the caps. */
250
+ $caps = array();
251
+ if($post->post_type!='event')
252
+ return $caps;
253
+ }
254
+
255
+ /* If editing a event, assign the required capability. */
256
+ if ( 'edit_event' == $cap ) {
257
+ if ( $user_id == $post->post_author )
258
+ $caps[] = $post_type->cap->edit_posts;
259
+ else
260
+ $caps[] = $post_type->cap->edit_others_posts;
261
+ }
262
+
263
+ /* If deleting a event, assign the required capability. */
264
+ elseif ( 'delete_event' == $cap ) {
265
+ if (isset($post->post_author ) && $user_id == $post->post_author)
266
+ $caps[] = $post_type->cap->delete_posts;
267
+ else
268
+ $caps[] = $post_type->cap->delete_others_posts;
269
+ }
270
+
271
+ /* If reading a private event, assign the required capability. */
272
+ elseif ( 'read_event' == $cap ) {
273
+
274
+ if ( 'private' != $post->post_status )
275
+ $caps[] = 'read';
276
+ elseif ( $user_id == $post->post_author )
277
+ $caps[] = 'read';
278
+ else
279
+ $caps[] = $post_type->cap->read_private_posts;
280
+ }
281
+
282
+ /* Return the capabilities required by the user. */
283
+ return $caps;
284
+ }
285
+ add_filter( 'map_meta_cap', 'eventorganiser_event_meta_cap', 10, 4 );
286
+
287
+
288
+ /**
289
+ * Adds the Event Organiser icon to the page head
290
+ * Hooked onto admin_head
291
+ *
292
+ * @ignore
293
+ * @access private
294
+ * @since 1.0
295
+ */
296
+ function eventorganiser_plugin_header_image() {
297
+ global $post_type;
298
+
299
+ if ((isset($_GET['post_type']) && $_GET['post_type'] == 'event') || ($post_type == 'event')) : ?>
300
+ <style>
301
+ #icon-edit { background:transparent url('<?php echo EVENT_ORGANISER_URL.'/css/images/eoicon-32.png';?>') no-repeat; }
302
+ </style>
303
+ <?php endif;
304
+ }
305
+ add_action('admin_head', 'eventorganiser_plugin_header_image');
306
+
307
+ /**
308
+ * With appropriate settings we add a menu item of 'post_type_archive' type.
309
+ * WP doesn't understand this so we set the url ourself - hooking just before its saved to db.
310
+ * Hooked onto wp_update_nav_menu_item
311
+ *
312
+ * @ignore
313
+ * @access private
314
+ * @since 1.0
315
+ */
316
+ function eventorganiser_update_nav_item($menu_id,$menu_item_db_id,$args){
317
+ if($args['menu-item-type'] == 'post_type_archive' && $args['menu-item-object'] =='event'){
318
+ $post_type = $args['menu-item-object'];
319
+ $args['menu-item-url'] = get_post_type_archive_link($post_type);
320
+ update_post_meta( $menu_item_db_id, '_menu_item_url', esc_url_raw($args['menu-item-url']) );
321
+ }
322
+ }
323
+ add_action('wp_update_nav_menu_item','eventorganiser_update_nav_item',10,3);
324
+
325
+
326
+ /**
327
+ * WP doesn't know when our custom menu item ('Events') is 'current'. We make it 'current', by
328
+ * adding the appropriate classes if viewing an event, event archive or event taxonomy
329
+ * Hooked onto wp_nav_menu_objects
330
+ *
331
+ * @ignore
332
+ * @access private
333
+ * @since 1.0
334
+ */
335
+ function eventorganiser_make_item_current($items,$args){
336
+ if(is_post_type_archive('event')|| is_singular('event')|| eo_is_event_taxonomy()){
337
+ foreach ($items as $item){
338
+ if('post_type_archive'!=$item->type || 'event'!=$item->object)
339
+ continue;
340
+
341
+ $item->classes[] = 'current-menu-item';
342
+
343
+ $_anc_id = (int) $item->db_id;
344
+ $active_ancestor_item_ids=array();
345
+ while(( $_anc_id = get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) ) &&
346
+ ! in_array( $_anc_id, $active_ancestor_item_ids ) ){
347
+ $active_ancestor_item_ids[] = $_anc_id;
348
+ }
349
+
350
+ //Loop through ancestors and give them 'ancestor' or 'parent' class
351
+ foreach ($items as $key=>$parent_item){
352
+ $classes = (array) $parent_item->classes;
353
+
354
+ //If menu item is the parent
355
+ if ($parent_item->db_id == $item->menu_item_parent ) {
356
+ $classes[] = 'current-menu-parent';
357
+ $items[$key]->current_item_parent = true;
358
+ }
359
+
360
+ //If menu item is an ancestor
361
+ if ( in_array( intval( $parent_item->db_id ), $active_ancestor_item_ids ) ) {
362
+ $classes[] = 'current-menu-ancestor';
363
+ $items[$key]->current_item_ancestor = true;
364
+ }
365
+
366
+ $items[$key]->classes = array_unique( $classes );
367
+ }
368
+ }
369
+ }
370
+ return $items;
371
+ }
372
+ add_filter( 'wp_nav_menu_objects', 'eventorganiser_make_item_current',10,2);
373
+
374
+
375
+ /**
376
+ * If a menu isn't being used the above won't work. They're using wp_list_pages, so the
377
+ * best we can do is append a link to the end of the list.
378
+ * Hooked onto wp_list_pages
379
+ *
380
+ * @ignore
381
+ * @access private
382
+ * @since 1.0
383
+ */
384
+ function eventorganiser_menu_link($items) {
385
+
386
+ if( eventorganiser_get_option('addtomenu') != '1' )
387
+ return $items;
388
+
389
+ $class ='menu-item menu-item-type-event';
390
+
391
+ if(is_post_type_archive('event')|| is_singular('event')|| eo_is_event_taxonomy())
392
+ $class = 'current_page_item';
393
+
394
+ $items .= sprintf('<li class="%s"><a href="%s" > %s </a></li>',
395
+ $class,
396
+ get_post_type_archive_link('event'),
397
+ esc_html(eventorganiser_get_option('navtitle'))
398
+ );
399
+ return $items;
400
+ }
401
+ add_filter( 'wp_list_pages', 'eventorganiser_menu_link',10,1 );
402
+
403
+ /**
404
+ * Contextual help for event pages
405
+ *
406
+ * @ignore
407
+ * @access private
408
+ * @since 1.0
409
+ */
410
+ function eventorganiser_cpt_help_text($contextual_help, $screen_id, $screen) {
411
+
412
+ //The add_help_tab function for screen was introduced in WordPress 3.3. Add it only to event pages.
413
+ if( ! method_exists($screen, 'add_help_tab') || ! in_array($screen_id, array('event','edit-event','event_page_venues','event_page_calendar')) )
414
+ return $contextual_help;
415
+
416
+ switch($screen_id):
417
+ //Add help for event editing / creating page
418
+ case ('event'):
419
+ $screen->add_help_tab( array(
420
+ 'id' => 'creating-events',
421
+ 'title' => __('Creating events','eventorganiser'),
422
+ 'content' => '<p>' . __('Creating events:','eventorganiser') . '</p>'.
423
+ '<ul>' .
424
+ '<li>' . __('The start date is the date the event starts. If the event is a reoccuring event, this is the start date of the first occurrence.','eventorganiser') . '</li>' .
425
+ '<li>' . __('The end date is the date the event finishes. If the event is a reoccuring event, this is the end date of the first occurrence.','eventorganiser') . '</li>' .
426
+ '<li>' . __('All dates and times must be entered in the specified format. This format can changed in the settings page.','eventorganiser') . '</li>' .
427
+ '</ul>'
428
+ ));
429
+ $screen->add_help_tab( array(
430
+ 'id' => 'repeating-events',
431
+ 'title' => __('Repeating events','eventorganiser'),
432
+ 'content' => '<p>' . __('To repeat an event according to some regular pattern, use the reocurrence dropdown menu to select how the event is to repeat. Further options then appear, ','eventorganiser') . '</p>' .
433
+ '<ul>' .
434
+ '<li>' . __('Specify how regularly the event should repeat (default 1)','eventorganiser') . '</li>' .
435
+ '<li>' . __('Choose the reoccurrence end date. No further occurrences are added after this date, but an occurrence that starts before may finish after this date.','eventorganiser') . '</li>' .
436
+ '<li>' . __('If monthly reoccurrence is selected, select whether this should repeat on that date of the month (e.g. on the 24th) or on the day of the month (e.g. on the third Tuesday) ','eventorganiser') . '</li>' .
437
+ '<li>' . __('If weekly reoccurrence is selected, select which days of the week the event should be repeated. If no days are selected, the day of the start date is used','eventorganiser') . '</li>' .
438
+ '</ul>'
439
+ ));
440
+ $screen->add_help_tab( array(
441
+ 'id' => 'selecting-venues',
442
+ 'title' => __('Selecting a venue','eventorganiser'),
443
+ 'content' => '<p>' . __('Selecting a venue','eventorganiser') . '</p>' .
444
+ '<ul>' .
445
+ '<li>' . __('Use the venues input field to search for existing venues','eventorganiser') . '</li>' .
446
+ '<li>' . __('Only pre-existing venues can be selected. To add a venue, go to the venues page.','eventorganiser') . '</li>' .
447
+ '</ul>'
448
+ ));
449
+ break;
450
+
451
+ //Add help for event admin table page
452
+ case ('edit-event'):
453
+
454
+ $screen->add_help_tab( array(
455
+ 'id'=>'overview',
456
+ 'title' => __('Overview'),
457
+ 'content'=>'<p>' . __('This is the list of all saved events. Note that <strong> reoccurring events appear as a single row </strong> in the table and the start and end date refers to the first occurrence of that event.','eventorganiser') . '</p>' ));
458
+ break;
459
+
460
+ //Add help for venue admin table page
461
+ case ('event_page_venues'):
462
+ $contextual_help =
463
+ '<p>' . __("Hovering over a row in the venues list will display action links that allow you to manage that venue. You can perform the following actions:",'eventorganiser') . '</p>' .
464
+ '<ul>' .
465
+ '<li>' . __('Edit takes you to the editing screen for that venue. You can also reach that screen by clicking on the venue title.','eventorganiser') . '</li>' .
466
+ '<li>' . __('Delete will permanently remove the venue','eventorganiser') . '</li>' .
467
+ '<li>' . __("View will take you to the venue's page",'eventorganiser') . '</li>' .
468
+ '</ul>';
469
+ break;
470
+
471
+ //Add help for calendar view
472
+ case ('event_page_calendar'):
473
+ $screen->add_help_tab( array(
474
+ 'id'=>'overview',
475
+ 'title'=>__('Overview'),
476
+ 'content'=>'<p>' . __("This page shows all (occurrances of) events. You can view the summary of an event by clicking on it. If you have the necessary permissions, a link to the event's edit page will appear also.",'eventorganiser'). '</p>' .
477
+ '<p>' . __("By clicking the relevant tab, you can view events in Month, Week or Day mode. You can also filter the events by events by category and venue. The 'go to date' button allows you to quickly jump to a specific date.",'eventorganiser'). '</p>'
478
+ ));
479
+ $screen->add_help_tab( array(
480
+ 'id'=>'add-event',
481
+ 'title'=>__('Add Event','eventorganiser'),
482
+ 'content'=>'<p>' . __("You can create an event on this Calendar, by clicking on day or dragging over multiple days (in Month view) or multiple times (in Week and Day view). You can give the event a title, specify a venue and provide a descripton. The event can be immediately published or saved as a draft. In any case, the event is created and you are forwarded to that event's edit page.",'eventorganiser') . '</p>' ));
483
+ break;
484
+ endswitch;
485
+
486
+ //Add a link to Event Organiser documentation on every EO page
487
+ $screen->set_help_sidebar(
488
+ '<p> <strong>'. __('For more information','eventorganiser').'</strong></br>'
489
+ .sprintf(__('See the <a %s> documentation</a>','eventorganiser'),'target="_blank" href="http://wp-event-organiser.com/documentation/"').'</p>'
490
+ .sprintf('<p><strong><a href="%s">%s</a></strong></p>', 'http://wordpress.org/support/plugin/event-organiser',__('Have a question?','eventorganiser'))
491
+ .sprintf('<p><strong><a href="%s">%s</a></strong></p>', admin_url('index.php?page=eo-pro'),__('Go Pro!','eventorganiser'))
492
+ );
493
+
494
+ return $contextual_help;
495
+ }
496
+ add_action( 'contextual_help', 'eventorganiser_cpt_help_text', 10, 3 );
497
+
498
+ /*
499
+ * The following adds the ability to associate a colour with an event-category.
500
+ * Currently stores data in the options table/
501
+ * If Taxonomy meta table becomes core, then these options will be migrated there.
502
+ */
503
+
504
+ /**
505
+ * Enqueue the javascript necessary for colour-picker on category pages.
506
+ * Hooked onto admin_menu. Why?
507
+ *
508
+ * @ignore
509
+ * @access private
510
+ * @since 1.3
511
+ */
512
+ function eventorganiser_colour_scripts() {
513
+ wp_enqueue_style( 'farbtastic' );
514
+ wp_enqueue_script( 'farbtastic' );
515
+ wp_enqueue_script( 'jQuery' );
516
+ }
517
+ add_action( 'admin_menu', 'eventorganiser_colour_scripts' );
518
+
519
+
520
+ /**
521
+ * When a category is created/updated save its color
522
+ * Hooked onto created_event-category and edited_event-category
523
+ *
524
+ * @ignore
525
+ * @access private
526
+ * @since 1.3
527
+ */
528
+ function eventorganiser_save_event_cat_meta( $term_id ) {
529
+ if ( isset( $_POST['eo_term_meta'] ) ):
530
+ $term_meta = get_option( "eo-event-category_$term_id");
531
+ $cat_keys = array_keys($_POST['eo_term_meta']);
532
+
533
+ foreach ($cat_keys as $key):
534
+ if (isset($_POST['eo_term_meta'][$key]))
535
+ $term_meta[$key] = $_POST['eo_term_meta'][$key];
536
+ endforeach;
537
+
538
+ //save the option array
539
+ update_option( "eo-event-category_$term_id", $term_meta );
540
+ endif;
541
+ }
542
+ add_action('created_event-category', 'eventorganiser_save_event_cat_meta', 10, 2);
543
+ add_action( 'edited_event-category', 'eventorganiser_save_event_cat_meta', 10, 2);
544
+
545
+ /**
546
+ * When a category is deleted, delete the saved colour (saved in options table).
547
+ * Hooked onto delete_event-category
548
+ *
549
+ * @ignore
550
+ * @access private
551
+ * @since 1.3
552
+ */
553
+ function eventorganiser_tax_term_deleted($term_id, $tt_id){
554
+ //Delete taxonomies meta
555
+ delete_option('eo-event-category_'.$term_id);
556
+ }
557
+ add_action('delete_event-category','eventorganiser_tax_term_deleted',10,2);
558
+
559
+ /**
560
+ * Add the colour picker forms to main taxonomy page: (This one needs stuff wrapped in Divs)
561
+ * uses eventorganiser_tax_meta_form to display the guts of the form.
562
+ * Hooked onto event-category_add_form_fields
563
+ * @uses eventorganiser_tax_meta_form
564
+ *
565
+ * @ignore
566
+ * @access private
567
+ * @since 1.3
568
+ */
569
+ function eventorganiser_add_tax_meta($taxonomy){
570
+ ?>
571
+ <div class="form-field"><?php eventorganiser_tax_meta_form('');?></div>
572
+ <p> &nbsp; </br>&nbsp; </p>
573
+ <?php
574
+ }
575
+ add_action('event-category_add_form_fields', 'eventorganiser_add_tax_meta',10,1);
576
+
577
+
578
+ /**
579
+ * Add the colour picker forms to taxonomy-edit page: (This one needs stuff wrapped in rows)
580
+ * uses eventorganiser_tax_meta_form to display the guts of the form.
581
+ * Hooked onto event-category_edit_form_fields
582
+ * @uses eventorganiser_tax_meta_form
583
+ *
584
+ * @ignore
585
+ * @access private
586
+ * @since 1.3
587
+ */
588
+ function eventorganiser_edit_tax_meta($term,$taxonomy){
589
+ //Check for existing data
590
+ $term_meta = get_option( "eo-event-category_$term->term_id");
591
+ $colour = (!empty($term_meta) && isset($term_meta['colour']) ? $term_meta['colour'] : '');
592
+ ?>
593
+ <tr class="form-field"><?php eventorganiser_tax_meta_form($colour);?></tr>
594
+ <?php
595
+ }
596
+ add_action( 'event-category_edit_form_fields', 'eventorganiser_edit_tax_meta', 10, 2);
597
+
598
+ /**
599
+ * Displays the guts of the taxonomy-meta form (i.e. colour picker);
600
+ *
601
+ * @ignore
602
+ * @access private
603
+ * @since 1.3
604
+ */
605
+ function eventorganiser_tax_meta_form($colour){
606
+ ?>
607
+ <th>
608
+ <label for="tag-description"><?php _e('Color','eventorganiser')?></label>
609
+ </th>
610
+ <td>
611
+ <input type="text" style="width:100px" name="eo_term_meta[colour]" class="color colour-input" id="color" value="<?php echo $colour; ?>" />
612
+ <a id="link-color-example" class="color hide-if-no-js" style="border: 1px solid #DFDFDF;border-radius: 4px 4px 4px 4px;margin: 0 7px 0 3px;padding: 4px 14px;"></a>
613
+ <div style="z-index: 100; background: none repeat scroll 0% 0% rgb(238, 238, 238); border: 1px solid rgb(204, 204, 204); position: absolute;display: none;" id="colorpicker"></div>
614
+ <p><?php _e('Assign the category a colour.','eventorganiser')?></p>
615
+ </td>
616
+ <script>
617
+ var farbtastic;(function($){var pickColor=function(a){farbtastic.setColor(a);$('.colour-input').val(a);$('a.color').css('background-color',a)};$(document).ready(function(){farbtastic=$.farbtastic('#colorpicker',pickColor);pickColor($('.colour-input').val());$('.color').click(function(e){e.preventDefault();if($('#colorpicker').is(":visible")){$('#colorpicker').hide()}else{$('#colorpicker').show()}});$('.colour-input').keyup(function(){var a=$('.colour-input').val(),b=a;a=a.replace(/[^a-fA-F0-9]/,'');if('#'+a!==b)$('.colour-input').val(a);if(a.length===3||a.length===6)pickColor('#'+a)});$(document).mousedown(function(){$('#colorpicker').hide()})})})(jQuery);
618
+ </script>
619
+ <?php
620
+ }
621
+
622
+ /**
623
+ * Add the colour of the category to the term object.
624
+ * Hooked onto get_event-category
625
+ *
626
+ * @ignore
627
+ * @access private
628
+ * @since 1.3
629
+ */
630
+ function eventorganiser_append_cat_meta($term){
631
+ if($term):
632
+ $term_meta = get_option( "eo-event-category_{$term->term_id}");
633
+ $colour = (isset($term_meta['colour']) ? $term_meta['colour'] : '');
634
+ $term->color = $colour;
635
+ endif;
636
+ return $term;
637
+ }
638
+ add_filter('get_event-category','eventorganiser_append_cat_meta');
639
+
640
+
641
+ /**
642
+ * Add the colour of the category to the term object.
643
+ * Hooked onto get_event-category
644
+ *
645
+ * @ignore
646
+ * @access private
647
+ * @since 1.3
648
+ */
649
+ function eventorganiser_get_terms_meta($terms){
650
+ if($terms):
651
+ foreach($terms as $term):
652
+ if(isset($term->taxonomy) && $term->taxonomy=='event-category'){
653
+ $term_meta = get_option( "eo-event-category_{$term->term_id}");
654
+ $colour = (isset($term_meta['colour']) ? $term_meta['colour'] : '');
655
+ $term->color = $colour;
656
+ }
657
+ endforeach;
658
+ endif;
659
+ return $terms;
660
+ }
661
+ add_filter('get_terms','eventorganiser_get_terms_meta');
662
+ add_filter('get_the_terms','eventorganiser_get_terms_meta');
663
+
664
+
665
+ /**
666
+ * Retrieve a category term's colour.
667
+ *
668
+ * @since 1.3
669
+ * @param term|slug $term The event category term object, or slug. Can be empty to get colour of term being viewed.
670
+ * @return string The event category colour in Hex format
671
+ */
672
+ function eo_get_category_meta($term='',$key=''){
673
+ if( $key != 'color' )
674
+ return false;
675
+
676
+ if (is_object($term)){
677
+ if(isset($term->color))
678
+ return $term->color;
679
+ else
680
+ $term = $term->slug;
681
+ }
682
+
683
+ if( !empty($term) ){
684
+ $term = get_term_by('slug', $term,'event-category');
685
+ if( isset($term->color))
686
+ return $term->color;
687
+
688
+ }elseif( is_tax('event-category') ){
689
+ $term = get_queried_object();
690
+ $term = $term->term_id;
691
+ $term = get_term( $term, 'event-category' );
692
+ if( isset($term->color))
693
+ return $term->color;
694
+ }
695
+
696
+ return false;
697
+ }
698
+
699
+ /**
700
+ * Adds custom tables to $wpdb;
701
+ *
702
+ * @ignore
703
+ * @access private
704
+ * @since 1.5
705
+ */
706
+ function eventorganiser_wpdb_fix(){
707
+ global $wpdb;
708
+ $wpdb->eo_venuemeta = "{$wpdb->prefix}eo_venuemeta";
709
+ $wpdb->eo_events = "{$wpdb->prefix}eo_events";
710
+ }
711
+ add_action( 'init', 'eventorganiser_wpdb_fix',1);
712
+ add_action( 'switch_blog', 'eventorganiser_wpdb_fix');
713
+
714
+
715
+ /**
716
+ * Updates venue meta cache when an event's venue is retrieved..
717
+ * Hooked onto wp_get_object_terms
718
+ *
719
+ * @ignore
720
+ * @access private
721
+ * @since 1.5
722
+ */
723
+ function _eventorganiser_get_event_venue($terms, $post_ids,$taxonomies,$args){
724
+ //Passes taxonomies as a string inside quotes...
725
+ $taxonomies = explode(',',trim($taxonomies,"\x22\x27"));
726
+ return eventorganiser_update_venue_meta_cache( $terms, $taxonomies);
727
+ }
728
+ add_filter('wp_get_object_terms','_eventorganiser_get_event_venue',10,4);
729
+
730
+
731
+ /**
732
+ * Updates venue meta cache when event venues are retrieved.
733
+ *
734
+ * For backwards compatibility it adds the venue details to the taxonomy terms.
735
+ * Hooked onto get_terms and get_event-venue
736
+ *
737
+ * @ignore
738
+ * @access private
739
+ * @since 1.5
740
+ *
741
+ * @param array $terms Array of terms,
742
+ * @param string $tax Should be (an array containing) 'event-venue'.
743
+ * @param array Array of event-venue terms,
744
+ */
745
+ function eventorganiser_update_venue_meta_cache( $terms, $tax){
746
+
747
+ if( is_array($tax) && !in_array('event-venue',$tax) ){
748
+ return $terms;
749
+ }
750
+ if( !is_array($tax) && $tax != 'event-venue'){
751
+ return $terms;
752
+ }
753
+
754
+ $single = false;
755
+ if( ! is_array($terms) ){
756
+ $single = true;
757
+ $terms = array( $terms );
758
+ }
759
+
760
+ if( empty($terms) )
761
+ return $terms;
762
+
763
+ //TODO Sort this out when $terms is an array of IDs not objects.
764
+ $term_ids = wp_list_pluck($terms,'term_id');
765
+
766
+ update_meta_cache('eo_venue',$term_ids);
767
+
768
+ //Backwards compatible. Depreciated - use the functions, not properties.
769
+ foreach ($terms as $term){
770
+ if( !is_object($term) )
771
+ continue;
772
+ $term_id = (int) $term->term_id;
773
+
774
+ if( !isset($term->venue_address) ){
775
+ $address = eo_get_venue_address($term_id);
776
+ foreach( $address as $key => $value )
777
+ $term->{'venue_'.$key} = $value;
778
+ }
779
+
780
+ if( !isset($term->venue_lat) || !isset($term->venue_lng) ){
781
+ $term->venue_lat = number_format(floatval(eo_get_venue_lat($term_id)), 6);
782
+ $term->venue_lng = number_format(floatval(eo_get_venue_lng($term_id)), 6);
783
+ }
784
+
785
+ }
786
+
787
+ if( $single ) return $terms[0];
788
+
789
+ return $terms;
790
+ }
791
+ add_filter('get_terms','eventorganiser_update_venue_meta_cache',10,2);
792
+ add_filter('get_event-venue','eventorganiser_update_venue_meta_cache',10,2);
793
+
794
+
795
+
796
+ /**
797
+ * Allows event-venue terms to be sorted by address, city, state, country, or postcode (on venue admin table)
798
+ * Hooked onto terms_clauses
799
+ *
800
+ * @ignore
801
+ * @access private
802
+ * @since 1.5
803
+ */
804
+ function eventorganiser_join_venue_meta($pieces,$taxonomies,$args){
805
+ global $wpdb;
806
+
807
+ if( ! in_array('event-venue',$taxonomies) )
808
+ return $pieces;
809
+
810
+ /* Order by */
811
+ $address_keys = array_keys(_eventorganiser_get_venue_address_fields());
812
+ if( in_array('_'.$args['orderby'], $address_keys) )
813
+ $meta_key ='_'.$args['orderby'];
814
+ else
815
+ $meta_key = false;
816
+
817
+ if(false === $meta_key)
818
+ return $pieces;
819
+
820
+ $sql = get_meta_sql(array(array('key'=>$meta_key)), 'eo_venue', 't', 'term_id');
821
+
822
+ $pieces['join'] .= $sql['join'];
823
+ $pieces['where'] .= $sql['where'];
824
+ $pieces['orderby'] = "ORDER BY {$wpdb->eo_venuemeta}.meta_value";
825
+ return $pieces;
826
+ }
827
+ add_filter('terms_clauses', 'eventorganiser_join_venue_meta',10,3);
828
+
829
+ /**
830
+ * Filters to the edit venue term link so that points to the correct place
831
+ * Hooked onto get_edit_term_link
832
+ *
833
+ * @ignore
834
+ * @access private
835
+ * @since 1.5
836
+ */
837
+ function eventorganiser_edit_venue_link($link, $term_id, $taxonomy){
838
+
839
+ if( $taxonomy != 'event-venue' )
840
+ return $link;
841
+
842
+ $tax = get_taxonomy( $taxonomy );
843
+ if ( !current_user_can( $tax->cap->edit_terms ) )
844
+ return;
845
+
846
+ $venue = get_term($term_id, $taxonomy);
847
+
848
+ $link = add_query_arg(array(
849
+ 'page'=>'venues',
850
+ 'action'=>'edit',
851
+ 'event-venue'=> $venue->slug,
852
+ ), admin_url('edit.php?post_type=event'));
853
+
854
+ return $link;
855
+ }
856
+ add_filter('get_edit_term_link','eventorganiser_edit_venue_link',10,3);
857
+
858
+
859
+ /*
860
+ * A walker class to use that extends wp_dropdown_categories and allows it to use the term's slug as a value rather than ID.
861
+ *
862
+ * See http://core.trac.wordpress.org/ticket/13258
863
+ *
864
+ * Usage, as normal:
865
+ * wp_dropdown_categories($args);
866
+ *
867
+ * But specify the custom walker class, and (optionally) a 'id' or 'slug' for the 'value' parameter:
868
+ * $args=array('walker'=> new EO_Walker_TaxonomyDropdown(), 'value'=>'slug', .... );
869
+ * wp_dropdown_categories($args);
870
+ *
871
+ * If the 'value' parameter is not set it will use term ID for categories, and the term's slug for other taxonomies in the value attribute of the term's <option>.
872
+ */
873
+
874
+ class EO_Walker_TaxonomyDropdown extends Walker_CategoryDropdown{
875
+
876
+ function start_el(&$output, $category, $depth, $args) {
877
+ $pad = str_repeat('&nbsp;', $depth * 3);
878
+ $cat_name = apply_filters('list_cats', $category->name, $category);
879
+
880
+ if( !isset($args['value']) ){
881
+ $args['value'] = ( $category->taxonomy != 'category' ? 'slug' : 'id' );
882
+ }
883
+
884
+ $value = ($args['value']=='slug' ? $category->slug : $category->term_id );
885
+
886
+ $output .= "\t<option class=\"level-$depth\" value=\"".$value."\"";
887
+ if ( $value === (string) $args['selected'] ){
888
+ $output .= ' selected="selected"';
889
+ }
890
+ $output .= '>';
891
+ $output .= $pad.$cat_name;
892
+ if ( $args['show_count'] )
893
+ $output .= '&nbsp;&nbsp;('. $category->count .')';
894
+
895
+ $output .= "</option>\n";
896
+ }
897
+
898
+ }
899
+
900
+ /**
901
+ * For this to work you need to add the following to the custom field exceptions on the ThreeWP settings page:
902
+ *
903
+ * _eventorganiser_event_schedule
904
+ * _eventorganiser_schedule_start_start
905
+ * _eventorganiser_schedule_start_finish
906
+ *_eventorganiser_schedule_last_start
907
+ *_eventorganiser_schedule_last_finish
908
+ *
909
+ * @access private
910
+ * @ignore
911
+ *
912
+ */
913
+ function eventorganiser_threeWP( $activity ){
914
+
915
+
916
+ if( isset( $activity['activity_id'] ) && $activity['activity_id'] == '3broadcast_broadcasted' && 'event' == get_post_type( get_the_ID() ) ){
917
+
918
+ $details = $activity['activity_details'];
919
+ $current_blog_id = get_current_blog_id();
920
+ $original_id = get_the_ID();
921
+ $original_cats = get_the_terms( $original_id, 'event-category' );
922
+ $original_venue_id = eo_get_venue( $original_id );
923
+
924
+ //Venue Meta &Thumbnail
925
+ $venue_meta = eo_get_venue_meta( $original_venue_id, '', false );
926
+ if( $original_venue_id && $venue_thumbnail_id = eo_get_venue_meta( $original_venue_id, '_eventorganiser_thumbnail_id', true ) ){
927
+
928
+ $original_upload_dir = wp_upload_dir();
929
+ $metadata = wp_get_attachment_metadata( $venue_thumbnail_id );
930
+ $data = get_post( $venue_thumbnail_id );
931
+ $file = $metadata['file'];
932
+ $guid = $data->guid;
933
+ $post_title = $data->post_title;
934
+ $menu_order = $data->menu_order;
935
+ $post_excerpt = $data->post_excerpt;
936
+ $filename_base = basename( $metadata['file'] );
937
+ $filename_path = $original_upload_dir[ 'basedir' ] . '/' . $metadata[ 'file' ];
938
+
939
+ $venue_thumbnail = compact( 'filename_path' , 'file', 'filename_base', 'post_title', 'guid', 'menu_order', 'post_excerpt' );
940
+ unset( $venue_meta['_eventorganiser_thumbnail_id'] );
941
+ }
942
+
943
+ foreach( $details as $broadcast ){
944
+ $blog_id = $broadcast['blog_id'];
945
+ $post_id = $broadcast['post_id'];
946
+ switch_to_blog( $blog_id );
947
+
948
+ $event_data = array ( 'force_regenerate_dates' => true );
949
+ eo_update_event($post_id, $event_data );
950
+
951
+ $venue_id = eo_get_venue( $post_id );
952
+ //Delete old venue meta
953
+ if( $old_meta = eo_get_venue_meta( $venue_id, '', false ) ){
954
+ $old_meta_keys = array_keys( $old_meta );
955
+ foreach( $old_meta_keys as $key ){
956
+ eo_delete_venue_meta( $venue_id, $key );
957
+ }
958
+ }
959
+ //Add new venue meta
960
+ foreach( $venue_meta as $key => $values ){
961
+ foreach( $values as $value ){
962
+ eo_add_venue_meta( $venue_id, $key, $value );
963
+ }
964
+ }
965
+
966
+ //Sync cat colours
967
+ $cats = get_the_terms( $post_id, 'event-category' );
968
+ foreach( $cats as $cat ){
969
+ //Find original cat
970
+ foreach( $original_cats as $original_cat ){
971
+ if( $original_cat->slug == $cat->slug ){
972
+ $re = update_option( "eo-event-category_{$cat->term_id}", array( 'colour' => $original_cat->color ) );
973
+ break;
974
+ }
975
+ }
976
+ }
977
+
978
+ //Sync venue thumbnails
979
+ $upload_dir = wp_upload_dir();
980
+ if ( file_exists( $venue_thumbnail['filename_path'] ) ){
981
+ $file_path = $upload_dir['path'] . '/' . $venue_thumbnail['filename_base'];
982
+
983
+ // Copy the file to the blog's upload directory
984
+ copy( $venue_thumbnail['filename_path'], $file_path );
985
+
986
+ if( false == ( $attachment_id = eventorganiser_file_to_attachment( $venue_thumbnail['file'] ) ) ){
987
+
988
+ // And now create the attachment stuff.
989
+ // This is taken almost directly from http://codex.wordpress.org/Function_Reference/wp_insert_attachment
990
+ $wp_filetype = wp_check_filetype( $venue_thumbnail['filename_base'], null );
991
+ $attachment = array(
992
+ 'guid' => $upload_dir['url'] . '/' . $venue_thumbnail['filename_base'],
993
+ 'menu_order' => $venue_thumbnail['menu_order'],
994
+ 'post_excerpt' =>$venue_thumbnail['post_excerpt'],
995
+ 'post_mime_type' => $wp_filetype['type'],
996
+ 'post_title' => $venue_thumbnail['post_title'],
997
+ 'post_status' => 'inherit',
998
+ );
999
+ $attachment_id = wp_insert_attachment( $attachment, $file_path, 0 );
1000
+
1001
+ // Now to handle the metadata.
1002
+ require_once(ABSPATH . "wp-admin" . '/includes/image.php' );
1003
+ $attach_data = wp_generate_attachment_metadata( $attachment_id, $file_path );
1004
+ wp_update_attachment_metadata( $attachment_id, $attach_data );
1005
+ }//If attachment post doesn't exist
1006
+
1007
+ eo_update_venue_meta( $venue_id, '_eventorganiser_thumbnail_id', $attachment_id );
1008
+ }//If original file exists
1009
+
1010
+ }//Foreach blog
1011
+ switch_to_blog( $current_blog_id );
1012
+ }//If broadcasting
1013
+
1014
+ }
1015
+
1016
+
1017
+ function eventorganiser_file_to_attachment( $file ){
1018
+
1019
+ $attachments = get_posts( array(
1020
+ 'numberposts' => 1,
1021
+ 'post_type' => 'attachment',
1022
+ 'fields' => 'ids',
1023
+ 'meta_query' => array(
1024
+ array(
1025
+ 'value' => $file,
1026
+ 'key' => '_wp_attached_file',
1027
+ ),
1028
+ )
1029
+ ));
1030
+ if( $attachments )
1031
+ return intval( $attachments[0] );
1032
+ else
1033
+ return false;
1034
+ }
1035
+
1036
+ add_action( 'threewp_activity_monitor_new_activity', 'eventorganiser_threeWP' );
1037
+
1038
+
1039
+ function eventorganiser_event_shortlink( $shortlink, $id, $context ) {
1040
+
1041
+ //Context can be post/blog/meta ID
1042
+ $event_id = 0;
1043
+ if( 'query' == $context && is_singular( 'event' ) ){
1044
+ $event_id = get_queried_object_id();
1045
+ }elseif( 'post' == $context ){
1046
+ $event_id = $id;
1047
+ }
1048
+
1049
+ //Only do something if of event post type
1050
+ if( 'event' == get_post_type( $event_id ) ){
1051
+ $shortlink = home_url( '?p=' . $event_id );
1052
+ }
1053
+
1054
+ return $shortlink;
1055
+ }
1056
+ add_filter( 'pre_get_shortlink', 'eventorganiser_event_shortlink', 10, 3 );
1057
+ ?>
includes/event-organiser-event-functions.php ADDED
@@ -0,0 +1,1231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Event related functions
4
+ *
5
+ * @package event-functions
6
+ */
7
+
8
+ /**
9
+ * Retrieve list of events matching criteria.
10
+ *
11
+ * This function is a wrapper for get_posts(). **As such parameters from {@see `get_posts()`} and {@link http://codex.wordpress.org/Class_Reference/WP_Query `WP_Query`} can also be used**.
12
+ * Their default values are as indicated by the relevant codex page unless specified below.
13
+ * You can also use {@see `get_posts()`} and {@link http://codex.wordpress.org/Class_Reference/WP_Query `WP_Query`} instead to retrieve events.
14
+ *
15
+ * The `$args` array can include the following.
16
+ *
17
+ * * **event_start_before** - default: `null`
18
+ * * **event_end_before** - default: `null`
19
+ * * **event_start_after** - default: `null`
20
+ * * **event_end_after** - default: `null`. This argument, and those expect dates in **Y-m-d** format or {@link http://wp-event-organiser.com/documentation/relative-date-formats/ relative dates}.
21
+ * * **numberposts** - default is `-1` (all events)
22
+ * * **orderby** - default is `eventstart`. You can also have `eventend`.
23
+ * * **showpastevents** - default is `true` (it's recommended to use `event_start_after=today` or `event_end_after=today` instead)
24
+ *
25
+ * If you use `get_posts()` or `WP_Query` instead then you should ensure the following:
26
+ *
27
+ * * **post_type** - is set to 'event'
28
+ * * **suppress_filters** - is set to false
29
+ *
30
+ *
31
+ * ###Example
32
+ *
33
+ * <?php
34
+ * $events = eo_get_events(array(
35
+ * 'numberposts'=>5,
36
+ * 'event_start_after'=>'today',
37
+ * 'showpastevents'=>true,//Will be deprecated, but set it to true to play it safe.
38
+ * ));
39
+ *
40
+ * if($events):
41
+ * echo '<ul>';
42
+ * foreach ($events as $event):
43
+ * //Check if all day, set format accordingly
44
+ * $format = ( eo_is_all_day($event->ID) ? get_option('date_format') : get_option('date_format').' '.get_option('time_format') );
45
+ * printf(
46
+ * '<li><a href="%s"> %s </a> on %s </li>',
47
+ * get_permalink($event->ID),
48
+ * get_the_title($event->ID),
49
+ * eo_get_the_start($format, $event->ID,null,$event->occurrence_id)
50
+ * );
51
+ * endforeach;
52
+ * echo '</ul>';
53
+ * endif;
54
+ * ?>
55
+ *
56
+ * @since 1.0.0
57
+ * @uses get_posts()
58
+ * @package event-query-functions
59
+ * @link https://gist.github.com/4165380 List up-coming events
60
+ *@link https://gist.github.com/4190351 Adds up-coming events in the venue tooltip
61
+ *@link http://wp-event-organiser.com/documentation/relative-date-formats/ Using relative dates in event queries
62
+ *@link http://wp-event-organiser.com/forums/topic/retrieving-events-using-wp_query/ Retrieving events with `WP_Query`
63
+ * @param array $args Event query arguments.
64
+ * @return array An array of event (post) objects. Like get_posts. In case of failure it returns null.
65
+ */
66
+ function eo_get_events($args=array()){
67
+
68
+ //In case an empty string is passed
69
+ if(empty($args))
70
+ $args = array();
71
+
72
+ //These are preset to ensure the plugin functions properly
73
+ $required = array('post_type'=> 'event','suppress_filters'=>false);
74
+
75
+ //These are the defaults
76
+ $defaults = array(
77
+ 'numberposts'=>-1,
78
+ 'orderby'=> 'eventstart',
79
+ 'order'=> 'ASC',
80
+ 'showrepeats'=>1,
81
+ 'group_events_by'=>'',
82
+ 'showpastevents'=>true);
83
+
84
+ //Construct the query array
85
+ $query_array = array_merge($defaults,$args,$required);
86
+
87
+ if(!empty($query_array['venue'])){
88
+ $query_array['event-venue'] = $query_array['venue'];
89
+ unset($query_array['venue']);
90
+ }
91
+
92
+ //Ensure all date queries are yyyy-mm-dd format. Process relative strings ('today','tomorrow','+1 week')
93
+ $dates = array('ondate','event_start_after','event_start_before','event_end_before','event_end_after');
94
+ foreach($dates as $prop):
95
+ if(!empty($query_array[$prop]))
96
+ $query_array[$prop] = eo_format_date($query_array[$prop],'Y-m-d');
97
+ endforeach;
98
+
99
+ //Make sure 'false' is passed as integer 0
100
+ if(strtolower($query_array['showpastevents'])==='false') $query_array['showpastevents']=0;
101
+
102
+ if($query_array){
103
+ $events=get_posts($query_array);
104
+ return $events;
105
+ }
106
+
107
+ return null;
108
+ }
109
+
110
+
111
+ /**
112
+ * Retrieve a row object from events table of the event by ID of the event
113
+ * @access private
114
+ *@ignore
115
+ * @since 1.0
116
+ *
117
+ * @param int $post_id Post ID of the event.
118
+ * @param int $deprecated The occurrence number. Deprecated use $occurrence_id
119
+ * @param int $occurrence_id The occurrence ID
120
+ * @return row object of event's row in Events table
121
+ */
122
+ function eo_get_by_postid($post_id,$deprecated=0, $occurrence_id=0){
123
+ global $wpdb;
124
+
125
+ if( !empty($occurrence_id) ){
126
+ $column = 'event_id';
127
+ $value = $occurrence_id;
128
+ }else{
129
+ //Backwards compatibility!
130
+ $column = 'event_occurrence';
131
+ $value = $deprecated;
132
+ }
133
+
134
+ $querystr = $wpdb->prepare("
135
+ SELECT StartDate,EndDate,StartTime,FinishTime FROM {$wpdb->eo_events}
136
+ WHERE {$wpdb->eo_events}.post_id=%d
137
+ AND ({$wpdb->eo_events}.{$column}=%d)
138
+ LIMIT 1",$post_id,$value);
139
+
140
+ return $wpdb->get_row($querystr);
141
+ }
142
+
143
+
144
+ /**
145
+ * Returns the start date of occurrence of event.
146
+ * If used inside the loop, with no id no set, returns start date of
147
+ * current event occurrence.
148
+ * @since 1.0.0
149
+ * @package event-date-functions
150
+ *
151
+ * @param string $format String of format as accepted by PHP date
152
+ * @param int $post_id Post ID of the event
153
+ * @param int $deprecated The occurrence number. Deprecated. Use $occurrence_id
154
+ * @param int $occurrence_id The occurrence ID
155
+ * @return string the start date formated to given format, as accepted by PHP date
156
+ */
157
+ function eo_get_the_start($format='d-m-Y',$post_id=0,$deprecated=0, $occurrence_id=0){
158
+ global $post;
159
+ $event = $post;
160
+
161
+ if( !empty($deprecated) ){
162
+ _deprecated_argument( __FUNCTION__, '1.5.6', 'Third argument is depreciated. Please use a fourth argument - occurrence ID. Available from $post->occurrence_id' );
163
+
164
+ //Backwards compatiblity
165
+ if( !empty($post_id) ) $event = eo_get_by_postid($post_id,$deprecated, $occurrence_id);
166
+
167
+ if(empty($event))
168
+ return false;
169
+
170
+ $date = trim($event->StartDate).' '.trim($event->StartTime);
171
+
172
+ if(empty($date)||$date==" ")
173
+ return false;
174
+
175
+ return eo_format_date($date,$format);
176
+ }
177
+
178
+ $occurrence_id = (int) ( empty($occurrence_id) && isset($event->occurrence_id) ? $event->occurrence_id : $occurrence_id);
179
+
180
+ $occurrences = eo_get_the_occurrences_of($post_id);
181
+
182
+ if( !$occurrences || !isset($occurrences[$occurrence_id]) )
183
+ return false;
184
+
185
+ $start = $occurrences[$occurrence_id]['start'];
186
+
187
+ return apply_filters('eventorganiser_get_the_start', eo_format_datetime( $start, $format ), $start, $format, $post_id, $occurrence_id );
188
+ }
189
+
190
+ /**
191
+ * Returns the start date of occurrence of event an event, like {@see `eo_get_the_start()`}.
192
+ * The difference is that the occurrence ID *must* be supplied (event ID is not).
193
+ * @since 1.6
194
+ * @ignore
195
+ *
196
+ * @param string $format String of format as accepted by PHP date
197
+ * @param int $occurrence_id The occurrence ID
198
+ * @return string the start date formated to given format, as accepted by PHP date
199
+ */
200
+ function eo_get_the_occurrence_start($format='d-m-Y',$occurrence_id){
201
+ global $wpdb;
202
+
203
+ $querystr = $wpdb->prepare("
204
+ SELECT StartDate,StartTime FROM {$wpdb->eo_events}
205
+ WHERE {$wpdb->eo_events}.event_id=%d
206
+ LIMIT 1",$occurrence_id);
207
+
208
+ $occurrence = $wpdb->get_row($querystr);
209
+
210
+ if( !$occurrence )
211
+ return false;
212
+
213
+ $date = trim($occurrence->StartDate).' '.trim($occurrence->StartTime);
214
+
215
+ if(empty($date)||$date==" ")
216
+ return false;
217
+
218
+ return eo_format_date($date,$format);
219
+ }
220
+
221
+ /**
222
+ * Echos the start date of occurence of event
223
+ * @since 1.0.0
224
+ * @uses eo_get_the_start()
225
+ * @package event-date-functions
226
+ *
227
+ * @param string $format String of format as accepted by PHP date
228
+ * @param int $post_id Post ID of the event
229
+ * @param int $deprecated The occurrence number. Deprecated. Use $occurrence_id instead
230
+ * @param int $occurrence_id The occurrence ID
231
+ */
232
+ function eo_the_start($format='d-m-Y',$post_id=0,$deprecated=0,$occurrence_id=0){
233
+ echo eo_get_the_start($format,$post_id,$deprecated, $occurrence_id);
234
+ }
235
+
236
+
237
+ /**
238
+ * Returns the end date of occurrence of event.
239
+ * If used inside the loop, with no id no set, returns end date of
240
+ * current event occurrence.
241
+ * @since 1.0.0
242
+ * @package event-date-functions
243
+ *
244
+ * @param string $format String of format as accepted by PHP date
245
+ * @param int $post_id The event (post) ID. Uses current event if empty.
246
+ * @param int $deprecated The occurrence number. Deprecated. Use $occurrence_id instead
247
+ * @param int $occurrence_id The occurrence ID
248
+ * @return string the end date formated to given format, as accepted by PHP date
249
+ */
250
+ function eo_get_the_end($format='d-m-Y',$post_id=0,$deprecated=0, $occurrence_id=0){
251
+ global $post;
252
+ $event = $post;
253
+
254
+ if( !empty($deprecated) ){
255
+ _deprecated_argument( __FUNCTION__, '1.5.6', 'Third argument is depreciated. Please use a fourth argument - occurrence ID. Available from $post->occurrence_id' );
256
+
257
+ //Backwards compatiblity
258
+ if( !empty($post_id) ) $event = eo_get_by_postid($post_id,$deprecated, $occurrence_id);
259
+
260
+ if(empty($event))
261
+ return false;
262
+
263
+ $date = trim($event->EndDate).' '.trim($event->FinishTime);
264
+
265
+ if(empty($date)||$date==" ")
266
+ return false;
267
+
268
+ return eo_format_date($date,$format);
269
+ }
270
+ $occurrence_id = (int) ( empty($occurrence_id) && isset($event->occurrence_id) ? $event->occurrence_id : $occurrence_id);
271
+
272
+ $occurrences = eo_get_the_occurrences_of($post_id);
273
+
274
+ if( !$occurrences || !isset($occurrences[$occurrence_id]) )
275
+ return false;
276
+
277
+ $end = $occurrences[$occurrence_id]['end'];
278
+
279
+ return apply_filters('eventorganiser_get_the_end', eo_format_datetime( $end, $format ), $end, $format, $post_id, $occurrence_id );
280
+ }
281
+
282
+ /**
283
+ * Echos the end date of occurence of event
284
+ * @since 1.0.0
285
+ * @uses eo_get_the_end()
286
+ * @package event-date-functions
287
+ *
288
+ * @param string $format String of format as accepted by PHP date
289
+ * @param int $post_id Post ID of the event
290
+ * @param int $occurrence The occurrence number. Deprecated. Use $occurrence_id instead
291
+ * @param int $occurrence_id The occurrence ID
292
+ */
293
+ function eo_the_end($format='d-m-Y',$post_id=0,$occurrence=0, $occurrence_id=0){
294
+ echo eo_get_the_end($format,$post_id,$occurrence, $occurrence_id);
295
+ }
296
+
297
+
298
+ /**
299
+ * Gets the formated date of next occurrence of an event
300
+ * @since 1.0.0
301
+ * @package event-date-functions
302
+ *
303
+ * @param string $format The format to use, using PHP Date format
304
+ * @param int $post_id The event (post) ID,
305
+ * @return string The formatted date or false if no date exists
306
+ */
307
+ function eo_get_next_occurrence($format='d-m-Y',$post_id=0){
308
+ $next_occurrence = eo_get_next_occurrence_of($post_id);
309
+
310
+ if( !$next_occurrence )
311
+ return false;
312
+
313
+ $next = $next_occurrence['start'];
314
+ return apply_filters('eventorganiser_get_next_occurrence', eo_format_datetime( $next, $format ), $next, $format, $post_id );
315
+ }
316
+
317
+ /**
318
+ * Returns an array of datetimes (start and end) corresponding to the next occurrence of an event
319
+ * {@see `eo_get_next_occurrence()`} on the other hand returns a formated datetime of the start date.
320
+ * To get the current occurrence{@see `eo_get_current_occurrence_of()`}
321
+ *
322
+ * @package event-date-functions
323
+ * @since 1.6
324
+ *
325
+ * @param int $post_id The event (post) ID. Uses current event if empty.
326
+ * @return array Array with keys 'start' and 'end', with corresponding datetime objects
327
+ */
328
+ function eo_get_next_occurrence_of($post_id=0){
329
+ global $wpdb;
330
+ $post_id = (int) ( empty($post_id) ? get_the_ID() : $post_id);
331
+
332
+ //Retrieve the blog's local time and create the date part
333
+ $tz = eo_get_blog_timezone();
334
+ $blog_now = new DateTime(null, $tz );
335
+ $now_date =$blog_now->format('Y-m-d');
336
+ $now_time =$blog_now->format('H:i:s');
337
+
338
+ $nextoccurrence = $wpdb->get_row($wpdb->prepare("
339
+ SELECT StartDate, StartTime, EndDate, FinishTime
340
+ FROM {$wpdb->eo_events}
341
+ WHERE {$wpdb->eo_events}.post_id=%d
342
+ AND (
343
+ ({$wpdb->eo_events}.StartDate > %s) OR
344
+ ({$wpdb->eo_events}.StartDate = %s AND {$wpdb->eo_events}.StartTime >= %s))
345
+ ORDER BY {$wpdb->eo_events}.StartDate ASC
346
+ LIMIT 1",$post_id,$now_date,$now_date,$now_time));
347
+
348
+ if( ! $nextoccurrence )
349
+ return false;
350
+
351
+ $start = new DateTime($nextoccurrence->StartDate.' '.$nextoccurrence->StartTime, $tz);
352
+ $end = new DateTime($nextoccurrence->EndDate.' '.$nextoccurrence->FinishTime, $tz);
353
+
354
+ return compact('start','end');
355
+ }
356
+
357
+
358
+ /**
359
+ * Prints the formated date of next occurrence of an event
360
+ * @since 1.0.0
361
+ * @uses eo_get_next_occurence()
362
+ * @package event-date-functions
363
+ *
364
+ * @param string $format The format to use, using PHP Date format
365
+ * @param int $post_id The event (post) ID. Uses current event if empty.
366
+ */
367
+ function eo_next_occurence($format='',$post_id=0){
368
+ echo eo_get_next_occurence($format,$post_id);
369
+ }
370
+
371
+ /**
372
+ * Returns an array of datetimes (start and end) corresponding to the current occurrence of an event.
373
+ * If the event has multiple overlapping occurrences currently running, returns the one with the latest start date.
374
+ * To get the next occurrence{@see `eo_get_next_occurrence_of()`}
375
+ *
376
+ * @since 1.7
377
+ * @package event-date-functions
378
+ *
379
+ * @param int $post_id The event (post) ID. Uses current event if empty.
380
+ * @return array Array with keys 'start' and 'end', with corresponding datetime objects
381
+ */
382
+ function eo_get_current_occurrence_of($post_id=0){
383
+ global $wpdb;
384
+
385
+ $post_id = (int) ( empty($post_id) ? get_the_ID() : $post_id);
386
+
387
+ //Retrieve the blog's local time and create the date part
388
+ $tz = eo_get_blog_timezone();
389
+ $blog_now = new DateTime(null, $tz );
390
+ $now_date =$blog_now->format('Y-m-d');
391
+ $now_time =$blog_now->format('H:i:s');
392
+
393
+ //Get the current occurrence. May be multiple (overlapping) occurrences. Pick the latest.
394
+ $current_occurrence = $wpdb->get_row($wpdb->prepare("
395
+ SELECT StartDate, StartTime, EndDate, FinishTime
396
+ FROM {$wpdb->eo_events}
397
+ WHERE {$wpdb->eo_events}.post_id=%d
398
+ AND (
399
+ ({$wpdb->eo_events}.StartDate < %s) OR
400
+ ({$wpdb->eo_events}.StartDate = %s AND {$wpdb->eo_events}.StartTime <= %s)
401
+ )AND(
402
+ ({$wpdb->eo_events}.EndDate > %s) OR
403
+ ({$wpdb->eo_events}.EndDate = %s AND {$wpdb->eo_events}.EndDate >= %s)
404
+ )
405
+ ORDER BY {$wpdb->eo_events}.StartDate DESC
406
+ LIMIT 1",$post_id,$now_date,$now_date,$now_time, $now_date,$now_date,$now_time));
407
+
408
+ if( ! $current_occurrence )
409
+ return false;
410
+
411
+ $start = new DateTime($current_occurrence->StartDate.' '.$current_occurrence->StartTime, $tz);
412
+ $end = new DateTime($current_occurrence->EndDate.' '.$current_occurrence->FinishTime, $tz);
413
+
414
+ return compact('start','end');
415
+ }
416
+
417
+
418
+ /**
419
+ * Return true is the event is an all day event.
420
+ * @since 1.2
421
+ *
422
+ * @param int $post_id The event (post) ID. Uses current event if empty.
423
+ * @return bool True if event runs all day, or false otherwise
424
+ */
425
+ function eo_is_all_day($post_id=0){
426
+ $post_id = (int) ( empty($post_id) ? get_the_ID() : $post_id);
427
+
428
+ if( empty($post_id) )
429
+ return false;
430
+
431
+ $schedule = eo_get_event_schedule($post_id);
432
+
433
+ return (bool) $schedule['all_day'];
434
+ }
435
+
436
+ /**
437
+ * Returns the formated date of first occurrence of an event
438
+ * @since 1.0.0
439
+ * @package event-date-functions
440
+ *
441
+ * @param string $format the format to use, using PHP Date format
442
+ * @param int $post_id The event (post) ID. Uses current event if empty.
443
+ * @return string The formatted date
444
+ */
445
+ function eo_get_schedule_start($format='d-m-Y',$post_id=0){
446
+ $post_id = (int) ( empty($post_id) ? get_the_ID() : $post_id);
447
+ $schedule = eo_get_event_schedule($post_id);
448
+ $schedule_start = $schedule['schedule_start'];
449
+ return apply_filters('eventorganiser_get_schedule_start', eo_format_datetime( $schedule_start, $format ), $schedule_start, $format, $post_id );
450
+ }
451
+
452
+ /**
453
+ * Prints the formated date of first occurrence of an event
454
+ * @since 1.0.0
455
+ * @uses eo_get_schedule_start()
456
+ * @package event-date-functions
457
+ *
458
+ * @param string $format The format to use, using PHP Date format
459
+ * @param int $post_id The event (post) ID. Uses current event if empty.
460
+ */
461
+ function eo_schedule_start($format='d-m-Y',$post_id=0){
462
+ echo eo_get_schedule_start($format,$post_id);
463
+ }
464
+
465
+
466
+ /**
467
+ * Returns the formated date of the last occurrence of an event
468
+ * @since 1.4.0
469
+ * @package event-date-functions
470
+ *
471
+ * @param string $format The format to use, using PHP Date format
472
+ * @param int $post_id The event (post) ID. Uses current event if empty.
473
+ * @return string The formatted date
474
+ */
475
+ function eo_get_schedule_last($format='d-m-Y',$post_id=0){
476
+ $post_id = (int) ( empty($post_id) ? get_the_ID() : $post_id);
477
+ $schedule = eo_get_event_schedule($post_id);
478
+ $schedule_last = $schedule['schedule_last'];
479
+ return apply_filters('eventorganiser_get_schedule_last', eo_format_datetime( $schedule_last, $format ), $schedule_last, $format, $post_id );
480
+ }
481
+
482
+ /**
483
+ * Prints the formated date of the last occurrence of an event
484
+ * @since 1.4.0
485
+ * @uses eo_get_schedule_last()
486
+ * @package event-date-functions
487
+ *
488
+ * @param string $format The format to use, using PHP Date format
489
+ * @param int $post_id The event (post) ID. Uses current event if empty.
490
+ * @return string The formatted date
491
+ */
492
+ function eo_schedule_last($format='d-m-Y',$post_id=0){
493
+ echo eo_get_schedule_last($format,$post_id);
494
+ }
495
+
496
+
497
+ /**
498
+ * Returns true if event reoccurs or false if it is a one time event.
499
+ * @since 1.0.0
500
+ *
501
+ * @param int $post_id The event (post) ID. Uses current event if empty.
502
+ * @return bool true if event a reoccurring event
503
+ */
504
+ function eo_reoccurs($post_id=0){
505
+ $post_id = (int) ( empty($post_id) ? get_the_ID() : $post_id);
506
+
507
+ if( empty($post_id) )
508
+ return false;
509
+
510
+ $schedule = eo_get_event_schedule($post_id);
511
+
512
+ return ($schedule['schedule'] != 'once');
513
+ }
514
+
515
+
516
+ /**
517
+ * Returns a summary of the events schedule.
518
+ * @since 1.0.0
519
+ * @ignore
520
+ *
521
+ * @param int $post_id The event (post) ID. Uses current event if empty.
522
+ * @return string A summary of the events schedule.
523
+ */
524
+ function eo_get_schedule_summary($post_id=0){
525
+ global $post,$wp_locale;
526
+
527
+ $ical2day = array('SU'=> $wp_locale->weekday[0],'MO'=>$wp_locale->weekday[1],'TU'=>$wp_locale->weekday[2], 'WE'=>$wp_locale->weekday[3],
528
+ 'TH'=>$wp_locale->weekday[4],'FR'=>$wp_locale->weekday[5],'SA'=>$wp_locale->weekday[6]);
529
+
530
+ $nth= array(__('last','eventorganiser'),'',__('first','eventorganiser'),__('second','eventorganiser'),__('third','eventorganiser'),__('fourth','eventorganiser'));
531
+
532
+ $reoccur = eo_get_event_schedule($post_id);
533
+
534
+ if(empty($reoccur))
535
+ return false;
536
+
537
+ $return='';
538
+
539
+ if($reoccur['schedule']=='once'){
540
+ $return = __('one time only','eventorganiser');
541
+
542
+ }elseif($reoccur['schedule']=='custom'){
543
+ $return = __('custom reoccurrence','eventorganiser');
544
+
545
+ }else{
546
+ switch($reoccur['schedule']):
547
+
548
+ case 'daily':
549
+ if($reoccur['frequency']==1):
550
+ $return .=__('every day','eventorganiser');
551
+ else:
552
+ $return .=sprintf(__('every %d days','eventorganiser'),$reoccur['frequency']);
553
+ endif;
554
+ break;
555
+
556
+ case 'weekly':
557
+ if($reoccur['frequency']==1):
558
+ $return .=__('every week on','eventorganiser');
559
+ else:
560
+ $return .=sprintf(__('every %d weeks on','eventorganiser'),$reoccur['frequency']);
561
+ endif;
562
+
563
+ foreach( $reoccur['schedule_meta'] as $ical_day){
564
+ $days[] = $ical2day[$ical_day];
565
+ }
566
+ $return .=' '.implode(', ',$days);
567
+ break;
568
+
569
+ case 'monthly':
570
+ if($reoccur['frequency']==1):
571
+ $return .=__('every month on the','eventorganiser');
572
+ else:
573
+ $return .=sprintf(__('every %d months on the','eventorganiser'),$reoccur['frequency']);
574
+ endif;
575
+ $return .= ' ';
576
+ $bymonthday =preg_match('/^BYMONTHDAY=(\d{1,2})/' ,$reoccur['schedule_meta'],$matches);
577
+
578
+ if( $bymonthday ){
579
+ $d = intval($matches[1]);
580
+ $m =intval($reoccur['schedule_start']->format('n'));
581
+ $y =intval($reoccur['schedule_start']->format('Y'));
582
+ $reoccur['start']->setDate($y,$m,$d);
583
+ $return .= $reoccur['schedule_start']->format('jS');
584
+
585
+ }elseif($reoccur['schedule_meta']=='date'){
586
+ $return .= $reoccur['schedule_start']->format('jS');
587
+
588
+ }else{
589
+ $byday = preg_match('/^BYDAY=(-?\d{1,2})([a-zA-Z]{2})/' ,$reoccur['schedule_meta'],$matches);
590
+ if($byday):
591
+ $n=intval($matches[1])+1;
592
+ $return .=$nth[$n].' '.$ical2day[$matches[2]];
593
+ else:
594
+ $bydayOLD = preg_match('/^(-?\d{1,2})([a-zA-Z]{2})/' ,$reoccur['schedule_meta'],$matchesOLD);
595
+ $n=intval($matchesOLD[1])+1;
596
+ $return .=$nth[$n].' '.$ical2day[$matchesOLD[2]];
597
+ endif;
598
+ }
599
+ break;
600
+ case 'yearly':
601
+ if($reoccur['frequency']==1):
602
+ $return .=__('every year','eventorganiser');
603
+ else:
604
+ $return .=sprintf(__('every %d years','eventorganiser'),$reoccur['frequency']);
605
+ endif;
606
+ break;
607
+
608
+ endswitch;
609
+ $return .= ' '.__('until','eventorganiser').' '. eo_format_datetime($reoccur['schedule_last'],'M, jS Y');
610
+ }
611
+
612
+ return $return;
613
+ }
614
+
615
+ /**
616
+ * Prints a summary of the events schedule.
617
+ * @since 1.0.0
618
+ * @uses eo_get_schedule_summary()
619
+ * @ignore
620
+ *
621
+ * @param int $post_id The event (post) ID. Uses current event if empty.
622
+ */
623
+ function eo_display_reoccurence($post_id=0){
624
+ echo eo_get_schedule_summary($post_id);
625
+ }
626
+
627
+ /**
628
+ * Returns an array of occurrences. Each occurrence is an array with 'start' and 'end' key.
629
+ * Both of these hold a DateTime object (for the start and end of that occurrence respecitvely).
630
+ * @since 1.5
631
+ * @package event-date-functions
632
+ * @access private
633
+ * @ignore
634
+ *
635
+ * @param int $post_id The event (post) ID. Uses current event if empty.
636
+ * @return array Array of arrays of DateTime objects of the start and end date-times of occurences. False if none exist.
637
+ */
638
+ function eo_get_the_future_occurrences_of( $post_id=0 ){
639
+ global $wpdb;
640
+
641
+ $today = new DateTime('now',eo_get_blog_timezone());
642
+ $post_id = (int) ( empty($post_id) ? get_the_ID() : $post_id);
643
+
644
+ if(empty($post_id))
645
+ return false;
646
+
647
+ $results = $wpdb->get_results(
648
+ $wpdb->prepare(
649
+ "SELECT event_id, StartDate,StartTime,EndDate,FinishTime FROM {$wpdb->eo_events}
650
+ WHERE {$wpdb->eo_events}.post_id=%d AND ( StartDate > %s OR ( StartDate = %s AND StartTime >= %s) ) ORDER BY StartDate ASC",
651
+ $post_id,
652
+ $today->format('Y-m-d'),
653
+ $today->format('Y-m-d'),
654
+ $today->format('H:i:s')
655
+ )
656
+ );
657
+
658
+ if( !$results )
659
+ return false;
660
+
661
+ $occurrences=array();
662
+ foreach($results as $row):
663
+ $occurrences[$row->event_id] = array(
664
+ 'start' => new DateTime($row->StartDate.' '.$row->StartTime, eo_get_blog_timezone()),
665
+ 'end' => new DateTime($row->EndDate.' '.$row->FinishTime, eo_get_blog_timezone())
666
+ );
667
+ endforeach;
668
+
669
+ return apply_filters( 'eventorganiser_get_the_future_occurrences_of', $occurrences, $post_id );
670
+ }
671
+ /**
672
+ * Returns an array of occurrences. Each occurrence is an array with 'start' and 'end' key.
673
+ * Both of these hold a DateTime object (for the start and end of that occurrence respecitvely).
674
+ * @since 1.5
675
+ * @package event-date-functions
676
+ *
677
+ * @param int $post_id The event (post) ID. Uses current event if empty.
678
+ * @return array Array of arrays of DateTime objects of the start and end date-times of occurences. False if none exist.
679
+ */
680
+ function eo_get_the_occurrences_of($post_id=0){
681
+ global $wpdb;
682
+
683
+ $post_id = (int) ( empty($post_id) ? get_the_ID() : $post_id);
684
+
685
+ if(empty($post_id))
686
+ return false;
687
+
688
+ //Can't cache datetime objects before 5.3
689
+ //@see{http://wordpress.org/support/topic/warning-datetimeformat-functiondatetime-format?replies=7#post-3940247}
690
+ if( version_compare(PHP_VERSION, '5.3.0') >= 0 ){
691
+ $occurrences = wp_cache_get( 'eventorganiser_occurrences_'.$post_id );
692
+ }else{
693
+ $occurrences = false;
694
+ }
695
+
696
+ if( !$occurrences ){
697
+
698
+ $results = $wpdb->get_results($wpdb->prepare("
699
+ SELECT event_id, StartDate,StartTime,EndDate,FinishTime FROM {$wpdb->eo_events}
700
+ WHERE {$wpdb->eo_events}.post_id=%d ORDER BY StartDate ASC",$post_id));
701
+
702
+ if( !$results )
703
+ return false;
704
+
705
+ $occurrences=array();
706
+ foreach($results as $row):
707
+ $occurrences[$row->event_id] = array(
708
+ 'start' => new DateTime($row->StartDate.' '.$row->StartTime, eo_get_blog_timezone()),
709
+ 'end' => new DateTime($row->EndDate.' '.$row->FinishTime, eo_get_blog_timezone())
710
+ );
711
+ endforeach;
712
+ wp_cache_set( 'eventorganiser_occurrences_'.$post_id, $occurrences );
713
+ }
714
+
715
+ return apply_filters( 'eventorganiser_get_the_occurrences_of', $occurrences, $post_id );
716
+ }
717
+
718
+ /**
719
+ * Returns the colour of a category
720
+ * @ignore
721
+ * @access private
722
+ */
723
+ function eo_get_category_color($term){
724
+ return eo_get_category_meta($term,'color');
725
+ }
726
+
727
+ /**
728
+ * Returns the colour of a category associated with the event.
729
+ * Applies the {@see `eventorganiser_event_color`} filter.
730
+ * @since 1.6
731
+ *
732
+ * @param int $post_id The event (post) ID. Uses current event if empty.
733
+ * @return string The colour of the category in HEX format
734
+ */
735
+ function eo_get_event_color($post_id=0){
736
+ $post_id = (int) ( empty($post_id) ? get_the_ID() : $post_id);
737
+
738
+ if( empty($post_id) )
739
+ return false;
740
+
741
+ $color = false;
742
+
743
+ $terms = get_the_terms($post_id, 'event-category');
744
+ if( $terms && !is_wp_error($terms) ){
745
+ foreach ($terms as $term):
746
+ if( ! empty($term->color) ){
747
+ $color_code = ltrim($term->color, '#');
748
+ if ( ctype_xdigit($color_code) && (strlen($color_code) == 6 || strlen($color_code) == 3)){
749
+ $color = '#'.$color_code;
750
+ break;
751
+ }
752
+ }
753
+ endforeach;
754
+ }
755
+
756
+ /**
757
+ * Filters the colour associated with an event
758
+ *@link http://wordpress.org/support/topic/plugin-event-organiser-color-code-for-venues-instead-of-categories
759
+ *@param string $color Event colour in HEX format
760
+ *@param int $post_id The event (post) ID
761
+ */
762
+ return apply_filters('eventorganiser_event_color',$color,$post_id);
763
+ }
764
+
765
+ /**
766
+ * Returns an array of classes associated with an event.
767
+ * Adds eo-event-venue-[venue slug] for the event's venue.
768
+ * Adds eo-event-cat-[category slug] for each event category it bleongs to.
769
+ * Adds eo-event-[future|past|running].
770
+ * Applies filter {@see `eventorganiser_event_classes`}
771
+ * @since 1.6
772
+ *
773
+ * @param int $post_id The event (post) ID. Uses current event if empty.
774
+ * @param int $occurrence_id The occurrence ID. Uses current event if empty.
775
+ * @return array Array of classes
776
+ */
777
+ function eo_get_event_classes($post_id=0, $occurrence_id=0){
778
+ global $post;
779
+
780
+ $post_id = (int) ( empty($post_id) ? get_the_ID() : $post_id );
781
+ $occurrence_id = (int) ( empty($occurrence_id) && isset($post->occurrence_id) ? $post->occurrence_id : $occurrence_id );
782
+
783
+ $event_classes = array();
784
+
785
+ //Add venue class
786
+ if( eo_get_venue_slug() )
787
+ $event_classes[] = 'eo-event-venue-'.eo_get_venue_slug();
788
+
789
+ //Add category classes
790
+ $cats= get_the_terms(get_the_ID(), 'event-category');
791
+ if( $cats && !is_wp_error($cats) ){
792
+ foreach ($cats as $cat)
793
+ $event_classes[] = 'eo-event-cat-'.$cat->slug;
794
+ }
795
+
796
+ //Add 'time' class
797
+ $start = eo_get_the_start(DATETIMEOBJ, $post_id, null, $occurrence_id);
798
+ $end= eo_get_the_end(DATETIMEOBJ, $post_id, null, $occurrence_id);
799
+ $now = new DateTime('now',eo_get_blog_timezone());
800
+ if( $start > $now ){
801
+ $event_classes[] = 'eo-event-future';
802
+ }elseif( $end < $now ){
803
+ $event_classes[] = 'eo-event-past';
804
+ }else{
805
+ $event_classes[] = 'eo-event-running';
806
+ }
807
+
808
+ $event_classes = array_unique($event_classes);
809
+ return apply_filters('eventorganiser_event_classes', $event_classes, $post_id, $occurrence_id);
810
+ }
811
+
812
+
813
+ /**
814
+ * Checks if the query is for an event taxonomy.
815
+ *
816
+ * When no $query is passed, acts as a simple wrapper for `is_tax()`.
817
+ * More generally acts as a wrapper for `$query->is_tax()`.
818
+ *
819
+ * @since 1.6
820
+ *
821
+ * @param $query - The query to check. If not passed, uses the global $wp_query;
822
+ * @return bool True if query is for any event taxonomy (e.g. 'event-venue', 'event-category', 'event-tag').
823
+ */
824
+ function eo_is_event_taxonomy( $query = false ){
825
+ $event_tax = get_object_taxonomies( 'event' );
826
+
827
+ //Handle post tags
828
+ if( in_array( 'post_tag', $event_tax ) ){
829
+ if( ( !$query && is_tag() ) || $query->is_tag() )
830
+ return true;
831
+ }
832
+
833
+ //Handle categories
834
+ if( in_array( 'category', $event_tax ) ){
835
+ if( ( !$query && is_category() ) || $query->is_category() )
836
+ return true;
837
+ }
838
+
839
+ if( !$query ){
840
+ return is_tax( $event_tax );
841
+ }else{
842
+ return $query->is_tax( $event_tax );
843
+ }
844
+ }
845
+
846
+ /**
847
+ * Retrieves the permalink for the ICAL event feed. A simple wrapper for `get_feed_link()`.
848
+ *
849
+ * Retrieve the permalink for the events feed. The returned link is the url with which visitors can subscribe
850
+ * to your events. Visiting the url directly will prompt a download an ICAL file of your events. The events feed
851
+ * includes only **public** events (draft, private and trashed events are not included).
852
+ *
853
+ * @since 1.6
854
+ *
855
+ * @return string The link to the ICAL event feed..
856
+ */
857
+ function eo_get_events_feed(){
858
+ return get_feed_link('eo-events');
859
+ }
860
+
861
+
862
+ /**
863
+ * Returns a the url which adds a particular occurrence of an event to
864
+ * a google calendar. Must be used inside the loop
865
+ *
866
+ *Returns an url which adds a particular occurrence of an event to a Google calendar. This function can only be used inside the loop.
867
+ * An entire series cannot be added to a Google calendar - however users can subscribe to your events. Please note that, unlike
868
+ * subscribing to events, changes made to an event will not be reflected on an event added to the Google calendar.
869
+ *
870
+ * @since 1.2.0
871
+ *
872
+ * @return string Url which adds event to a google calendar
873
+ */
874
+ function eo_get_the_GoogleLink(){
875
+ global $post;
876
+ setup_postdata($post);
877
+
878
+ if(empty($post)|| get_post_type($post )!='event'){
879
+ wp_reset_postdata();
880
+ return false;
881
+ }
882
+
883
+ $start = eo_get_the_start(DATETIMEOBJ);
884
+ $end = eo_get_the_start(DATETIMEOBJ);
885
+
886
+ if(eo_is_all_day()):
887
+ $end->modify('+1 second');
888
+ $format = 'Ymd';
889
+ else:
890
+ $format = 'Ymd\THis\Z';
891
+ $start->setTimezone( new DateTimeZone('UTC') );
892
+ $end->setTimezone( new DateTimeZone('UTC') );
893
+ endif;
894
+
895
+ $excerpt = apply_filters('the_excerpt_rss', get_the_excerpt());
896
+
897
+ $url = add_query_arg(array(
898
+ 'text'=>get_the_title(),
899
+ 'dates'=>$start->format($format).'/'.$end->format($format),
900
+ 'trp'=>false,
901
+ 'details'=> esc_html($excerpt),
902
+ 'sprop'=>get_bloginfo('name')
903
+ ),'http://www.google.com/calendar/event?action=TEMPLATE');
904
+
905
+ $venue_id = eo_get_venue();
906
+ if($venue_id):
907
+ $venue =eo_get_venue_name($venue_id).", ".implode(', ',eo_get_venue_address($venue_id));
908
+ $url = add_query_arg('location',$venue, $url);
909
+ endif;
910
+
911
+ wp_reset_postdata();
912
+ return $url;
913
+ }
914
+
915
+ /**
916
+ * @ignore
917
+ */
918
+ function eo_has_event_started($id='',$occurrence){
919
+ $tz = eo_get_blog_timezone();
920
+ $start = new DateTime(eo_get_the_start('d-m-Y H:i',$id,$occurrence), $tz);
921
+ $now = new DateTime('now', $tz);
922
+
923
+ return ($start <= $now );
924
+ }
925
+
926
+ /**
927
+ * @ignore
928
+ */
929
+ function eo_has_event_finished($id='',$occurrence=0){
930
+ $tz = eo_get_blog_timezone();
931
+ $end = new DateTime(eo_get_the_end('d-m-Y H:i',$id,$occurrence), $tz);
932
+ $now = new DateTime('now', $tz);
933
+
934
+ return ($end <= $now );
935
+ }
936
+
937
+ /**
938
+ * @ignore
939
+ */
940
+ function eo_event_category_dropdown( $args = '' ) {
941
+ $defaults = array(
942
+ 'show_option_all' => '',
943
+ 'echo' => 1,
944
+ 'selected' => 0,
945
+ 'name' => 'event-category',
946
+ 'id' => '',
947
+ 'class' => 'postform event-organiser event-category-dropdown event-dropdown',
948
+ 'tab_index' => 0,
949
+ );
950
+
951
+ $defaults['selected'] = (is_tax('event-category') ? get_query_var('event-category') : 0);
952
+ $r = wp_parse_args( $args, $defaults );
953
+ $r['taxonomy']='event-category';
954
+ extract( $r );
955
+
956
+ $tab_index_attribute = '';
957
+ if ( (int) $tab_index > 0 )
958
+ $tab_index_attribute = " tabindex=\"$tab_index\"";
959
+
960
+ $categories = get_terms($taxonomy, $r );
961
+ $name = esc_attr( $name );
962
+ $class = esc_attr( $class );
963
+ $id = $id ? esc_attr( $id ) : $name;
964
+
965
+ $output = "<select style='width:150px' name='$name' id='$id' class='$class' $tab_index_attribute>\n";
966
+
967
+ if ( $show_option_all ) {
968
+ $output .= '<option '.selected($selected,0,false).' value="0">'.$show_option_all.'</option>';
969
+ }
970
+
971
+ if ( ! empty( $categories ) ) {
972
+ foreach ($categories as $term):
973
+ $output .= '<option value="'.$term->slug.'"'.selected($selected,$term->slug,false).'>'.$term->name.'</option>';
974
+ endforeach;
975
+ }
976
+ $output .= "</select>\n";
977
+
978
+ if ( $echo )
979
+ echo $output;
980
+
981
+ return $output;
982
+ }
983
+
984
+ /**
985
+ * Returns HTML mark-up for the fullCalendar
986
+ *
987
+ * It also (indirectly) triggers the enquing of the necessary scripts and styles. The `$args` array
988
+ * accepts exactly the same arguments as the shortcode, they are
989
+ *
990
+ * * **headerleft** (string) What appears on the left of the calendar header. Default 'title'.
991
+ * * **headercenter** (string) What appears on the left of the calendar header. Default ''.
992
+ * * **headerright** (string) What appears on the left of the calendar header. Default 'prev next today'.
993
+ * * **defaultview** (string) The view the calendar loads on. Default 'month',
994
+ * * **event_category** (string) Restrict calendar to specified category. Default '' (all categories)
995
+ * * **event_venue** (string) Restrict calendar to specified venue. Default '' (all venues)
996
+ * * **timeformat** (string) Time format for calendar. Default 'G:i'.
997
+ * * **axisformat** (string) Axis time format (for day/week views). WP's time format option.
998
+ * * **key** (bool) Whether to show a category key. Default false.
999
+ * * **tooltip** (bool) Whether to show a tooltips. Default true.
1000
+ * * **weekends** (bool) Whether to include weekends in the calendar. Default true.
1001
+ * * **mintime** (string) Earliest time to show on week/day views. Default '0',
1002
+ * * **maxtime** (string) Latest time to show on week/day views. Default '24',
1003
+ * * **alldayslot** (bool) Whether to include an all day slot (week / day views) in the calendar. Default true.
1004
+ * * **alldaytext** (string) Text to display in all day slot. Default 'All Day'.
1005
+ * * **columnformatmonth** (string) Dateformat for month columns. Default 'D'.
1006
+ * * **columnformatweek** (string) Dateformat for month columns. Default 'D n/j'.
1007
+ * * **columnformatday** (string) Dateformat for month columns. Default 'l n/j',
1008
+ * * **year** The year the calendar should start on (e.g. 2013)
1009
+ * * **month** The month the calendar should start on (1=Jan, 12=Dec)
1010
+ * * **date** The calendar the date should start on
1011
+ *
1012
+ * @link http://arshaw.com/fullcalendar/ The fullCalendar (jQuery plug-in)
1013
+ * @link https://github.com/stephenharris/fullcalendar Event Organiser version of fullCalendar
1014
+ * @since 1.7
1015
+ * @param array $args An array of attributes for the calendar
1016
+ * @return string HTML mark-up.
1017
+ */
1018
+ function eo_get_event_fullcalendar( $args ){
1019
+
1020
+ $defaults = array(
1021
+ 'headerleft'=>'title', 'headercenter'=>'', 'headerright'=>'prev next today', 'defaultview'=>'month',
1022
+ 'event_category'=>'', 'event_venue'=>'', 'timeformat'=>'G:i', 'axisformat'=>get_option('time_format'), 'key'=>false,
1023
+ 'tooltip'=>true, 'weekends'=>true, 'mintime'=>'0', 'maxtime'=>'24', 'alldayslot'=>true,
1024
+ 'alldaytext'=>__('All Day','eventorganiser'), 'columnformatmonth'=>'D', 'columnformatweek'=>'D n/j', 'columnformatday'=>'l n/j',
1025
+ 'titleformatmonth' => 'F Y', 'titleformatweek' => "M j[ Y]{ '&#8212;'[ M] j Y}", 'titleformatday' => 'l, M j, Y',
1026
+ 'year' => false, 'month' => false, 'date' => false,
1027
+ );
1028
+ $args = shortcode_atts( $defaults, $args );
1029
+ $key = $args['key'];
1030
+ unset($args['key']);
1031
+
1032
+ //Convert php time format into xDate time format
1033
+ $date_attributes = array( 'timeformat', 'axisformat', 'columnformatday', 'columnformatweek', 'columnformatmonth',
1034
+ 'titleformatmonth', 'titleformatday', 'titleformatweek' );
1035
+ $args['timeformatphp'] = $args['timeformat'];
1036
+ foreach ( $date_attributes as $date_attribute ){
1037
+ $args[$date_attribute] = str_replace( '((', '[', $args[$date_attribute] );
1038
+ $args[$date_attribute] = str_replace( '))', ']', $args[$date_attribute] );
1039
+ $args[$date_attribute.'php'] = $args[$date_attribute];
1040
+ $args[$date_attribute] = eventorganiser_php2xdate( $args[$date_attribute] );
1041
+ }
1042
+
1043
+ //Month expects 0-11, we ask for 1-12.
1044
+ $args['month'] = ( $args['month'] ? $args['month'] - 1 : false );
1045
+
1046
+ EventOrganiser_Shortcodes::$calendars[] = array_merge( $args );
1047
+ EventOrganiser_Shortcodes::$add_script = true;
1048
+ $id = count( EventOrganiser_Shortcodes::$calendars );
1049
+
1050
+ $html = '<div id="eo_fullcalendar_'.$id.'_loading" style="background:white;position:absolute;z-index:5" >';
1051
+ $html .= '<img src="'.esc_url(EVENT_ORGANISER_URL.'/css/images/loading-image.gif').'" style="vertical-align:middle; padding: 0px 5px 5px 0px;" />' . __( 'Loading&#8230;', 'eventorganiser' );
1052
+ $html .= '</div>';
1053
+ $html .= '<div class="eo-fullcalendar eo-fullcalendar-shortcode" id="eo_fullcalendar_'.$id.'"></div>';
1054
+
1055
+ if ( $key ){
1056
+ $args = array( 'orderby' => 'name', 'show_count' => 0, 'hide_empty' => 0 );
1057
+ $html .= eventorganiser_category_key( $args,$id );
1058
+ }
1059
+ return $html;
1060
+ }
1061
+
1062
+
1063
+ /**
1064
+ * Returns HTML mark-up for a list of event meta information.
1065
+ *
1066
+ * Uses microformat.
1067
+ * @since 1.7
1068
+ * @ignore
1069
+ * @param int $post_id The event (post) ID. Uses current event if not supplied
1070
+ * @return string|bool HTML mark-up. False if an invalid $post_is provided.
1071
+ */
1072
+ function eo_get_event_meta_list( $post_id=0 ){
1073
+
1074
+ $post_id = (int) ( empty($post_id) ? get_the_ID() : $post_id);
1075
+
1076
+ if( empty($post_id) )
1077
+ return false;
1078
+
1079
+ $html = '<ul class="eo-event-meta" style="margin:10px 0px;">';
1080
+
1081
+ if( $venue_id = eo_get_venue($post_id) ){
1082
+ $html .= sprintf('<li><strong>%s:</strong> <a href="%s">
1083
+ <span itemprop="location" itemscope itemtype="http://data-vocabulary.org/​Organization">
1084
+ <span itemprop="name">%s</span>
1085
+ <span itemprop="geo" itemscope itemtype="http://data-vocabulary.org/​Geo">
1086
+ <meta itemprop="latitude" content="%f" />
1087
+ <meta itemprop="longitude" content="%f" />
1088
+ </span>
1089
+ </span></a></li>',
1090
+ __('Venue','eventorganiser'),
1091
+ eo_get_venue_link($venue_id),
1092
+ eo_get_venue_name($venue_id),
1093
+ eo_get_venue_lat($venue_id),
1094
+ eo_get_venue_lng($venue_id)
1095
+ );
1096
+ }
1097
+
1098
+ if( get_the_terms(get_the_ID(),'event-category') ){
1099
+ $html .= sprintf('<li><strong>%s:</strong> %s</li>',
1100
+ __('Categories','eventorganiser'),
1101
+ get_the_term_list( get_the_ID(),'event-category', '', ', ', '' )
1102
+ );
1103
+ }
1104
+
1105
+ if( get_the_terms(get_the_ID(),'event-tag') && !is_wp_error( get_the_terms(get_the_ID(),'event-tag') ) ){
1106
+ $html .= sprintf('<li><strong>%s:</strong> %s</li>',
1107
+ __('Tags','eventorganiser'),
1108
+ get_the_term_list( get_the_ID(),'event-tag', '', ', ', '' )
1109
+ );
1110
+ }
1111
+
1112
+ $html .='</ul>';
1113
+
1114
+ return apply_filters('eventorganiser_event_meta_list', $html, $post_id);
1115
+ }
1116
+
1117
+
1118
+ /**
1119
+ * Returns an the link for the event archive
1120
+ *
1121
+ * Optionally provide the year , or month or day to get an year/month/day archive link.
1122
+ * To get a month archive you should provide a year
1123
+ * To get a day archive you should provide a year and month.
1124
+ *
1125
+ *@since 1.7
1126
+ *@param int $year Year in full format, e.g. 2018. Must be provide for date-based archive link.
1127
+ *@param int $month Numeric representation of month. 1 = Jan, 12 = Dec. Must be provide for month or day archive link
1128
+ *@param int $day Day of the month 1-31
1129
+ *@return string Link to the requested archive
1130
+ *
1131
+ */
1132
+ function eo_get_event_archive_link( $year=false,$month=false, $day=false){
1133
+ global $wp_rewrite;
1134
+
1135
+ $archive = get_post_type_archive_link('event');
1136
+
1137
+ if( $year == false && $month == false && $day == false )
1138
+ return $archive;
1139
+
1140
+ $_year = str_pad($year, 4, "0", STR_PAD_LEFT);
1141
+ $_month = str_pad($month, 2, "0", STR_PAD_LEFT);
1142
+ $_day = str_pad($day, 2, "0", STR_PAD_LEFT);
1143
+
1144
+ if( $day ){
1145
+ $date = compact('_year','_month','_day');
1146
+ }elseif( $month ){
1147
+ $date = compact('_year','_month');
1148
+ }else{
1149
+ $date = compact('_year');
1150
+ }
1151
+
1152
+ if( $archive && $wp_rewrite->using_mod_rewrite_permalinks() && $permastruct = $wp_rewrite->get_extra_permastruct('event_archive') ){
1153
+ $archive = home_url( str_replace('%event_ondate%',implode('/',$date), $permastruct ) );
1154
+ }else{
1155
+ $archive = add_query_arg('ondate',implode('-',$date),$archive);
1156
+ }
1157
+
1158
+ return $archive;
1159
+ }
1160
+
1161
+
1162
+ function eo_break_occurrence( $post_id, $event_id ){
1163
+
1164
+ global $post;
1165
+ $post = get_post( $post_id );
1166
+ setup_postdata( $post_id );
1167
+
1168
+ do_action( 'eventorganiser_pre_break_occurrence', $post_id, $event_id );
1169
+
1170
+ $tax_input = array();
1171
+ foreach ( array( 'event-category', 'event-tag', 'event-venue' ) as $tax ):
1172
+ $terms = get_the_terms( $post->ID, $tax );
1173
+ if ( $terms && !is_wp_error( $terms ) ){
1174
+ $tax_input[$tax] = array_map( 'intval', wp_list_pluck( $terms, 'term_id' ) );
1175
+ }
1176
+ endforeach;
1177
+
1178
+ //Post details
1179
+ $post_array = array(
1180
+ 'post_title' => $post->post_title, 'post_name' => $post->post_name, 'post_author' => $post->post_author,
1181
+ 'post_content' => $post->post_content, 'post_status' => $post->post_status, 'post_date' => $post->post_date,
1182
+ 'post_date_gmt' => $post->post_date_gmt, 'post_excerpt' => $post->post_excerpt, 'post_password' => $post->post_password,
1183
+ 'post_type' => 'event', 'tax_input' => $tax_input, 'comment_status' => $post->comment_status, 'ping_status' => $post->ping_status,
1184
+ );
1185
+
1186
+ //Event details
1187
+ $event_array = array(
1188
+ 'start' => eo_get_the_start( DATETIMEOBJ, $post_id, null, $event_id ),
1189
+ 'end' => eo_get_the_end(DATETIMEOBJ, $post_id, null, $event_id ),
1190
+ 'all_day' => ( eo_is_all_day( $post_id ) ? 1 : 0 ),
1191
+ 'schedule' => 'once',
1192
+ 'frequency' => 1,
1193
+ );
1194
+
1195
+ //Create new event with duplicated details (new event clears cache)
1196
+ $new_event_id = eo_insert_event( $post_array, $event_array );
1197
+
1198
+ //delete occurrence, and copy post meta
1199
+ if ( $new_event_id && !is_wp_error( $new_event_id ) ){
1200
+ $response = _eventorganiser_remove_occurrence( $post_id, $event_id );
1201
+
1202
+ $post_custom = get_post_custom( $post_id );
1203
+ foreach ( $post_custom as $meta_key => $meta_values ) {
1204
+
1205
+ //Don't copy these
1206
+ $ignore_meta = array( '_eo_tickets', '_edit_last', '_edit_last', '_edit_lock' ) ;
1207
+ $ignore_meta = apply_filters( 'eventorganiser_breaking_occurrence_exclude_meta', $ignore_meta );
1208
+ if( in_array( $meta_key, $ignore_meta ) )
1209
+ continue;
1210
+
1211
+ //Don't copy event meta
1212
+ if( 0 == strncmp( $meta_key, '_eventorganiser', 15 ) )
1213
+ continue;
1214
+
1215
+ foreach ( $meta_values as $meta_value ) {
1216
+ //get_post_meta() without a key doesn't unserialize:
1217
+ // @see{https://github.com/WordPress/WordPress/blob/3.5.1/wp-includes/meta.php#L289}
1218
+ $meta_value = maybe_unserialize( $meta_value );
1219
+ add_post_meta( $new_event_id, $meta_key, $meta_value );
1220
+ }
1221
+ }
1222
+ }
1223
+ _eventorganiser_delete_calendar_cache();
1224
+
1225
+ do_action( 'eventorganiser_occurrence_broken', $post_id, $event_id, $new_event_id );
1226
+
1227
+ wp_reset_postdata();
1228
+ return $new_event_id;
1229
+ }
1230
+
1231
+ ?>
includes/event-organiser-install.php ADDED
@@ -0,0 +1,388 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Install routine
5
+ *
6
+ *@since 1.0
7
+ *@access private
8
+ *@ignore
9
+ */
10
+ function eventorganiser_install( $is_networkwide = false ){
11
+ global $wpdb;
12
+
13
+ // Is this multisite and did the user click network activate?
14
+ $is_multisite = ( function_exists('is_multisite') && is_multisite() );
15
+
16
+ if ($is_multisite && $is_networkwide) {
17
+ // Get the current blog so we can return to it.
18
+ $current_blog_id = get_current_blog_id();
19
+
20
+ // Get a list of all blogs.
21
+ $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
22
+ if( $blog_ids ){
23
+ foreach ( $blog_ids as $blog_id ) {
24
+ switch_to_blog( $blog_id );
25
+ eventorganiser_site_install();
26
+ }
27
+ switch_to_blog( $current_blog_id );
28
+ }else{
29
+ eventorganiser_site_install();
30
+ }
31
+ }else {
32
+ eventorganiser_site_install();
33
+ }
34
+ }
35
+
36
+ function eventorganiser_site_install(){
37
+ global $wpdb, $eventorganiser_db_version;
38
+
39
+ eventorganiser_wpdb_fix();
40
+
41
+ $charset_collate = '';
42
+ if ( ! empty($wpdb->charset) )
43
+ $charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
44
+ if ( ! empty($wpdb->collate) )
45
+ $charset_collate .= " COLLATE $wpdb->collate";
46
+
47
+ //Events table
48
+ $sql_events_table = "CREATE TABLE " .$wpdb->eo_events. " (
49
+ event_id bigint(20) NOT NULL AUTO_INCREMENT,
50
+ post_id bigint(20) NOT NULL,
51
+ StartDate DATE NOT NULL,
52
+ EndDate DATE NOT NULL,
53
+ StartTime TIME NOT NULL,
54
+ FinishTime TIME NOT NULL,
55
+ event_occurrence bigint(20) NOT NULL,
56
+ PRIMARY KEY (event_id),
57
+ KEY StartDate (StartDate),
58
+ KEY EndDate (EndDate)
59
+ )".$charset_collate;
60
+
61
+ //Venue meta table
62
+ $sql_venuemeta_table ="CREATE TABLE {$wpdb->prefix}eo_venuemeta (
63
+ meta_id bigint(20) unsigned NOT NULL auto_increment,
64
+ eo_venue_id bigint(20) unsigned NOT NULL default '0',
65
+ meta_key varchar(255) default NULL,
66
+ meta_value longtext,
67
+ PRIMARY KEY (meta_id),
68
+ KEY eo_venue_id (eo_venue_id),
69
+ KEY meta_key (meta_key)
70
+ ) $charset_collate; ";
71
+
72
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
73
+ dbDelta($sql_events_table);
74
+ dbDelta($sql_venuemeta_table);
75
+
76
+ //Add options and capabilities
77
+ $eventorganiser_options = array (
78
+ 'supports' => array('title','editor','author','thumbnail','excerpt','custom-fields','comments'),
79
+ 'event_redirect' => 'events',
80
+ 'dateformat'=>'dd-mm',
81
+ 'prettyurl'=> 1,
82
+ 'templates'=> 1,
83
+ 'addtomenu'=> 0,
84
+ 'excludefromsearch'=>0,
85
+ 'showpast'=> 0,
86
+ 'group_events'=>'',
87
+ 'url_venue'=>'events/event',
88
+ 'url_venue'=> 'events/venues',
89
+ 'url_cat' => 'events/category',
90
+ 'url_tag' => 'events/tag',
91
+ 'navtitle' => __('Events','eventorganiser'),
92
+ 'eventtag' => 1,
93
+ 'feed' => 1,
94
+ 'runningisnotpast' => 0,
95
+ 'deleteexpired' => 0
96
+ );
97
+ add_option('eventorganiser_options',$eventorganiser_options);
98
+
99
+ /* Add existing notices */
100
+ $notices = array('autofillvenue17','changedtemplate17');
101
+ add_option('eventorganiser_admin_notices',$notices);
102
+
103
+ //Add roles to administrator
104
+ global $wp_roles;
105
+ $all_roles = $wp_roles->roles;
106
+ $eventorganiser_roles = array(
107
+ 'edit_events' => __( 'Edit Events', 'eventorganiser' ),
108
+ 'publish_events' => __( 'Publish Events', 'eventorganiser' ),
109
+ 'delete_events' => __( 'Delete Events', 'eventorganiser' ),
110
+ 'edit_others_events' => __( 'Edit Others\' Events', 'eventorganiser' ),
111
+ 'delete_others_events' => __( 'Delete Other\'s Events', 'eventorganiser' ),
112
+ 'read_private_events' => __( 'Read Private Events', 'eventorganiser' ),
113
+ 'manage_venues' => __( 'Manage Venues', 'eventorganiser' ),
114
+ 'manage_event_categories' => __( 'Manage Event Categories & Tags', 'eventorganiser' ),
115
+ );
116
+ foreach ($all_roles as $role_name => $display_name):
117
+ $role = $wp_roles->get_role($role_name);
118
+ if($role->has_cap('manage_options')){
119
+ foreach($eventorganiser_roles as $eo_role=>$eo_role_display):
120
+ $role->add_cap($eo_role);
121
+ endforeach;
122
+ }
123
+ endforeach; //End foreach $all_roles
124
+
125
+ //Manually register CPT and CTs ready for flushing
126
+ eventorganiser_create_event_taxonomies();
127
+ eventorganiser_cpt_register();
128
+
129
+ //Flush rewrite rules only on activation, and after CPT/CTs has been registered.
130
+ flush_rewrite_rules();
131
+ }
132
+
133
+ /**
134
+ * Deactivate routine
135
+ *
136
+ * Flushes rewrite rules. Don't clear cron jobs, as these won't be re-added.
137
+ *
138
+ *@since 1.5
139
+ *@access private
140
+ *@ignore
141
+ */
142
+ function eventorganiser_deactivate(){
143
+ flush_rewrite_rules();
144
+ }
145
+
146
+
147
+ /**
148
+ * Upgrade routine. Hooked onto admin_init
149
+ *
150
+ *@since 1.1
151
+ *@access private
152
+ *@ignore
153
+ */
154
+ function eventorganiser_upgradecheck(){
155
+ global $eventorganiser_db_version, $wpdb;
156
+ global $EO_Errors;
157
+
158
+ $installed_ver = get_option('eventorganiser_version');
159
+
160
+ if( empty($installed_ver) ){
161
+ //This is a fresh install. Add current database version
162
+ add_option('eventorganiser_version', $eventorganiser_db_version);
163
+
164
+ //But a bug in 1.5 means that it could be that they first installed in 1.5 (as no db version was added)
165
+ //So set to 1.5. Fresh installs will have to go through the 1.6 (and above) update, but this is ok.
166
+ $installed_ver = '1.5';
167
+ }
168
+
169
+ //If this is an old version, perform some updates.
170
+ if ( !empty($installed_ver ) && $installed_ver != $eventorganiser_db_version ):
171
+
172
+ if($installed_ver <'1.3'){
173
+ wp_die('You cannot upgrade to this version from 1.3 or before. Please upgrade to 1.5.7 first.');
174
+ }
175
+
176
+ if($installed_ver <'1.4'){
177
+ eventorganiser_140_update();
178
+ }
179
+
180
+ if($installed_ver <'1.5'){
181
+ eventorganiser_150_update();
182
+ }
183
+ if( $installed_ver < '1.6' ){
184
+ //Remove columns:
185
+ $columns = $wpdb->get_col("DESC {$wpdb->eo_events}", 0);
186
+ $remove_columns = array('Venue','event_schedule','event_schedule_meta', 'event_frequency','reoccurrence_start', 'reoccurrence_end' );
187
+ $delete_columns = array_intersect($remove_columns, $columns);
188
+ if( !empty($delete_columns) )
189
+ $sql = $wpdb->query("ALTER TABLE {$wpdb->eo_events} DROP COLUMN ".implode(', DROP COLUMN ',$delete_columns).';');
190
+
191
+ eventorganiser_install();
192
+ }
193
+
194
+ if( $installed_ver < '1.6.2' ){
195
+ $options = get_option('eventorganiser_options');
196
+ if( !empty($options['eventtag']) ){
197
+ $options['supports'][] = 'eventtag';
198
+ update_option('eventorganiser_options', $options);
199
+ }
200
+ }
201
+ if( $installed_ver < '1.7.1' ){
202
+ //Forgot to remove event_allday in 1.6 upgrade. This causes problems no Windows servers.
203
+ $columns = $wpdb->get_col("DESC {$wpdb->eo_events}", 0);
204
+ $remove_columns = array('event_allday');
205
+ if( !empty($delete_columns) )
206
+ $sql = $wpdb->query("ALTER TABLE {$wpdb->eo_events} DROP COLUMN ".implode(', DROP COLUMN ',$delete_columns).';');
207
+ flush_rewrite_rules();
208
+ }
209
+ update_option('eventorganiser_version', $eventorganiser_db_version);
210
+
211
+ //Run upgrade checks
212
+ add_action('admin_notices', 'eventorganiser_db_checks',0);
213
+ endif;
214
+ }
215
+ add_action('admin_init', 'eventorganiser_upgradecheck');
216
+
217
+ /**
218
+ * Upgrade routine for 1.5
219
+ *
220
+ *@since 1.5
221
+ *@access private
222
+ *@ignore
223
+ */
224
+ function eventorganiser_150_update(){
225
+ global $wpdb;
226
+ $et =$wpdb->eo_events;
227
+ $events = $wpdb->get_results("SELECT*, min({$et}.StartDate) as StartDate, min({$et}.EndDate) as EndDate FROM $wpdb->eo_events GROUP BY {$et}.post_id ORDER BY {$et}.StartDate");
228
+ if( $events ):
229
+ foreach( $events as $event ):
230
+
231
+ $post_id = (int) $event->post_id;
232
+
233
+ $event_data = array(
234
+ 'schedule' => $event->event_schedule,
235
+ 'all_day' => $event->event_allday,
236
+ 'schedule_meta' => ('weekly' == $event->event_schedule ? maybe_unserialize($event->event_schedule_meta) : $event->event_schedule_meta),
237
+ 'frequency' => $event->event_frequency,
238
+ 'exclude'=>array(),
239
+ 'include'=>array(),
240
+ );
241
+ $start = new DateTime($event->StartDate.' '.$event->StartTime, eo_get_blog_timezone());
242
+ $end = new DateTime($event->EndDate.' '.$event->FinishTime, eo_get_blog_timezone());
243
+ $schedule_last = new DateTime($event->reoccurrence_end.' '.$event->StartTime, eo_get_blog_timezone());
244
+
245
+ $seconds = round(abs($start->format('U') - $end->format('U')));
246
+ $days = floor($seconds/86400);// 86400 = 60*60*24 seconds in a normal day
247
+ $sec_diff = $seconds - $days*86400;
248
+ $duration_str = '+'.$days.'days '.$sec_diff.' seconds';
249
+ $event_data['duration_str'] =$duration_str;
250
+
251
+ $schedule_last_end = clone $schedule_last;
252
+ $schedule_last_end->modify($duration_str);
253
+
254
+ update_post_meta( $post_id,'_eventorganiser_event_schedule', $event_data);
255
+ update_post_meta( $post_id,'_eventorganiser_schedule_start_start', $start->format('Y-m-d H:i:s')); //Schedule start
256
+ update_post_meta( $post_id,'_eventorganiser_schedule_start_finish', $end->format('Y-m-d H:i:s')); //Schedule start
257
+ update_post_meta( $post_id,'_eventorganiser_schedule_last_start', $schedule_last->format('Y-m-d H:i:s'));//Schedule last
258
+ update_post_meta( $post_id,'_eventorganiser_schedule_last_finish', $schedule_last_end->format('Y-m-d H:i:s'));//Schedule last
259
+
260
+ endforeach;
261
+ endif;
262
+
263
+ }
264
+
265
+ /**
266
+ * Upgrade routine for 1.4
267
+ *
268
+ *@since 1.4
269
+ *@access private
270
+ *@ignore
271
+ */
272
+ function eventorganiser_140_update(){
273
+ //Migrates from Venue table to venue meta table
274
+
275
+ //Run install to create new table:
276
+ eventorganiser_install();
277
+
278
+ global $wpdb;
279
+ $eventorganiser_venue_table = $wpdb->prefix."eo_venues";
280
+
281
+ $venues = eo_get_the_venues();
282
+ $venue_metavalues = $wpdb->get_results(" SELECT venue_slug, venue_address, venue_postal, venue_country, venue_lng, venue_lat, venue_description FROM $eventorganiser_venue_table");
283
+ $fields = array('venue_address'=>'_address','venue_postal'=>'_postcode','venue_country'=>'_country','venue_lng'=>'_lng','venue_lat'=>'_lat','venue_description'=>'_description');
284
+
285
+ foreach( $venue_metavalues as $venue ){
286
+ $term = get_term_by('slug',$venue->venue_slug,'event-venue');
287
+ if( empty($term) || is_wp_error($term) )
288
+ continue;
289
+
290
+ foreach ($fields as $column_name => $meta_key){
291
+ if( ! empty($venue->$column_name) ){
292
+ update_metadata('eo_venue',$term->term_id,$meta_key,$venue->$column_name);
293
+ }
294
+ }
295
+ }
296
+ }
297
+
298
+
299
+ /**
300
+ * Uninstall routine
301
+ *
302
+ *@since 1.0
303
+ *@access private
304
+ *@ignore
305
+ */
306
+ function eventorganiser_uninstall( $is_networkwide = false ){
307
+ global $wpdb;
308
+
309
+ // Is this multisite and did the user click network activate?
310
+ $is_multisite = ( function_exists('is_multisite') && is_multisite() );
311
+
312
+ if ( $is_multisite && $is_networkwide ) {
313
+ // Get the current blog so we can return to it.
314
+ $current_blog_id = get_current_blog_id();
315
+
316
+ // Get a list of all blogs.
317
+ $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
318
+ if( $blog_ids ){
319
+ foreach ( $blog_ids as $blog_id ) {
320
+ switch_to_blog( $blog_id );
321
+ eventorganiser_uninstall_site();
322
+ }
323
+ switch_to_blog( $current_blog_id );
324
+ }else{
325
+ eventorganiser_uninstall_site();
326
+ }
327
+ }else {
328
+ eventorganiser_uninstall_site();
329
+ }
330
+
331
+ }
332
+
333
+ function eventorganiser_uninstall_site(){
334
+ global $wpdb,$eventorganiser_roles, $wp_roles,$wp_taxonomies;
335
+
336
+ eventorganiser_clear_cron_jobs();
337
+ eventorganiser_create_event_taxonomies();
338
+
339
+ //Remove custom taxonomies and terms.
340
+ $taxs = array('event-category','event-venue','event-tag');
341
+ $terms = get_terms($taxs, 'hide_empty=0' );
342
+
343
+ if( $terms ){
344
+ foreach ($terms as $term) {
345
+ $term_id = (int)$term->term_id;
346
+ wp_delete_term($term_id ,$term->taxonomy);
347
+ }
348
+ }
349
+
350
+ //Remove all posts of CPT Event
351
+ //?? $wpdb->query("DELETE FROM $wpdb->posts WHERE post_type = 'event'");
352
+
353
+ //Delete options
354
+ delete_option('eventorganiser_options');
355
+ delete_option('eventorganiser_admin_notices');
356
+ delete_option('eventorganiser_version');
357
+ delete_option('eo_notice');
358
+ delete_option('widget_eo_calendar_widget');
359
+ delete_option('widget_eo_list_widget');
360
+
361
+ //Remove Event Organiser capabilities
362
+ $all_roles = $wp_roles->roles;
363
+ foreach ($all_roles as $role_name => $display_name):
364
+ $role = $wp_roles->get_role($role_name);
365
+ foreach($eventorganiser_roles as $eo_role=>$eo_role_display):
366
+ $role->remove_cap($eo_role);
367
+ endforeach;
368
+ endforeach;
369
+
370
+ eventorganiser_clear_cron_jobs();
371
+
372
+ //Drop tables
373
+ $wpdb->query("DROP TABLE IF EXISTS $wpdb->eo_events");
374
+ $eventorganiser_venue_table = $wpdb->prefix."eo_venues";
375
+ $wpdb->query("DROP TABLE IF EXISTS $eventorganiser_venue_table");
376
+ $wpdb->query("DROP TABLE IF EXISTS $wpdb->eo_venuemeta");
377
+
378
+ //Remove user-meta-data:
379
+ $meta_keys = array('metaboxhidden_event','closedpostboxes_event','wp_event_page_venues_per_page','manageedit-eventcolumnshidden');
380
+ $sql =$wpdb->prepare("DELETE FROM $wpdb->usermeta WHERE ");
381
+ foreach($meta_keys as $key):
382
+ $sql .= $wpdb->prepare("meta_key = %s OR ",$key);
383
+ endforeach;
384
+ $sql.=" 1=0 "; //Deal with final 'OR', must be something false!
385
+ $re =$wpdb->get_results( $sql);
386
+ flush_rewrite_rules();
387
+ }
388
+ ?>
includes/event-organiser-register.php ADDED
@@ -0,0 +1,780 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Register jQuery scripts and CSS files
4
+ * Hooked on to init
5
+ *
6
+ * @since 1.0.0
7
+ * @ignore
8
+ * @access private
9
+ */
10
+ function eventorganiser_register_script() {
11
+ global $wp_locale;
12
+ $version = '2.0.1';
13
+
14
+ $ext = (defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG) ? '' : '.min';
15
+
16
+ /* FullCalendar */
17
+ wp_register_script( 'eo_fullcalendar', EVENT_ORGANISER_URL."js/fullcalendar{$ext}.js",array(
18
+ 'jquery',
19
+ 'jquery-ui-core',
20
+ 'jquery-ui-widget',
21
+ 'jquery-ui-button',
22
+ ),$version,true);
23
+
24
+ /* Google Maps */
25
+ $protocal = is_ssl() ? 'https://' : 'http://';
26
+ if( is_admin() )
27
+ wp_register_script( 'eo_GoogleMap', $protocal.'maps.googleapis.com/maps/api/js?sensor=false&language='.substr(get_locale(),0,2));
28
+ else
29
+ wp_register_script( 'eo_GoogleMap', $protocal.'maps.googleapis.com/maps/api/js?sensor=false&callback=eo_load_map&language='.substr(get_locale(),0,2));
30
+
31
+ /* Front-end script */
32
+ wp_register_script( 'eo_front', EVENT_ORGANISER_URL."js/frontend{$ext}.js",array(
33
+ 'jquery','eo_qtip2',
34
+ 'jquery-ui-core',
35
+ 'jquery-ui-widget',
36
+ 'jquery-ui-button',
37
+ 'jquery-ui-datepicker',
38
+ 'eo_fullcalendar',
39
+ ),$version,true);
40
+
41
+ /* Add js variables to frontend script */
42
+ wp_localize_script( 'eo_front', 'EOAjaxFront', array(
43
+ 'adminajax'=>admin_url( 'admin-ajax.php'),
44
+ 'locale'=>array(
45
+ 'locale' => substr(get_locale(),0,2),
46
+ 'monthNames'=>array_values($wp_locale->month),
47
+ 'monthAbbrev'=>array_values($wp_locale->month_abbrev),
48
+ 'dayNames'=>array_values($wp_locale->weekday),
49
+ 'dayAbbrev'=>array_values($wp_locale->weekday_abbrev),
50
+ 'ShowMore'=>__('Show More','eventorganiser'),
51
+ 'ShowLess'=>__('Show Less','eventorganiser'),
52
+ 'today'=>__('today','eventorganiser'),
53
+ 'day'=>__('day','eventorganiser'),
54
+ 'week'=>__('week','eventorganiser'),
55
+ 'month'=>__('month','eventorganiser'),
56
+ 'gotodate'=>__('go to date','eventorganiser'),
57
+ 'cat'=>__('View all categories','eventorganiser'),
58
+ 'venue'=>__('View all venues','eventorganiser'),
59
+ )
60
+ ));
61
+
62
+ /* WP-JS-Hooks */
63
+ wp_register_script( 'eo-wp-js-hooks', EVENT_ORGANISER_URL."js/event-manager{$ext}.js",array('jquery'),$version,true);
64
+
65
+ /* Q-Tip */
66
+ wp_register_script( 'eo_qtip2', EVENT_ORGANISER_URL.'js/qtip2.js',array('jquery'),$version,true);
67
+
68
+ /* Styles */
69
+ wp_register_style('eo_calendar-style',EVENT_ORGANISER_URL.'css/fullcalendar.css',array(),$version);
70
+ wp_register_style('eo_front',EVENT_ORGANISER_URL.'css/eventorganiser-front-end.css',array(),$version);
71
+ }
72
+ add_action('init', 'eventorganiser_register_script');
73
+
74
+ /**
75
+ *Register jQuery scripts and CSS files for admin
76
+ *
77
+ * @since 1.0.0
78
+ * @ignore
79
+ * @access private
80
+ */
81
+ function eventorganiser_register_scripts(){
82
+ $version = '2.0.1';
83
+ $ext = (defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG) ? '' : '.min';
84
+
85
+ /* Venue scripts for venue & event edit */
86
+ wp_register_script( 'eo_venue', EVENT_ORGANISER_URL."js/venues{$ext}.js",array(
87
+ 'jquery',
88
+ 'eo_GoogleMap'
89
+ ),$version,true);
90
+
91
+ /* Script for event edit page */
92
+ wp_register_script( 'eo_event', EVENT_ORGANISER_URL."js/event{$ext}.js",array(
93
+ 'jquery',
94
+ 'jquery-ui-datepicker',
95
+ 'jquery-ui-autocomplete',
96
+ 'jquery-ui-widget',
97
+ 'jquery-ui-position'
98
+ ),$version,true);
99
+
100
+ /* Script for admin calendar */
101
+ wp_register_script( 'eo_calendar', EVENT_ORGANISER_URL."js/admin-calendar{$ext}.js",array(
102
+ 'eo_fullcalendar',
103
+ 'jquery-ui-dialog',
104
+ 'jquery-ui-tabs',
105
+ 'jquery-ui-position'
106
+ ),$version,true);
107
+
108
+ /* Pick and register jQuery UI style */
109
+ $style = ( 'classic' == get_user_option( 'admin_color') ? 'classic' : 'fresh' );
110
+ wp_register_style('eventorganiser-jquery-ui-style',EVENT_ORGANISER_URL."css/eventorganiser-admin-{$style}.css",array(),$version);
111
+
112
+ /* Admin styling */
113
+ wp_register_style('eventorganiser-style',EVENT_ORGANISER_URL.'css/eventorganiser-admin-style.css',array('eventorganiser-jquery-ui-style'),$version );
114
+
115
+ /* Inline Help */
116
+ wp_register_script( 'eo-inline-help', EVENT_ORGANISER_URL.'js/inline-help.js',array( 'jquery', 'eo_qtip2' ), $version, true );
117
+ }
118
+ add_action( 'admin_init', 'eventorganiser_register_scripts', 5 );
119
+
120
+
121
+ /**
122
+ * The "Comprehensive Google Map Plugin" plug-in deregisters all other Google scripts registered
123
+ * by other plug-ins causing these plug-ins not to function. This plug-in removes that behaviour.
124
+ *
125
+ * Of course if two google scripts are loaded there may be problems, but this is better than always having
126
+ * experiencing a 'bug'. At time writing the function responsible `cgmp_google_map_deregister_scripts()`
127
+ * can be found here {@see https://github.com/azagniotov/Comprehensive-Google-Map-Plugin/blob/master/functions.php#L520 }
128
+ *
129
+ * @see https://github.com/stephenharris/Event-Organiser/issues/49
130
+ * @see http://wordpress.org/support/topic/googlemap-doesnt-shown-on-event-detail-page
131
+ * @since 1.7.4
132
+ * @ignore
133
+ * @access private
134
+ */
135
+ function eventorganiser_cgmp_workaround(){
136
+ remove_action( 'wp_head', 'cgmp_google_map_deregister_scripts', 200 );
137
+ }
138
+ add_action( 'wp_head', 'eventorganiser_cgmp_workaround', 1 );
139
+
140
+ /**
141
+ * Check the export and event creation (from Calendar view) actions.
142
+ * These cannot be called later. Most other actions are only called when
143
+ * the appropriate page is loading.
144
+ *
145
+ * @since 1.0.0
146
+ * @ignore
147
+ * @access private
148
+ */
149
+ function eventorganiser_admin_init(){
150
+ global $EO_Errors;
151
+ $EO_Errors = new WP_Error();
152
+ }
153
+ add_action('admin_init','eventorganiser_admin_init',0);
154
+ add_action('admin_init', array('Event_Organiser_Im_Export', 'get_object'));
155
+
156
+ /**
157
+ * @since 1.0.0
158
+ * @ignore
159
+ * @access private
160
+ */
161
+ function eventorganiser_public_export(){
162
+ if( eventorganiser_get_option('feed') ){
163
+ add_feed('eo-events', array('Event_Organiser_Im_Export','get_object'));
164
+ }
165
+ }
166
+ add_action('init','eventorganiser_public_export');
167
+
168
+ /**
169
+ * Queues up the javascript / style scripts for Events custom page type
170
+ * Hooked onto admin_enqueue_scripts
171
+ *
172
+ * @since 1.0.0
173
+ * @ignore
174
+ * @access private
175
+ */
176
+ function eventorganiser_add_admin_scripts( $hook ) {
177
+ global $post,$current_screen,$wp_locale;
178
+
179
+ if ( $hook == 'post-new.php' || $hook == 'post.php') {
180
+ if( $post->post_type == 'event' ) {
181
+
182
+ wp_enqueue_script('eo_event');
183
+ wp_localize_script( 'eo_event', 'EO_Ajax_Event', array(
184
+ 'ajaxurl' => admin_url( 'admin-ajax.php' ),
185
+ 'startday'=>intval(get_option('start_of_week')),
186
+ 'format'=> eventorganiser_php2jquerydate( eventorganiser_get_option('dateformat') ),
187
+ 'current_user_can' => array(
188
+ 'manage_venues' => current_user_can( 'manage_venues' ),
189
+ ),
190
+ 'location'=>get_option('timezone_string'),
191
+ 'locale'=>array(
192
+ 'monthNames'=>array_values($wp_locale->month),
193
+ 'monthAbbrev'=>array_values($wp_locale->month_abbrev),
194
+ 'dayAbbrev'=>array_values($wp_locale->weekday_abbrev),
195
+ 'showDates' => __( 'Show dates', 'eventorganiser' ),
196
+ 'hideDates' => __( 'Hide dates', 'eventorganiser' ),
197
+ 'weekDay'=>$wp_locale->weekday,
198
+ 'hour'=>__('Hour','eventorganiser'),
199
+ 'minute'=>__('Minute','eventorganiser'),
200
+ 'day'=>__('day','eventorganiser'),
201
+ 'days'=>__('days','eventorganiser'),
202
+ 'week'=>__('week','eventorganiser'),
203
+ 'weeks'=>__('weeks','eventorganiser'),
204
+ 'month'=>__('month','eventorganiser'),
205
+ 'months'=>__('months','eventorganiser'),
206
+ 'year'=>__('year','eventorganiser'),
207
+ 'years'=>__('years','eventorganiser'),
208
+ 'daySingle'=>__('every day','eventorganiser'),
209
+ 'dayPlural'=>__('every %d days','eventorganiser'),
210
+ 'weekSingle'=>__('every week on','eventorganiser'),
211
+ 'weekPlural'=>__('every %d weeks on','eventorganiser'),
212
+ 'monthSingle'=>__('every month on the','eventorganiser'),
213
+ 'monthPlural'=>__('every %d months on the','eventorganiser'),
214
+ 'yearSingle'=>__('every year on the','eventorganiser'),
215
+ 'yearPlural'=>__('every %d years on the','eventorganiser'),
216
+ 'summary'=>__('This event will repeat','eventorganiser'),
217
+ 'until'=>__('until','eventorganiser'),
218
+ 'occurrence'=>array(__('first','eventorganiser'),__('second','eventorganiser'),__('third','eventorganiser'),__('fourth','eventorganiser'),__('last','eventorganiser'))
219
+ )
220
+ ));
221
+ wp_enqueue_script('eo_venue');
222
+ wp_enqueue_style('eventorganiser-style');
223
+ }
224
+ }elseif($current_screen->id=='edit-event'){
225
+ wp_enqueue_style('eventorganiser-style');
226
+ }
227
+ }
228
+ add_action( 'admin_enqueue_scripts', 'eventorganiser_add_admin_scripts', 998, 1 );
229
+
230
+
231
+ /**
232
+ * Perform database and WP version checks. Display appropriate error messages.
233
+ * Triggered on update.
234
+ *
235
+ * @since 1.4.0
236
+ * @ignore
237
+ * @access private
238
+ */
239
+ function eventorganiser_db_checks(){
240
+ global $wpdb;
241
+
242
+ //Check tables exist
243
+ $table_errors = array();
244
+ if($wpdb->get_var("show tables like '$wpdb->eo_events'") != $wpdb->eo_events):
245
+ $table_errors[]= $wpdb->eo_events;
246
+ endif;
247
+ if($wpdb->get_var("show tables like '$wpdb->eo_venuemeta'") != $wpdb->eo_venuemeta):
248
+ $table_errors[]=$wpdb->eo_venuemeta;
249
+ endif;
250
+
251
+ if(!empty($table_errors)):?>
252
+ <div class="error" >
253
+ <p>There has been an error with Event Organiser. One or more tables are missing:</p>
254
+ <ul>
255
+ <?php foreach($table_errors as $table):
256
+ echo "<li>".$table."</li>";
257
+ endforeach; ?>
258
+ </ul>
259
+ <p>Please try re-installing the plugin.</p>
260
+ </div>
261
+ <?php endif;
262
+
263
+ //Check WordPress version
264
+ if(get_bloginfo('version')<'3.3'):?>
265
+ <div class="error" >
266
+ <p>Event Organiser requires <strong>WordPress 3.3</strong> to function properly. Your version is <?php echo get_bloginfo('version'); ?>. </p>
267
+ </div>
268
+ <?php endif;
269
+ }
270
+
271
+
272
+ /**
273
+ * Displays any errors or notices in the global $EO_Errors
274
+ *
275
+ * @since 1.4.0
276
+ * @ignore
277
+ * @access private
278
+ */
279
+ function eventorganiser_admin_notices(){
280
+ global $EO_Errors;
281
+ $errors=array();
282
+ $notices=array();
283
+ if(isset($EO_Errors)):
284
+ $errors = $EO_Errors->get_error_messages('eo_error');
285
+ $notices= $EO_Errors->get_error_messages('eo_notice');
286
+ if(!empty($errors)):?>
287
+ <div class="error" >
288
+ <?php foreach ($errors as $error):?>
289
+ <p><?php echo $error;?></p>
290
+ <?php endforeach;?>
291
+ </div>
292
+ <?php endif;?>
293
+ <?php if(!empty($notices)):?>
294
+ <div class="updated">
295
+ <?php foreach ($notices as $notice):?>
296
+ <p><?php echo $notice;?></p>
297
+ <?php endforeach;?>
298
+ </div>
299
+ <?php endif;
300
+ endif;
301
+ }
302
+ add_action('admin_notices','eventorganiser_admin_notices');
303
+
304
+
305
+ /**
306
+ * Adds link to the plug-in settings on the settings page
307
+ *
308
+ * @since 1.5
309
+ * @ignore
310
+ * @access private
311
+ */
312
+ function eventorganiser_plugin_settings_link($links, $file) {
313
+
314
+ if( $file == 'event-organiser/event-organiser.php' ) {
315
+ /* Insert the link at the end*/
316
+ $links['settings'] = sprintf('<a href="%s"> %s </a>',
317
+ admin_url('options-general.php?page=event-settings'),
318
+ __('Settings','eventorganiser')
319
+ );
320
+ }
321
+ return $links;
322
+ }
323
+ add_filter('plugin_action_links', 'eventorganiser_plugin_settings_link', 10, 2);
324
+
325
+ /**
326
+ * Schedules cron job for automatically deleting expired events
327
+ *
328
+ * @since 1.4.0
329
+ * @ignore
330
+ * @access private
331
+ */
332
+ function eventorganiser_cron_jobs(){
333
+ wp_schedule_event(time()+60, 'daily', 'eventorganiser_delete_expired');
334
+ }
335
+
336
+
337
+ /**
338
+ * Clears the 'delete expired events' cron job.
339
+ *
340
+ * @since 1.4.0
341
+ * @ignore
342
+ * @access private
343
+ */
344
+ function eventorganiser_clear_cron_jobs(){
345
+ wp_clear_scheduled_hook('eventorganiser_delete_expired');
346
+ }
347
+
348
+ /**
349
+ * Returns the time in seconds until a specified cron job is scheduled.
350
+ *
351
+ *@since 1.8
352
+ *@see http://wordpress.stackexchange.com/questions/83270/when-does-next-cron-job-run-time-from-now/83279#83279
353
+ *
354
+ *@param string $cron_name The name of the cron job
355
+ *@return int|bool The time in seconds until the cron job is scheduled. False if
356
+ *it could not be found.
357
+ */
358
+ function eventorganiser_get_next_cron_time( $cron_name ){
359
+ if( $timestamp = wp_next_scheduled( $cron_name ) ){
360
+ $timestamp = $timestamp - time();
361
+ }
362
+ return $timestamp;
363
+ }
364
+
365
+
366
+ /**
367
+ * Callback for the delete expired events cron job. Deletes events that finished at least 24 hours ago.
368
+ * For recurring events it is only deleted once the last occurrence has expired.
369
+ *
370
+ * @since 1.4.0
371
+ * @ignore
372
+ * @access private
373
+ */
374
+ function eventorganiser_delete_expired_events(){
375
+ //Get expired events
376
+ $events = eo_get_events(array('showrepeats'=>0,'showpastevents'=>1,'eo_interval'=>'expired'));
377
+
378
+ $time_until_expired = (int) apply_filters( 'eventorganiser_events_expire_time', 24*60*60 );
379
+ $time_until_expired = max( $time_until_expired, 0 );
380
+
381
+ if($events):
382
+ $now = new DateTime('now', eo_get_blog_timezone());
383
+
384
+ foreach($events as $event):
385
+
386
+ $start = eo_get_the_start( DATETIMEOBJ, $event->ID, null, $event->occurrence_id );
387
+ $end = eo_get_the_end( DATETIMEOBJ, $event->ID, null, $event->occurrence_id );
388
+
389
+ $expired = round(abs($end->format('U')-$start->format('U'))) + $time_until_expired; //Duration + expire time
390
+
391
+ $finished = eo_get_schedule_last( DATETIMEOBJ, $event->ID );
392
+ $finished->modify("+$expired seconds");//[Expired time] after the last occurrence finishes
393
+
394
+ //Delete if [expired time] has passed
395
+ if( $finished <= $now ){
396
+ wp_trash_post((int) $event->ID);
397
+ }
398
+
399
+ endforeach;
400
+ endif;
401
+ }
402
+ add_action('eventorganiser_delete_expired', 'eventorganiser_delete_expired_events');
403
+
404
+
405
+
406
+ /**
407
+ * Adds retina support for the screen icon
408
+ * Thanks to numeeja (http://cubecolour.co.uk/)
409
+ *
410
+ * @since 1.5.0
411
+ * @ignore
412
+ * @access private
413
+ */
414
+ function eventorganiser_screen_retina_icon(){
415
+
416
+ $screen_id = get_current_screen()->id;
417
+
418
+ if( !in_array($screen_id, array('event','edit-event','edit-event-tag','edit-event-category','event_page_venues','event_page_calendar')) )
419
+ return;
420
+
421
+ $icon_url = EVENT_ORGANISER_URL.'css/images/eoicon-64.png'
422
+ ?>
423
+ <style>
424
+ @media screen and (-webkit-min-device-pixel-ratio: 2) {
425
+ #icon-edit.icon32 {
426
+ background: url(<?php echo $icon_url;?>) no-repeat;
427
+ background-size: 32px 32px;
428
+ height: 32px;
429
+ width: 32px;
430
+ }
431
+ }
432
+ </style>
433
+ <?php
434
+ }
435
+ add_action('admin_print_styles','eventorganiser_screen_retina_icon');
436
+
437
+
438
+ /**
439
+ * Purge the occurrences cache
440
+ * Hooked onto eventorganiser_save_event and eventorganiser_delete_event
441
+ *
442
+ *@access private
443
+ * @ignore
444
+ *@since 1.6
445
+ */
446
+ function _eventorganiser_delete_occurrences_cache($post_id=0){
447
+ wp_cache_delete( 'eventorganiser_occurrences_'.$post_id );
448
+ }
449
+ //The following need to trigger the cache clear clearly need to trigger a cache clear
450
+ $hooks = array('eventorganiser_save_event', 'eventorganiser_delete_event');
451
+ foreach( $hooks as $hook ){
452
+ add_action($hook, '_eventorganiser_delete_occurrences_cache');
453
+ }
454
+
455
+
456
+ /**
457
+ * Purge the cached results of get_calendar.
458
+ * Hooked onto eventorganiser_save_event, eventorganiser_delete_event, wp_trash_post,
459
+ * update_option_gmt_offset,update_option_start_of_week,update_option_rewrite_rules
460
+ * and edited_event-category.
461
+ *
462
+ *@access private
463
+ * @ignore
464
+ *@since 1.5
465
+ */
466
+ function _eventorganiser_delete_calendar_cache() {
467
+ delete_transient( 'eo_widget_calendar' );
468
+ delete_transient('eo_full_calendar_public');
469
+ delete_transient('eo_full_calendar_admin');
470
+ delete_transient('eo_widget_agenda');
471
+ }
472
+
473
+ //The following need to trigger the cache
474
+ $hooks = array(
475
+ 'eventorganiser_save_event', 'eventorganiser_delete_event', 'wp_trash_post','update_option_gmt_offset', /* obvious */
476
+ 'update_option_start_of_week', /* Start of week is used for calendars */
477
+ 'update_option_rewrite_rules', /* If permalinks updated - links on fullcalendar might now be invalid */
478
+ 'delete_option_rewrite_rules',
479
+ 'update_option_siteurl',
480
+ 'update_option_home',
481
+ 'edited_event-category', /* Colours of events may change */
482
+ );
483
+ foreach( $hooks as $hook ){
484
+ add_action($hook, '_eventorganiser_delete_calendar_cache');
485
+ }
486
+
487
+
488
+ /**
489
+ * Handles admin pointers
490
+ *
491
+ * Kick starts the enquing. Rename this to something unique (i.e. include your plugin/theme name).
492
+ *
493
+ *@access private
494
+ *@ignore
495
+ *@since 1.5
496
+ */
497
+ function eventorganiser_pointer_load( $hook_suffix ) {
498
+
499
+ $screen_id = get_current_screen()->id;
500
+
501
+ //Get pointers for this screen
502
+ $pointers = apply_filters('eventorganiser_admin_pointers-'.$screen_id, array());
503
+
504
+ if( !$pointers || !is_array($pointers) )
505
+ return;
506
+
507
+ // Get dismissed pointers
508
+ $dismissed = explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) );
509
+ $valid_pointers =array();
510
+
511
+ //Check pointers and remove dismissed ones.
512
+ foreach ($pointers as $pointer_id => $pointer ){
513
+
514
+ //Sanity check
515
+ if ( in_array( $pointer_id, $dismissed ) || empty($pointer) || empty( $pointer_id ) || empty( $pointer['target'] ) || empty( $pointer['options'] ) )
516
+ continue;
517
+
518
+ $pointer['pointer_id'] = $pointer_id;
519
+
520
+ //Add the pointer to $valid_pointers array
521
+ $valid_pointers['pointers'][] = $pointer;
522
+ }
523
+
524
+ //No valid pointers? Stop here.
525
+ if( empty($valid_pointers) )
526
+ return;
527
+
528
+ // Add pointers style to queue.
529
+ wp_enqueue_style( 'wp-pointer' );
530
+
531
+ // Add pointers script to queue. Add custom script.
532
+ wp_enqueue_script('eventorganiser-pointer',EVENT_ORGANISER_URL.'js/eventorganiser-pointer.js',array('wp-pointer','eo_event'));
533
+
534
+ // Add pointer options to script.
535
+ wp_localize_script('eventorganiser-pointer', 'eventorganiserPointer', $valid_pointers);
536
+ }
537
+ add_action( 'admin_enqueue_scripts', 'eventorganiser_pointer_load',99999);
538
+
539
+
540
+ /**
541
+ * Handles admin notices
542
+ *
543
+ * This is a class which automates (semi-permant) admin notices. This are notices which persist until an
544
+ * action is performed or they are manually dismissed by the user. The EO_Admin_Notice_Handle::admin_notice()
545
+ * generates the mark-up for the notices, and displays them on the appropriate screen. It also triggers printing
546
+ * javascript so that notices can be dismissed via AJAX. There is also a no-js fallback.
547
+ *
548
+ *@access private
549
+ *@ignore
550
+ *@since 1.7
551
+ */
552
+ class EO_Admin_Notice_Handler{
553
+
554
+ static $prefix = 'eventorganiser';
555
+
556
+ /**
557
+ * Hooks the dismiss listener (ajax and no-js) and maybe shows notices on admin_notices
558
+ */
559
+ static function load(){
560
+ add_action( 'admin_notices', array(__CLASS__,'admin_notice'));
561
+ add_action( 'admin_init',array(__CLASS__, 'dismiss_handler'));
562
+ add_action( 'wp_ajax_'.self::$prefix.'-dismiss-notice', array( __CLASS__, 'dismiss_handler' ) );
563
+ }
564
+
565
+ /**
566
+ * Print appropriate notices.
567
+ * Hooks EO_Admin_Notice_Handle::print_footer_scripts to admin_print_footer_scripts to
568
+ * print js to handle AJAX dismiss.
569
+ */
570
+ static function admin_notice(){
571
+
572
+ $screen_id = get_current_screen()->id;
573
+
574
+ //Notices of the form ID=> array('screen_id'=>screen ID, 'message' => Message,'type'=>error|alert)
575
+ $notices = array(
576
+ 'autofillvenue17'=>array(
577
+ 'screen_id'=>'event_page_venues',
578
+ 'message' => sprintf(__("<h4>City & State Fields Added</h4>City and state / province fields for venues have now been added. </br> If you'd like, Event Organiser can <a href='%s'>attempt to auto-fill them</a>. You can always manually change the details aftewards.",'eventorganiser'),
579
+ add_query_arg('action','eo-autofillcity',admin_url('admin-post.php'))
580
+ ),
581
+ 'type' => 'alert'
582
+ ),
583
+ 'changedtemplate17'=>array(
584
+ 'screen_id'=>'',
585
+ 'message' => __("<h4>The Default Templates Have Changed</h4>Don't panic! If you've set up your own templates in your theme you won't notice any change. </br> If you haven't and want the old templates back, <a href='http://wp-event-organiser.com/blog/new-default-templates-in-1-7'>see this post<a/>.",'eventorganiser'),
586
+ 'type' => 'alert'
587
+ ),
588
+ );
589
+
590
+ if( !$notices )
591
+ return;
592
+
593
+ $seen_notices = get_option(self::$prefix.'_admin_notices',array());
594
+
595
+ foreach( $notices as $id => $notice ){
596
+ $id = sanitize_key($id);
597
+
598
+ //Notices cannot have been dismissed and must have a message
599
+ if( in_array($id, $seen_notices) || empty($notice['message']) )
600
+ continue;
601
+
602
+ $notice_screen_id = (array) $notice['screen_id'];
603
+ $notice_screen_id = array_filter($notice_screen_id);
604
+
605
+ //Notices must for this screen. If empty, its for all screens.
606
+ if( !empty($notice_screen_id) && !in_array($screen_id, $notice_screen_id) )
607
+ continue;
608
+
609
+ $class = $notice['type'] == 'error' ? 'error' : 'updated';
610
+
611
+ printf("<div class='%s-notice {$class}' id='%s'>%s<p> <a class='%s' href='%s' title='%s'><strong>%s</strong></a></p></div>",
612
+ esc_attr(self::$prefix),
613
+ esc_attr(self::$prefix.'-notice-'.$id),
614
+ $notice['message'],
615
+ esc_attr(self::$prefix.'-dismiss'),
616
+ esc_url(add_query_arg(array(
617
+ 'action'=>self::$prefix.'-dismiss-notice',
618
+ 'notice'=>$id,
619
+ '_wpnonce'=>wp_create_nonce(self::$prefix.'-dismiss-'.$id),
620
+ ))),
621
+ __('Dismiss this notice','eventorganiser'),
622
+ __('Dismiss','eventorganiser')
623
+ );
624
+ }
625
+ add_action( 'admin_print_footer_scripts', array( __CLASS__, 'print_footer_scripts' ), 11 );
626
+
627
+ }
628
+
629
+ /**
630
+ * Handles AJAX and no-js requests to dismiss a notice
631
+ */
632
+ static function dismiss_handler(){
633
+
634
+ $notice = isset($_REQUEST['notice']) ? $_REQUEST['notice'] : false;
635
+ if( empty($notice) )
636
+ return;
637
+
638
+ if ( defined('DOING_AJAX') && DOING_AJAX ){
639
+ //Ajax dismiss handler
640
+ if( empty($_REQUEST['notice']) || empty($_REQUEST['_wpnonce']) || $_REQUEST['action'] !== self::$prefix.'-dismiss-notice' )
641
+ return;
642
+
643
+ if( !wp_verify_nonce( $_REQUEST['_wpnonce'],self::$prefix."-ajax-dismiss") )
644
+ return;
645
+
646
+ }else{
647
+ //Fallback dismiss handler
648
+ if( empty($_REQUEST['action']) || empty($_REQUEST['notice']) || empty($_REQUEST['_wpnonce']) || $_REQUEST['action'] !== self::$prefix.'-dismiss-notice' )
649
+ return;
650
+
651
+ if( !wp_verify_nonce( $_REQUEST['_wpnonce'],self::$prefix.'-dismiss-'.$notice ) )
652
+ return;
653
+ }
654
+
655
+ self::dismiss_notice($notice);
656
+
657
+ if ( defined('DOING_AJAX') && DOING_AJAX )
658
+ wp_die(1);
659
+ }
660
+
661
+ /**
662
+ * Dismiss a given a notice
663
+ *@param string $notice The notice (ID) to dismiss
664
+ */
665
+ static function dismiss_notice($notice){
666
+ $seen_notices = get_option(self::$prefix.'_admin_notices',array());
667
+ $seen_notices[] = $notice;
668
+ $seen_notices = array_unique($seen_notices);
669
+ update_option(self::$prefix.'_admin_notices',$seen_notices);
670
+ }
671
+
672
+ /**
673
+ * Prints javascript in footer to handle AJAX dismiss.
674
+ */
675
+ static function print_footer_scripts() {
676
+ ?>
677
+ <script type="text/javascript">
678
+ jQuery(document).ready(function ($){
679
+ var dismissClass = '<?php echo esc_js(self::$prefix."-dismiss");?>';
680
+ var ajaxaction = '<?php echo esc_js(self::$prefix."-dismiss-notice"); ?>';
681
+ var _wpnonce = '<?php echo wp_create_nonce(self::$prefix."-ajax-dismiss")?>';
682
+ var noticeClass = '<?php echo esc_js(self::$prefix."-notice");?>';
683
+
684
+ jQuery('.'+dismissClass).click(function(e){
685
+ e.preventDefault();
686
+ var noticeID= $(this).parents('.'+noticeClass).attr('id').substring(noticeClass.length+1);
687
+
688
+ $.post(ajaxurl, {
689
+ action: ajaxaction,
690
+ notice: noticeID,
691
+ _wpnonce: _wpnonce
692
+ }, function (response) {
693
+ if ('1' === response) {
694
+ $('#'+noticeClass+'-'+noticeID).fadeOut('slow');
695
+ } else {
696
+ $('#'+noticeClass+'-'+noticeID).removeClass('updated').addClass('error');
697
+ }
698
+ });
699
+ });
700
+ });
701
+ </script><?php
702
+ }
703
+ }//End EO_Admin_Notice_Handler
704
+ EO_Admin_Notice_Handler::load();
705
+
706
+
707
+ /**
708
+ * Handles city auto-fill request.
709
+ *
710
+ * Hooked onto admin_post_eo-autofillcity. Triggered when user clicks 'autofill' link.
711
+ * This routine goes through all the venues, reverse geocodes to find their city and
712
+ * autofills the city field (added in 1.7).
713
+ *
714
+ *@ignore
715
+ *@access private
716
+ *@link https://github.com/stephenh1988/Event-Organiser/issues/18
717
+ *@link http://open.mapquestapi.com/nominatim/ Nominatim Search Service
718
+ */
719
+ function _eventorganiser_autofill_city(){
720
+ $seen_notices = get_option('eventorganiser_admin_notices',array());
721
+
722
+ if( in_array('autofillvenue17', $seen_notices) )
723
+ return;
724
+
725
+ EO_Admin_Notice_Handler::dismiss_notice('autofillvenue17');
726
+
727
+ $cities =array();
728
+ $venues = eo_get_venues();
729
+
730
+ foreach( $venues as $venue ){
731
+ $venue_id = (int) $venue->term_id;
732
+ $latlng =extract(eo_get_venue_latlng($venue_id));
733
+
734
+ If( eo_get_venue_meta($venue_id,'_city',true) )
735
+ continue;
736
+
737
+ $response=wp_remote_get("http://open.mapquestapi.com/nominatim/v1/reverse?format=json&lat={$lat}&lon={$lng}&osm_type=N&limit=1");
738
+ $geo = json_decode(wp_remote_retrieve_body( $response ));
739
+ if( isset($geo->address->city) ){
740
+ $cities[$venue_id] = $geo->address->city;
741
+ eo_update_venue_meta($venue_id, '_city', $geo->address->city);
742
+ }
743
+ if( isset($geo->address->country_code) && 'gb' == $geo->address->country_code ){
744
+ //For the UK use county not state.
745
+ if( isset($geo->address->county) ){
746
+ $cities[$venue_id] = $geo->address->county;
747
+ eo_update_venue_meta($venue_id, '_state', $geo->address->county);
748
+ }
749
+ }else{
750
+ if( isset($geo->address->state) ){
751
+ $cities[$venue_id] = $geo->address->state;
752
+ eo_update_venue_meta($venue_id, '_state', $geo->address->state);
753
+ }
754
+ }
755
+ }
756
+
757
+ wp_safe_redirect(admin_url('edit.php?post_type=event&page=venues'));
758
+ }
759
+ add_action('admin_post_eo-autofillcity','_eventorganiser_autofill_city');
760
+
761
+ function _eventorganiser_theme_check_results(){
762
+ delete_option( 'eo_wp_footer_present' );
763
+ delete_option( 'eo_sidebar_correct' );
764
+ }
765
+ add_action( 'switch_theme', '_eventorganiser_theme_check_results' );
766
+
767
+ function _eventorganiser_check_sidebars( $sidebar ){
768
+ $before_widget = $sidebar['before_widget'];
769
+
770
+ if( did_action( 'register_sidebar') > 1 && ( -1 == get_option( 'eo_sidebar_correct' ) ) )
771
+ return;
772
+
773
+ if( strpos( $before_widget, '%1$s' ) == false || strpos( $before_widget, '%2$s' ) == false ){
774
+ update_option( 'eo_sidebar_correct', -1 );
775
+ }else{
776
+ update_option( 'eo_sidebar_correct', 1 );
777
+ }
778
+ }
779
+
780
+ ?>
includes/event-organiser-templates.php ADDED
@@ -0,0 +1,269 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template functions
4
+ *
5
+ * @package template-functions
6
+ */
7
+
8
+
9
+ /**
10
+ * Load a template part into a template
11
+ *
12
+ * Identical to {@see `get_template_part()`} except that it uses {@see `eo_locate_template()`}
13
+ * instead of {@see `locate_template()`}.
14
+ *
15
+ * Makes it easy for a theme to reuse sections of code in a easy to overload way
16
+ * for child themes. Looks for and includes templates {$slug}-{$name}.php
17
+ *
18
+ * You may include the same template part multiple times.
19
+ *
20
+ * @uses eo_locate_template()
21
+ * @since 1.7
22
+ * @uses do_action() Calls `get_template_part_{$slug}` action.
23
+ *
24
+ * @param string $slug The slug name for the generic template.
25
+ * @param string $name The name of the specialised template.
26
+ */
27
+ function eo_get_template_part( $slug, $name = null ) {
28
+ do_action( "get_template_part_{$slug}", $slug, $name );
29
+
30
+ $templates = array();
31
+ if ( isset($name) )
32
+ $templates[] = "{$slug}-{$name}.php";
33
+
34
+ $templates[] = "{$slug}.php";
35
+
36
+ eo_locate_template($templates, true, false);
37
+ }
38
+
39
+
40
+ /**
41
+ * Retrieve the name of the highest priority template file that exists.
42
+ *
43
+ * Searches the child theme first, then the parent theme before checking the plug-in templates folder.
44
+ * So parent themes can override the default plug-in templates, and child themes can over-ride both.
45
+ *
46
+ * Behaves almost identically to {@see locate_template()}
47
+ *
48
+ * @since 1.7
49
+ *
50
+ * @param string|array $template_names Template file(s) to search for, in order.
51
+ * @param bool $load If true the template file will be loaded if it is found.
52
+ * @param bool $require_once Whether to require_once or require. Default true. Has no effect if $load is false.
53
+ * @return string The template filename if one is located.
54
+ */
55
+ function eo_locate_template($template_names, $load = false, $require_once = true ) {
56
+ $located = '';
57
+
58
+ $template_dir = get_stylesheet_directory(); //child theme
59
+ $parent_template_dir = get_template_directory(); //parent theme
60
+
61
+ $stack = apply_filters( 'eventorganiser_template_stack', array( $template_dir, $parent_template_dir, EVENT_ORGANISER_DIR . 'templates' ) );
62
+
63
+ foreach ( (array) $template_names as $template_name ) {
64
+ if ( !$template_name )
65
+ continue;
66
+ foreach ( $stack as $template_stack ){
67
+ if ( file_exists( trailingslashit( $template_stack ) . $template_name ) ) {
68
+ $located = trailingslashit( $template_stack ) . $template_name;
69
+ break;
70
+ }
71
+ }
72
+ }
73
+
74
+ if ( $load && '' != $located )
75
+ load_template( $located, $require_once );
76
+
77
+ return $located;
78
+ }
79
+
80
+ /**
81
+ * Whether an event archive is being viewed
82
+ *
83
+ * My specifying the type of archive ( e.g. 'day', 'month' or 'year'), we can check if its
84
+ * a day, month or year archive. By default, it will just return `is_post_type_archive('event')`
85
+ *
86
+ * You can get the date of the archive via {@see `eo_get_event_archive_date()`}
87
+ *
88
+ *@uses is_post_type_archive()
89
+ *@since 1.7
90
+ *@param string $type The type archive. 'day', 'month', or 'year'
91
+ *@return bool Whether an event archive is being viewed, where type is specified, if its an event archive of that type.
92
+ */
93
+ function eo_is_event_archive( $type = false ){
94
+
95
+ if( !is_post_type_archive('event') )
96
+ return false;
97
+
98
+ $ondate = str_replace('/','-',get_query_var('ondate'));
99
+
100
+ switch( $type ){
101
+ case 'year':
102
+ if( _eventorganiser_check_datetime( $ondate.'-01-01 00:00', 'Y-m-d' ) )
103
+ return true;
104
+ return false;
105
+
106
+ case 'month':
107
+ if( _eventorganiser_check_datetime( $ondate.'-01 00:00', 'Y-m-d' ) )
108
+ return true;
109
+ return false;
110
+
111
+ case 'day':
112
+ if( _eventorganiser_check_datetime( $ondate.' 00:00', 'Y-m-d') )
113
+ return true;
114
+ return false;
115
+
116
+ default:
117
+ return true;
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Returns formatted date of an event archive.
123
+ *
124
+ * Returns the formatted ate of an event archive. E.g. for date archives, returns that date,
125
+ * for year archives returns 1st January of that year, for month archives 1st of that month.
126
+ * The date is formatted according to `$format` via {@see `eo_format_datetime()`}
127
+ *
128
+ * @since 1.7
129
+ * @uses is_post_type_archive()
130
+ * @uses eo_format_datetime()
131
+ * @link http://php.net/manual/en/function.date.php Formatting dates
132
+ * @param string|constant $format How to format the date, see http://php.net/manual/en/function.date.php or DATETIMEOBJ constant to return the datetime object.
133
+ * @return string|dateTime The formatted date
134
+ */
135
+ function eo_get_event_archive_date( $format = DATETIMEOBJ ){
136
+
137
+ if( !is_post_type_archive('event') )
138
+ return false;
139
+
140
+ $ondate = str_replace('/','-',get_query_var('ondate'));
141
+ $parts = count(explode('-',$ondate));
142
+
143
+ if( $parts == 1 && is_numeric($ondate) ){
144
+ //Numeric - interpret as year
145
+ $ondate .= '-01-01';
146
+ }elseif( $parts == 2 ){
147
+ // 2012-01 format: interpret as month
148
+ $ondate .= '-01';
149
+ }
150
+
151
+ $ondate = _eventorganiser_check_datetime( $ondate.' 00:00', 'Y-m-d' );
152
+ return eo_format_datetime($ondate, $format);
153
+ }
154
+
155
+ /**
156
+ * Checks if provided template path points to an 'event' template recognised by EO, given the context.
157
+ * This will one day ignore context, and if only the event archive template is present in theme folder
158
+ * it will use that regardless. If no event-archive tempate is present the plug-in will pick the most appropriate
159
+ * option, first from the theme/child-theme directory then the plugin.
160
+ *
161
+ * @ignore
162
+ * @since 1.3.1
163
+ *
164
+ * @param string $templatePath absolute path to template or filename (with .php extension)
165
+ * @param string $context What the template is for ('event','archive-event','event-venue', etc).
166
+ * @return (true|false) return true if template is recognised as an 'event' template. False otherwise.
167
+ */
168
+ function eventorganiser_is_event_template($templatePath,$context=''){
169
+
170
+ $template = basename($templatePath);
171
+
172
+ switch($context):
173
+ case 'event';
174
+ return $template == 'single-event.php';
175
+
176
+ case 'archive':
177
+ return $template == 'archive-event.php';
178
+
179
+ case 'event-venue':
180
+ if((1 == preg_match('/^taxonomy-event-venue((-(\S*))?).php/',$template) || $template == 'venues-template.php'))
181
+ return true;
182
+ return false;
183
+
184
+ case 'event-category':
185
+ return (1 == preg_match('/^taxonomy-event-category((-(\S*))?).php/',$template));
186
+
187
+ case 'event-tag':
188
+ return (1 == preg_match('/^taxonomy-event-tag((-(\S*))?).php/',$template));
189
+ endswitch;
190
+
191
+ return false;
192
+ }
193
+
194
+ /**
195
+ * Checks to see if appropriate templates are present in active template directory.
196
+ * Otherwises uses templates present in plugin's template directory.
197
+ * Hooked onto template_include'
198
+ *
199
+ * @ignore
200
+ * @since 1.0.0
201
+ * @param string $template Absolute path to template
202
+ * @return string Absolute path to template
203
+ */
204
+ function eventorganiser_set_template( $template ){
205
+
206
+ //Has EO template handling been turned off?
207
+ if( !eventorganiser_get_option('templates') )
208
+ return $template;
209
+
210
+ //If WordPress couldn't find an 'event' template use plug-in instead:
211
+
212
+ if( is_post_type_archive('event') && !eventorganiser_is_event_template($template,'archive'))
213
+ $template = EVENT_ORGANISER_DIR.'templates/archive-event.php';
214
+
215
+ if( ( is_tax('event-venue') || eo_is_venue() ) && !eventorganiser_is_event_template($template,'event-venue'))
216
+ $template = EVENT_ORGANISER_DIR.'templates/taxonomy-event-venue.php';
217
+
218
+ if( is_tax('event-category') && !eventorganiser_is_event_template($template,'event-category'))
219
+ $template = EVENT_ORGANISER_DIR.'templates/taxonomy-event-category.php';
220
+
221
+ if( is_tax('event-tag') && eventorganiser_get_option('eventtag') && !eventorganiser_is_event_template($template,'event-tag') )
222
+ $template = EVENT_ORGANISER_DIR.'templates/taxonomy-event-tag.php';
223
+
224
+ /*
225
+ * In view of theme compatibility, if an event template isn't found
226
+ * rather than using our own single-event.php, we use ordinary single.php and
227
+ * add content in via the_content
228
+ */
229
+ if( is_singular('event') && !eventorganiser_is_event_template($template,'event') ){
230
+ //Viewing a single event
231
+
232
+ //Hide next/previous post link
233
+ add_filter("next_post_link",'__return_false');
234
+ add_filter("previous_post_link",'__return_false');
235
+
236
+ //Prepend our event details
237
+ add_filter('the_content','_eventorganiser_single_event_content');
238
+ }
239
+
240
+ return $template;
241
+ }
242
+ add_filter('template_include', 'eventorganiser_set_template');
243
+
244
+
245
+ function _eventorganiser_single_event_content( $content ){
246
+
247
+ //Sanity check!
248
+ if( !is_singular('event') )
249
+ return $content;
250
+
251
+ global $eo_event_parsed;
252
+ if( !empty( $eo_event_parsed[get_the_ID()] ) ){
253
+ return $content;
254
+ }else{
255
+ $eo_event_parsed[get_the_ID()] = 1;
256
+ }
257
+
258
+ //Object buffering
259
+ ob_start();
260
+ eo_get_template_part('event-meta','event-single');
261
+ //include(EVENT_ORGANISER_DIR.'templates/event-meta-event-single.php');
262
+ $event_content = ob_get_contents();
263
+ ob_end_clean();
264
+
265
+ $event_content = apply_filters('eventorganiser_pre_event_content', $event_content, $content);
266
+
267
+ return $event_content.$content;
268
+ }
269
+ ?>
includes/event-organiser-utility-functions.php ADDED
@@ -0,0 +1,910 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Utility functions
4
+ *
5
+ * @package utility-functions
6
+ */
7
+
8
+ /**
9
+ * Formats a datetime object into a specified format and handles translations.
10
+ * Used by
11
+ *
12
+ * * {@see `eo_get_the_start()`}
13
+ * * {@see `eo_get_the_end()`}
14
+ * * {@see `eo_get_schedule_start()`}
15
+ * * {@see `eo_get_schedule_last()`}
16
+ *
17
+ * The constant DATETIMEOBJ can be passed to them to get datetime objects
18
+ * Applies {@see `eventorganiser_format_datetime`} filter
19
+ *
20
+ * @since 1.2.0
21
+ * @link http://php.net/manual/en/function.date.php PHP Date
22
+ *
23
+ * @param dateTime $datetime The datetime to format
24
+ * @param string|constant $format How to format the date, see http://php.net/manual/en/function.date.php or DATETIMEOBJ constant to return the datetime object.
25
+ * @return string|dateTime The formatted date
26
+ */
27
+ function eo_format_datetime($datetime,$format='d-m-Y'){
28
+ global $wp_locale;
29
+
30
+ if( DATETIMEOBJ == $format )
31
+ return $datetime;
32
+
33
+ if ( ( !empty( $wp_locale->month ) ) && ( !empty( $wp_locale->weekday ) ) ) :
34
+ //Translate
35
+ $datemonth = $wp_locale->get_month($datetime->format('m'));
36
+ $datemonth_abbrev = $wp_locale->get_month_abbrev($datemonth);
37
+ $dateweekday = $wp_locale->get_weekday($datetime->format('w'));
38
+ $dateweekday_abbrev = $wp_locale->get_weekday_abbrev( $dateweekday );
39
+ $datemeridiem = trim($wp_locale->get_meridiem($datetime->format('a')));
40
+ $datemeridiem_capital =$wp_locale->get_meridiem($datetime->format('A'));
41
+
42
+ $datemeridiem = (empty($datemeridiem) ? $datetime->format('a') : $datemeridiem);
43
+
44
+ $dateformatstring = ' '.$format;
45
+ $dateformatstring = preg_replace( "/([^\\\])D/", "\\1" . backslashit( $dateweekday_abbrev ), $dateformatstring );
46
+ $dateformatstring = preg_replace( "/([^\\\])F/", "\\1" . backslashit( $datemonth ), $dateformatstring );
47
+ $dateformatstring = preg_replace( "/([^\\\])l/", "\\1" . backslashit( $dateweekday ), $dateformatstring );
48
+ $dateformatstring = preg_replace( "/([^\\\])M/", "\\1" . backslashit( $datemonth_abbrev ), $dateformatstring );
49
+ $dateformatstring = preg_replace( "/([^\\\])a/", "\\1" . backslashit( $datemeridiem ), $dateformatstring );
50
+ $dateformatstring = preg_replace( "/([^\\\])A/", "\\1" . backslashit( $datemeridiem_capital ), $dateformatstring );
51
+ $dateformatstring = substr( $dateformatstring, 1, strlen( $dateformatstring ) -1 );
52
+ endif;
53
+ $formatted_datetime = $datetime->format($dateformatstring);
54
+ return apply_filters('eventorganiser_format_datetime', $formatted_datetime , $format, $datetime);
55
+ }
56
+
57
+ /**
58
+ * Formats a date string in the format 'YYYY-MM-DD H:i:s' format or even
59
+ * relative strings like 'today' into a specified format.
60
+ *
61
+ * @uses eo_format_datetime()
62
+ * @since 1.2.0
63
+ * @link http://php.net/manual/en/function.date.php PHP Date
64
+ *
65
+ * @param string $dateString The date as a string
66
+ * @param string $format How to format the date. DATETIMEOBJ for datetime object.
67
+ * @return string|dateTime The formatted date
68
+ */
69
+ function eo_format_date($dateString='',$format='d-m-Y'){
70
+
71
+ if($format!=''&& $dateString!=''){
72
+ $datetime = new DateTime($dateString, eo_get_blog_timezone());
73
+ $formated = eo_format_datetime($datetime,$format);
74
+ return $formated;
75
+ }
76
+ return false;
77
+ }
78
+
79
+ /**
80
+ * Returns the blog timezone
81
+ *
82
+ * Gets timezone settings from the db. If a timezone identifier is used just turns
83
+ * it into a DateTimeZone. If an offset is usd, it tries to find a suitable timezone.
84
+ * If all else fails it uses UTC.
85
+ *
86
+ * @since 1.3.0
87
+ *
88
+ * @return DateTimeZone The blog timezone
89
+ */
90
+ function eo_get_blog_timezone(){
91
+ $tzstring = wp_cache_get( 'eventorganiser_timezone' );
92
+
93
+ if ( false === $tzstring ) {
94
+
95
+ $tzstring =get_option('timezone_string');
96
+ $offset = get_option('gmt_offset');
97
+
98
+ // Remove old Etc mappings. Fallback to gmt_offset.
99
+ if ( !empty($tz_string) && false !== strpos($tzstring,'Etc/GMT') )
100
+ $tzstring = '';
101
+
102
+ if( empty($tzstring) && $offset!=0 ):
103
+ //use offset
104
+ $offset *= 3600; // convert hour offset to seconds
105
+ $allowed_zones = timezone_abbreviations_list();
106
+
107
+ foreach ($allowed_zones as $abbr):
108
+ foreach ($abbr as $city):
109
+ if ($city['offset'] == $offset){
110
+ $tzstring=$city['timezone_id'];
111
+ break 2;
112
+ }
113
+ endforeach;
114
+ endforeach;
115
+ endif;
116
+
117
+ //Issue with the timezone selected, set to 'UTC'
118
+ if( empty($tzstring) ):
119
+ $tzstring = 'UTC';
120
+ endif;
121
+
122
+ //Cache timezone string not timezone object
123
+ //Thanks to Ben Huson http://wordpress.org/support/topic/plugin-event-organiser-getting-500-is-error-when-w3-total-cache-is-on
124
+ wp_cache_set( 'eventorganiser_timezone', $tzstring );
125
+ }
126
+
127
+ if( $tzstring instanceof DateTimeZone)
128
+ return $tzstring;
129
+
130
+ $timezone = new DateTimeZone($tzstring);
131
+ return $timezone;
132
+ }
133
+
134
+
135
+ /**
136
+ * Calculates and formats an interval between two days, passed in any order.
137
+ * It's a PHP 5.2 workaround for {@link http://www.php.net/manual/en/dateinterval.format.php date interval format}
138
+ * @since 1.5
139
+ *
140
+ * @param dateTime $_date1 One date to compare
141
+ * @param dateTime $_date2 Second date to compare
142
+ * @param string $format Used to format the interval. See http://www.php.net/manual/en/dateinterval.format.php
143
+ * @return string Formatted interval.
144
+ */
145
+ function eo_date_interval($_date1,$_date2, $format){
146
+
147
+ //Calculate R values (signs)
148
+ $R = ($_date1 <= $_date2 ? '+' : '-');
149
+ $r = ($_date1 <= $_date2 ? '' : '-');
150
+
151
+ //Make sure $date1 is ealier
152
+ $date1 = ($_date1 <= $_date2 ? $_date1 : $_date2);
153
+ $date2 = ($_date1 <= $_date2 ? $_date2 : $_date1);
154
+
155
+ //Calculate total days difference
156
+ $total_days = round(abs($date1->format('U') - $date2->format('U'))/86400);
157
+
158
+ //A leap year work around - consistent with DateInterval
159
+ $leap_year = ( $date1->format('m-d') == '02-29' ? true : false);
160
+ if( $leap_year ){
161
+ //This will only effect counting the number of days - and is corrected later.
162
+ //Otherwise incrementing $date1 by a year will overflow to March
163
+ $date1->modify('-1 day');
164
+ }
165
+
166
+ $periods = array( 'years'=>-1,'months'=>-1,'days'=>-1,'hours'=>-1);
167
+
168
+ foreach ($periods as $period => &$i ){
169
+
170
+ if($period == 'days' && $leap_year )
171
+ $date1->modify('+1 day');//Corrects earlier adjustment
172
+
173
+ while( $date1 <= $date2 ){
174
+ $date1->modify('+1 '.$period);
175
+ $i++;
176
+ }
177
+
178
+ //Reset date and record increments
179
+ $date1->modify('-1 '.$period);
180
+ }
181
+ extract($periods);
182
+
183
+ //Minutes, seconds
184
+ $seconds = round(abs($date1->format('U') - $date2->format('U')));
185
+ $minutes = floor($seconds/60);
186
+ $seconds = $seconds - $minutes*60;
187
+
188
+ $replace = array(
189
+ '/%y/' => $years,
190
+ '/%Y/' => zeroise($years,2),
191
+ '/%m/' => $months,
192
+ '/%M/' => zeroise($months,2),
193
+ '/%d/' => $days,
194
+ '/%D/' => zeroise($days,2),
195
+ '/%a/' => zeroise($total_days,2),
196
+ '/%h/' => $hours,
197
+ '/%H/' => zeroise($hours,2),
198
+ '/%i/' => $minutes,
199
+ '/%I/' =>zeroise($minutes,2),
200
+ '/%s/' => $seconds,
201
+ '/%S/' => zeroise($seconds,2),
202
+ '/%r/' => $r,
203
+ '/%R/' => $R
204
+ );
205
+
206
+ return preg_replace(array_keys($replace), array_values($replace), $format);
207
+ }
208
+
209
+ /**
210
+ * Very basic class to convert php date format into xdate date format used for javascript.
211
+ *
212
+ * Takes a php date format and converts it to {@link http://arshaw.com/xdate/#Formatting xdate format} so
213
+ * that it can b used in javascript (notably the fullCalendar).
214
+ *
215
+ * Doesn't support
216
+ *L Whether it's a leap year
217
+ * N ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0)
218
+ * w Numeric representation of the day of the week (0=sun,...)
219
+ * z The day of the year (starting from 0)
220
+ * t Number of days in the given month
221
+ * B Swatch Internet time
222
+ * u microseconds
223
+ *
224
+ * e Timezone identifier (added in PHP 5.1.0) Examples: UTC, GMT, Atlantic/Azores
225
+ * I (capital i) Whether or not the date is in daylight saving time 1 if Daylight Saving Time, 0 otherwise.
226
+ * O Difference to Greenwich time (GMT) in hours Example: +0200
227
+ * T Timezone abbreviation Examples: EST, MDT ...
228
+ * Z Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those east of UTC is always positive.
229
+
230
+ * c ISO 8601 date (added in PHP 5) 2004-02-12T15:19:21+00:00
231
+ * r RFC 2822 formatted date Example: Thu, 21 Dec 2000 16:01:07 +0200
232
+ * U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) See also time()
233
+ *
234
+ * @since 1.4
235
+ *
236
+ *@param string $phpformat Format according to http://php.net/manual/en/function.date.php
237
+ *@return string The format translated to xdate format: http://arshaw.com/xdate/#Formatting
238
+ */
239
+ function eventorganiser_php2xdate($phpformat=""){
240
+ $php2xdate = array(
241
+ 'Y'=>'yyyy','y'=>'yy','L'=>''/*Not Supported*/,'o'=>'I',
242
+ 'j'=>'d','d'=>'dd','D'=>'ddd','l'=>'dddd','N'=>'', /*NS*/ 'S'=>'S',
243
+ 'w'=>'', /*NS*/ 'z'=>'',/*NS*/ 'W'=>'w',
244
+ 'F'=>'MMMM','m'=>'MM','M'=>'MMM','n'=>'M','t'=>'',/*NS*/
245
+ 'a'=>'tt','A'=>'TT',
246
+ 'B'=>'',/*NS*/'g'=>'h','G'=>'H','h'=>'hh','H'=>'HH','u'=>'fff',
247
+ 'i'=>'mm','s'=>'ss',
248
+ 'O'=>'zz ', 'P'=>'zzz',
249
+ 'c'=>'u',
250
+ );
251
+
252
+ $xdateformat="";
253
+
254
+ for($i=0; $i< strlen($phpformat); $i++){
255
+
256
+ //Handle backslash excape
257
+ if($phpformat[$i]=="\\"){
258
+ $xdateformat .= "\\".$phpformat[$i+1];
259
+ $i++;
260
+ continue;
261
+ }
262
+
263
+ if(isset($php2xdate[$phpformat[$i]])){
264
+ $xdateformat .= $php2xdate[$phpformat[$i]];
265
+ }else{
266
+ $xdateformat .= $phpformat[$i];
267
+ }
268
+ }
269
+ return $xdateformat;
270
+ }
271
+
272
+
273
+ /**
274
+ * Very basic class to convert php date format into jQuery UI date format used for javascript.
275
+ *
276
+ * Similar to {@see `eventorganiser_php2xdate()`} - but the format is slightly different for jQuery UI
277
+ * Takes a php date format and converts it to {@link http://docs.jquery.com/UI/Datepicker/formatDate} so
278
+ * that it can b used in javascript (notably by the datepicker).
279
+ *
280
+ * **Please note that this function does not convert time formats**
281
+ *
282
+ * @since 1.7
283
+ *
284
+ *@param string $phpformat Format according to http://php.net/manual/en/function.date.php
285
+ *@return string The format translated to xdate format: http://docs.jquery.com/UI/Datepicker/formatDate
286
+ */
287
+ function eventorganiser_php2jquerydate($phpformat=""){
288
+ $php2jquerydate = array(
289
+ 'Y'=>'yy','y'=>'y','L'=>''/*Not Supported*/,'o'=>'',/*Not Supported*/
290
+ 'j'=>'d','d'=>'dd','D'=>'D','DD'=>'dddd','N'=>'',/*NS*/ 'S' => ''/*NS*/,
291
+ 'w'=>'', /*NS*/ 'z'=>'o',/*NS*/ 'W'=>'w',
292
+ 'F'=>'MM','m'=>'mm','M'=>'M','n'=>'m','t'=>'',/*NS*/
293
+ 'a'=>''/*NS*/,'A'=>''/*NS*/,
294
+ 'B'=>'',/*NS*/'g'=>''/*NS*/,'G'=>''/*NS*/,'h'=>''/*NS*/,'H'=>''/*NS*/,'u'=>'fff',
295
+ 'i'=>''/*NS*/,'s'=>''/*NS*/,
296
+ 'O'=>''/*NS*/, 'P'=>''/*NS*/,
297
+ );
298
+
299
+ $jqueryformat="";
300
+
301
+ for($i=0; $i< strlen($phpformat); $i++){
302
+
303
+ //Handle backslash excape
304
+ if($phpformat[$i]=="\\"){
305
+ $jqueryformat .= "\\".$phpformat[$i+1];
306
+ $i++;
307
+ continue;
308
+ }
309
+
310
+ if(isset($php2jquerydate[$phpformat[$i]])){
311
+ $jqueryformat .= $php2jquerydate[$phpformat[$i]];
312
+ }else{
313
+ $jqueryformat .= $phpformat[$i];
314
+ }
315
+ }
316
+ return $jqueryformat;
317
+ }
318
+
319
+
320
+ /**
321
+ * A utilty function intended for removing duplicate DateTime objects from array
322
+ * @since 1.5
323
+ *@access private
324
+ * @ignore
325
+ *
326
+ * @param array $array Array of DateTime objects (though can work for other objects)
327
+ * @return array A sub-array of the passed array, containing only unique elements.
328
+ */
329
+ function _eventorganiser_remove_duplicates( $array=array() ){
330
+ //Do we need to worry about the times of the date-time objects?
331
+
332
+ if( empty($array) )
333
+ return $array;
334
+
335
+ $unique = array();
336
+ foreach ($array as $key=>$object){
337
+ if( !in_array($object, $unique) )
338
+ $unique[$key] = $object;
339
+ }
340
+
341
+ return $unique;
342
+ }
343
+
344
+
345
+ /**
346
+ * Utility function Compares two DateTime object,
347
+ * Returns +1 if the first date is after, -1 if its before or 0 if they're the same
348
+ * Note: does not compare *times*.
349
+ * Used to filter occurrances with udiff
350
+ *
351
+ * @access private
352
+ * @ignore
353
+ * @since 1.0.0
354
+ *
355
+ * @param dateTime $date1 The first date to compare
356
+ * @param dateTime $date2 The second date to compare
357
+ * @return int 1 | 0 | -1
358
+ */
359
+ function _eventorganiser_compare_dates($date1,$date2){
360
+ //Don't wish to compare times
361
+ if($date1->format('Ymd') == $date2->format('Ymd'))
362
+ return 0;
363
+
364
+ return ($date1 > $date2)? 1:-1;
365
+ }
366
+
367
+
368
+
369
+ /**
370
+ * A workaround which handles "[ordinal] [day] of +(n) month" statements when
371
+ * using php 5.2.
372
+ *
373
+ * @access private
374
+ * @ignore
375
+ * @since 1.3
376
+ *
377
+ * @param dateTime $date dateTime object to modify
378
+ * @param string $modify How to modify the date: e.g. 'last Sunday of +2 month'
379
+ * @return dateTime the modified dateTime object. DateTime is set to blog tz.
380
+ */
381
+ function _eventorganiser_php52_modify($date='',$modify=''){
382
+
383
+ //Expect e.g. 'Second Monday of +2 month
384
+ $pattern = '/([a-zA-Z]+)\s([a-zA-Z]+) of \+(\d+) month/';
385
+ preg_match($pattern, $modify, $matches);
386
+
387
+ $ordinal = array_search(strtolower($matches[1]), array('last','first','second','third','fourth') ); //0-4
388
+ $day = array_search(strtolower($matches[2]), array('sunday','monday','tuesday','wednesday','thursday','friday','saturday') ); //0-6
389
+ $freq = intval($matches[3]);
390
+
391
+ //set to first day of month
392
+ $date = date_create($date->format('Y-m-1'));
393
+
394
+ //first day of +$freq months
395
+ $date->modify('+'.$freq.' month');
396
+
397
+ //Calculate offset to day of week
398
+ if($ordinal >0):
399
+ $offset = ($day-intval($date->format('w')) +7)%7;//Offset to first weekday of that month (e.g. first Monday)
400
+ $d =($offset) +7*($ordinal-1) +1; //If wanting to get second, third or fourth monday add multiples of 7.
401
+
402
+ else:
403
+ //Ordinal is 'Last'
404
+ $date = date_create($date->format('Y-m-t'));//Last day
405
+ $offset = intval(($date->format('w')-$day+7)%7);//Find offset from last day to the last weekday of month (e.g. last Sunday)
406
+ $d = intval($date->format('t'))-$offset;
407
+ endif;
408
+
409
+ $date = date_create($date->format('Y-m-'.$d), eo_get_blog_timezone());
410
+
411
+ return $date;
412
+ }
413
+
414
+ /**
415
+ * Generates excerpt from contant if needed, and trims to a given length
416
+ *
417
+ * Is very simliar to wp_trim_excerpt. Doesn't apply excerpt_more filter
418
+ * Applies eventorganiser_trim_excerpt filter
419
+ * Must be used inside the loop
420
+ *
421
+ * @since 1.5
422
+ * @param string $text Optional. The excerpt. If set to empty, an excerpt is generated.
423
+ * @param int $excerpt_length The excerpt length if generated from content.
424
+ * @return string The excerpt.
425
+ */
426
+ function eventorganiser_trim_excerpt($text = '', $excerpt_length=55) {
427
+ $raw_excerpt = $text;
428
+ if ( '' == $text ) {
429
+ $text = get_the_content('');
430
+ $text = strip_shortcodes( $text );
431
+ $text = apply_filters('the_content', $text);
432
+ $text = str_replace(']]>', ']]&gt;', $text);
433
+ $text = wp_trim_words( $text, $excerpt_length, '...' );
434
+ }
435
+ return apply_filters('eventorganiser_trim_excerpt', $text, $raw_excerpt);
436
+ }
437
+
438
+
439
+ /**
440
+ * A helper function, creates a DateTime object from a date string and sets the timezone to the blog's current timezone
441
+ *
442
+ *@since 1.3
443
+ *@uses date_create()
444
+ *@param string $datetime_string A date-time in string format
445
+ *@return datetime The corresponding DateTime object.
446
+ */
447
+ function eventorganiser_date_create($datetime_string){
448
+ $tz = eo_get_blog_timezone();
449
+ return date_create($datetime_string,$tz);
450
+ }
451
+
452
+
453
+ /**
454
+ * (Private) Utility function checks a date-time string is formatted correctly (according to the options)
455
+ * @access private
456
+ * @ignore
457
+ * @since 1.0.0
458
+
459
+ * @param datetime_string - a datetime string
460
+ * @param string $format - Format of the datetime string. One of 'd-m-Y', 'm-d-Y' and 'Y-m-d'.
461
+ * @return int DateTime| false - the parsed datetime string as a DateTime object or false on error (incorrectly formatted for example)
462
+ */
463
+ function _eventorganiser_check_datetime( $datetime_string = '', $format = null ){
464
+
465
+ if( is_null( $format ) )
466
+ $format = eventorganiser_get_option('dateformat');
467
+
468
+ //Backwards compatible - can probably remove 2.1+
469
+ if( is_bool( $format ) ){
470
+ _deprecated_argument('_eventorganiser_check_datetime', '1.8', 'This function no longer accepts a boolean, pass the format of the passed date-time string.');
471
+ if( true === $format )
472
+ $format = 'Y-m-d';
473
+ else
474
+ $format = eventorganiser_get_option('dateformat');
475
+ }
476
+
477
+ //Get regular expression.
478
+ if( $format == 'Y-m-d' ){
479
+ $reg_exp = "/(?P<year>\d{4})[-.\/](?P<month>\d{1,})[-.\/](?P<day>\d{1,}) (?P<hour>\d{2}):(?P<minute>\d{2})/";
480
+
481
+ }elseif( $format == 'd-m-Y' ){
482
+ $reg_exp = "/(?P<day>\d{1,})[-.\/](?P<month>\d{1,})[-.\/](?P<year>\d{4}) (?P<hour>\d{2}):(?P<minute>\d{2})/";
483
+
484
+ }else{
485
+ $reg_exp = "/(?P<month>\d{1,})[-.\/](?P<day>\d{1,})[-.\/](?P<year>\d{4}) (?P<hour>\d{2}):(?P<minute>\d{2})/";
486
+ }
487
+
488
+ if( !preg_match($reg_exp, $datetime_string,$matches) )
489
+ return false;
490
+
491
+ extract(array_map('intval',$matches));
492
+
493
+ if ( !checkdate($month, $day, $year) || $hour < 0 || $hour > 23 || $minute < 0 || $minute > 59 )
494
+ return false;
495
+
496
+ $datetime = new DateTime(null, eo_get_blog_timezone());
497
+ $datetime->setDate($year, $month, $day);
498
+ $datetime->setTime($hour, $minute);
499
+ return $datetime;
500
+ }
501
+
502
+ /**
503
+ * Utility function for printing/returning radio boxes
504
+ *
505
+ * The $args array - excepts the following keys
506
+ *
507
+ * * **id** - The id of the radiobox (alias: label_for)
508
+ * * **name** - The name of the radiobox
509
+ * * **checked** - The the value to have checked
510
+ * * **options** - Array of options in 'value'=>'Label' format
511
+ * * **label** - The label for the radiobox field set
512
+ * * **class** - Class to be added to the radiobox field set
513
+ * * **echo** - Whether to print the mark-up or just return it
514
+ * * **help** - Optional, help text to accompany the field.
515
+ *
516
+ * @access private
517
+ * @param $args array The array of arguments
518
+ */
519
+ function eventorganiser_radio_field( $args ){
520
+
521
+ $args = wp_parse_args($args,array(
522
+ 'checked'=>'', 'help' => '', 'options'=>'', 'name'=>'', 'echo'=>1,
523
+ 'class'=>'', 'label' => '','label_for'=>''
524
+ ));
525
+
526
+ $id = ( !empty($args['id']) ? $args['id'] : $args['label_for']);
527
+ $name = isset($args['name']) ? $args['name'] : '';
528
+ $checked = $args['checked'];
529
+ $label = !empty($args['label']) ? '<legend><label>'.esc_html($args['label']).'</label></legend>' : '';
530
+ $class = !empty($args['class']) ? 'class="'.sanitize_html_class($args['class']).'"' : '';
531
+
532
+ $html = sprintf('<fieldset %s> %s', $class, $label);
533
+ if( !empty($args['options']) ){
534
+ foreach ($args['options'] as $value => $opt_label ){
535
+ $html .= sprintf('<label for="%1$s"><input type="radio" id="%1$s" name="%3$s" value="%4$s" %2$s> <span> %5$s </span></label><br>',
536
+ esc_attr($id.'_'.$value),
537
+ checked($value, $checked, false),
538
+ esc_attr($name),
539
+ esc_attr($value),
540
+ esc_html($opt_label));
541
+ }
542
+ }
543
+ if(!empty($args['help'])){
544
+ $html .= '<p class="description">'.esc_html($args['help']).'</p>';
545
+ }
546
+ $html .= '</fieldset>';
547
+
548
+ if( $args['echo'] )
549
+ echo $html;
550
+
551
+ return $html;
552
+ }
553
+
554
+
555
+ /**
556
+ * Utility function for printing/returning select field
557
+ *
558
+ * The $args array - excepts the following keys
559
+ *
560
+ * * **id** - The id of the select box (alias: label_for)
561
+ * * **name** - The name of the select box
562
+ * * **selected** - The the value to have selected
563
+ * * **options** - Array of options in 'value'=>'Label' format
564
+ * * **multiselect** True or False for multi-select
565
+ * * **label** - The label for the radiobox field set
566
+ * * **class** - Class to be added to the radiobox field set
567
+ * * **echo** - Whether to print the mark-up or just return it
568
+ * * **help** - Optional, help text to accompany the field.
569
+ *
570
+ * @access private
571
+ * @param $args array The array of arguments
572
+ */
573
+ function eventorganiser_select_field($args){
574
+
575
+ $args = wp_parse_args($args,array(
576
+ 'selected'=>'', 'help' => null, 'options'=>'', 'name'=>'', 'echo'=>1,
577
+ 'label_for'=>'','class'=>'','disabled'=>false,'multiselect'=>false,
578
+ 'inline_help' => false
579
+ ));
580
+
581
+ $id = ( !empty($args['id']) ? $args['id'] : $args['label_for']);
582
+ $name = isset($args['name']) ? $args['name'] : '';
583
+ $selected = $args['selected'];
584
+ $classes = array_map( 'sanitize_html_class', explode( ' ', $args['class'] ) );
585
+ $class = implode( ' ', $classes );
586
+ $multiselect = ($args['multiselect'] ? 'multiple' : '' );
587
+ $disabled = ($args['disabled'] ? 'disabled="disabled"' : '' );
588
+
589
+ $html = sprintf('<select %s name="%s" id="%s" %s>',
590
+ !empty( $class ) ? 'class="'.$class.'"' : '',
591
+ esc_attr($name),
592
+ esc_attr($id),
593
+ $multiselect.' '.$disabled
594
+ );
595
+ if( !empty( $args['show_option_all'] ) ){
596
+ $html .= sprintf('<option value="" %s> %s </option>',selected( empty($selected), true, false ), esc_html( $args['show_option_all'] ) );
597
+ }
598
+
599
+ if( !empty($args['options']) ){
600
+ foreach ($args['options'] as $value => $label ){
601
+ if( $args['multiselect'] && is_array($selected) )
602
+ $_selected = selected( in_array($value, $selected), true, false);
603
+ else
604
+ $_selected = selected($selected, $value, false);
605
+
606
+ $html .= sprintf('<option value="%s" %s> %s </option>',esc_attr($value),$_selected, esc_html($label));
607
+ }
608
+ }
609
+ $html .= '</select>'. $args['inline_help'];
610
+
611
+ if( isset( $args['help'] ) ){
612
+ $html .= '<p class="description">'.esc_html($args['help']).'</p>';
613
+ }
614
+
615
+ if( $args['echo'] )
616
+ echo $html;
617
+
618
+ return $html;
619
+ }
620
+
621
+
622
+ /**
623
+ * Utility function for printing/returning text field
624
+ *
625
+ * The $args array - excepts the following keys
626
+ *
627
+ * * **id** - The id of the select box (alias: label_for)
628
+ * * **name** - The name of the select box
629
+ * * **value** - The value of the text field
630
+ * * **type** - The type of the text field (e.g. 'text','hidden','password')
631
+ * * **options** - Array of options in 'value'=>'Label' format
632
+ * * **label** - The label for the radiobox field set
633
+ * * **class** - Class to be added to the radiobox field set
634
+ * * **echo** - Whether to print the mark-up or just return it
635
+ * * **help** - Optional, help text to accompany the field.
636
+ *
637
+ * @access private
638
+ * @param $args array The array of arguments
639
+ */
640
+ function eventorganiser_text_field($args){
641
+
642
+ $args = wp_parse_args( $args,
643
+ array(
644
+ 'type' => 'text', 'value'=>'', 'placeholder' => '','label_for'=>'', 'inline_help' => false,
645
+ 'size'=>false, 'min' => false, 'max' => false, 'style'=>false, 'echo'=>true, 'data'=>false,
646
+ 'class' => false,
647
+ )
648
+ );
649
+
650
+ $id = ( !empty($args['id']) ? $args['id'] : $args['label_for']);
651
+ $name = isset($args['name']) ? $args['name'] : '';
652
+ $value = $args['value'];
653
+ $type = $args['type'];
654
+ $classes = array_map( 'sanitize_html_class', explode( ' ', $args['class'] ) );
655
+ $class = implode( ' ', $classes );
656
+
657
+ $min = ( $args['min'] !== false ? sprintf('min="%d"', $args['min']) : '' );
658
+ $max = ( $args['max'] !== false ? sprintf('max="%d"', $args['max']) : '' );
659
+ $size = ( !empty($args['size']) ? sprintf('size="%d"', $args['size']) : '' );
660
+ $style = ( !empty($args['style']) ? sprintf('style="%s"', $args['style']) : '' );
661
+ $placeholder = ( !empty($args['placeholder']) ? sprintf('placeholder="%s"', $args['placeholder']) : '');
662
+ $disabled = ( !empty($args['disabled']) ? 'disabled="disabled"' : '' );
663
+
664
+ //Custom data-* attributes
665
+ $data = '';
666
+ if( !empty( $args['data'] ) && is_array( $args['data'] ) ){
667
+ foreach( $args['data'] as $key => $attr_value ){
668
+ $data .= sprintf( 'data-%s="%s"', esc_attr( $key ), esc_attr( $attr_value ) );
669
+ }
670
+ }
671
+
672
+ $attributes = array_filter( array($min,$max,$size,$placeholder,$disabled, $style, $data ) );
673
+
674
+ $html = sprintf('<input type="%s" name="%s" class="%s regular-text ltr" id="%s" value="%s" autocomplete="off" %s /> %s',
675
+ esc_attr( $type ),
676
+ esc_attr( $name ),
677
+ $class,
678
+ esc_attr( $id ),
679
+ esc_attr( $value ),
680
+ implode(' ', $attributes),
681
+ $args['inline_help']
682
+ );
683
+
684
+ if( isset($args['help']) ){
685
+ $html .= '<p class="description">'.$args['help'].'</p>';
686
+ }
687
+
688
+ if( $args['echo'] )
689
+ echo $html;
690
+
691
+ return $html;
692
+ }
693
+
694
+
695
+ /**
696
+ * Utility function for printing/returning text field
697
+ *
698
+ * The $args array - excepts the following keys
699
+ *
700
+ * * **id** - The id of the checkbox (alias: label_for)
701
+ * * **name** - The name of the select box
702
+ * * **options** - Single or Array of options in 'value'=>'Label' format
703
+ * * **values** - The values of the text field
704
+ * * **type** - The type of the text field (e.g. 'text','hidden','password')
705
+
706
+ * * **label** - The label for the radiobox field set
707
+ * * **class** - Class to be added to the radiobox field set
708
+ * * **echo** - Whether to print the mark-up or just return it
709
+ * * **help** - Optional, help text to accompany the field.
710
+ *
711
+ * @access private
712
+ * @param $args array The array of arguments
713
+ */
714
+ function eventorganiser_checkbox_field($args=array()){
715
+
716
+ $args = wp_parse_args($args,array(
717
+ 'help' => '','name'=>'', 'class'=>'',
718
+ 'checked'=>'', 'echo'=>true,'multiselect'=>false
719
+ ));
720
+
721
+ $id = ( !empty($args['id']) ? $args['id'] : $args['label_for']);
722
+ $name = isset($args['name']) ? $args['name'] : '';
723
+ $class = ( $args['class'] ? "class='".sanitize_html_class($args['class'])."'" :"" );
724
+
725
+ /* $options and $checked are either both arrays or they are both strings. */
726
+ $options = isset($args['options']) ? $args['options'] : false;
727
+ $checked = isset($args['checked']) ? $args['checked'] : 1;
728
+
729
+ $html ='';
730
+ if( is_array($options) ){
731
+ foreach( $options as $value => $opt_label ){
732
+ $html .= sprintf('<label for="%1$s">
733
+ <input type="checkbox" name="%2$s" id="%1$s" value="%3$s" %4$s %5$s>
734
+ %6$s </br>
735
+ </label>',
736
+ esc_attr($id.'_'.$value),
737
+ esc_attr(trim($name).'[]'),
738
+ esc_attr($value),
739
+ checked( in_array($value, $checked), true, false ),
740
+ $class,
741
+ esc_attr($opt_label)
742
+ );
743
+ }
744
+ }else{
745
+ $html .= sprintf('<input type="checkbox" id="%1$s" name="%2$s" %3$s %4$s value="%5$s">',
746
+ esc_attr($id),
747
+ esc_attr($name),
748
+ checked( $checked, $options, false ),
749
+ $class,
750
+ esc_attr($options)
751
+ );
752
+ }
753
+
754
+ if(!empty($args['help'])){
755
+ $html .= '<p class="description">'.$args['help'].'</p>';
756
+ }
757
+
758
+ if( $args['echo'] )
759
+ echo $html;
760
+
761
+ return $html;
762
+ }
763
+
764
+
765
+
766
+ /**
767
+ * Utility function for printing/returning text area
768
+ *
769
+ * The $args array - excepts the following keys
770
+ *
771
+ * * **id** - The id of the checkbox (alias: label_for)
772
+ * * **name** - The name of the select box
773
+ * * **options** - Single or Array of options in 'value'=>'Label' format
774
+ * * **tinymce** Whether to use the TinyMCE editor. The TinyMCE prints directly.
775
+ * * **value** - The value of the text area
776
+ * * **rows** - The number of rows. Default 5.
777
+ * * **cols** - The number of columns. Default 50.
778
+ * * **class** - Class to be added to the textarea
779
+ * * **echo** - Whether to print the mark-up or just return it
780
+ * * **help** - Optional, help text to accompany the field.
781
+ *
782
+ * @access private
783
+ * @param $args array The array of arguments
784
+ */
785
+ function eventorganiser_textarea_field($args){
786
+
787
+ $args = wp_parse_args($args,array(
788
+ 'type' => 'text', 'value'=>'', 'tinymce' => '', 'help' => '',
789
+ 'class'=>'large-text', 'echo'=>true,'rows'=>5, 'cols'=>50,
790
+ 'readonly'=> false,
791
+ ));
792
+
793
+ $id = ( !empty($args['id']) ? $args['id'] : $args['label_for']);
794
+ $name = isset($args['name']) ? $args['name'] : '';
795
+ $value = $args['value'];
796
+ $class = $args['class'];
797
+ $readonly = $args['readonly'] ? 'readonly' : '';
798
+ $html ='';
799
+
800
+ if( $args['tinymce'] ){
801
+ wp_editor( $value, esc_attr($id) ,array(
802
+ 'textarea_name'=>$name,
803
+ 'media_buttons'=>false,
804
+ ));
805
+ }else{
806
+ $html .= sprintf('<textarea cols="%s" rows="%d" name="%s" class="%s large-text" id="%s" %s >%s</textarea>',
807
+ intval($args['cols']),
808
+ intval($args['rows']),
809
+ esc_attr($name),
810
+ sanitize_html_class($class),
811
+ esc_attr($id),
812
+ $readonly,
813
+ esc_textarea($value)
814
+ );
815
+ }
816
+
817
+ if(!empty($args['help'])){
818
+ $html .= '<p class="description">'.$args['help'].'</p>';
819
+ }
820
+
821
+ if( $args['echo'] )
822
+ echo $html;
823
+
824
+ return $html;
825
+ }
826
+
827
+
828
+ function eventorganiser_esc_printf($text){
829
+ return str_replace('%','%%',$text);
830
+ }
831
+
832
+
833
+ function eventorganiser_cache_get( $key, $group ){
834
+
835
+ $ns_key = wp_cache_get( 'eventorganiser_'.$group.'_key' );
836
+
837
+ // if not set, initialize it
838
+ if ( $ns_key === false )
839
+ wp_cache_set( 'eventorganiser_'.$group.'_key', 1 );
840
+
841
+ return wp_cache_get( "eo_".$ns_key."_".$key, $group );
842
+ }
843
+
844
+
845
+ function eventorganiser_clear_cache( $group, $key = false ){
846
+
847
+ if( $key == false ){
848
+ //If a key is not specified clear entire group by incrementing name space
849
+ return wp_cache_incr( 'eventorganiser_'.$group.'_key' );
850
+
851
+ }elseif( $ns_key = wp_cache_get( 'eventorganiser_'.$group.'_key' ) ){
852
+ //If key is specified - clear particular key from the group
853
+ return wp_cache_delete( "eo_".$ns_key."_".$key, $group );
854
+ }
855
+ }
856
+
857
+
858
+ function eventorganiser_cache_set( $key, $value, $group, $expire = 0 ){
859
+ $ns_key = wp_cache_get( 'eventorganiser_'.$group.'_key' );
860
+
861
+ // if not set, initialize it
862
+ if ( $ns_key === false )
863
+ wp_cache_set( 'eventorganiser_'.$group.'_key', 1 );
864
+
865
+ return wp_cache_add( "eo_".$ns_key."_".$key, $value, $group, $expire );
866
+ }
867
+
868
+ /**
869
+ * Display inline help via a qTip2 tooltip.
870
+ *
871
+ * The function handles the javascript/css loading and generates the link HTML which will trigger the tooltip.
872
+ * The HTML can be returned or printed using the fourth argument.
873
+ *
874
+ * @param string $title The title of the tooltip that will appear
875
+ * @param string $content The content of the tooltip
876
+ * @param bool $echo Whether the link HTML should be printed as well as returned.
877
+ * @return string
878
+ */
879
+ function eventorganiser_inline_help( $title, $content, $echo = false, $type = 'help' ){
880
+ static $help = array();
881
+
882
+ $help[] = array(
883
+ 'title' => $title,
884
+ 'content' => $content,
885
+ );
886
+
887
+ wp_localize_script( 'eo-inline-help', 'eoHelp', $help );
888
+
889
+ //Ensure style is called after WP styles
890
+ add_action( 'admin_footer', '_eventorganiser_enqueue_inline_help_scripts', 100 );
891
+
892
+ $id = count($help)-1;
893
+ $src = EVENT_ORGANISER_URL."css/images/{$type}-14.png";
894
+
895
+ $link = sprintf( '<a href="#" id="%s" class="eo-inline-help eo-%s-inline"><img src="%s" width="16" height="16"></a>',
896
+ 'eo-inline-help-' . $id,
897
+ $type,
898
+ $src
899
+ );
900
+
901
+ if( $echo )
902
+ echo $link;
903
+
904
+ return $link;
905
+ }
906
+ function _eventorganiser_enqueue_inline_help_scripts(){
907
+ wp_enqueue_script( 'eo-inline-help' );
908
+ wp_enqueue_style( 'eventorganiser-style' );
909
+ }
910
+ ?>
includes/event-organiser-venue-functions.php ADDED
@@ -0,0 +1,867 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Venue related functions
4
+ *@package venue-functions
5
+ */
6
+
7
+ /**
8
+ * Returns the ID of the venue of an event.
9
+ *
10
+ * Can be used inside the loop to output the
11
+ * venue id of the current event by not passing an ID.
12
+ *
13
+ * Otherwise it returns the venue ID of the passed event ID.
14
+ *
15
+ * @since 1.0.0
16
+ * @param int $post_id The event (post) ID. Uses current event if empty.
17
+ * @return int The corresponding venue (event-venue term) ID
18
+ */
19
+ function eo_get_venue($event_id=''){
20
+ global $post;
21
+ $event = $post;
22
+
23
+ if( !empty($event_id) ){
24
+ $post_id = $event_id;
25
+ }else{
26
+ $post_id = (isset($post->ID) ? $post->ID : 0);
27
+ }
28
+
29
+ if( empty($post_id) )
30
+ return false;
31
+
32
+ $venue = get_the_terms($post_id,'event-venue');
33
+
34
+ if ( empty($venue) || is_wp_error( $venue ) )
35
+ return false;
36
+
37
+ $venue = array_pop($venue);
38
+
39
+ return (int) $venue->term_id;
40
+ }
41
+
42
+
43
+ /**
44
+ * Returns the slug of the venue of an event.
45
+ *
46
+ * When used without an argument it uses the event specified in the global $post (e.g. current event in the loop).
47
+ * Can be used inside the loop to output the venue id of the current event.
48
+ * @since 1.0.0
49
+ *
50
+ * @param int $post_id The event (post) ID. Uses current event if empty.
51
+ * @return int The corresponding venue (event-venue term) slug
52
+ */
53
+ function eo_get_venue_slug($event_id=''){
54
+ global $post;
55
+ $event = $post;
56
+
57
+ if( !empty($event_id) ){
58
+ $post_id = $event_id;
59
+ }else{
60
+ $post_id = (isset($post->ID) ? $post->ID : 0);
61
+ }
62
+
63
+ $venue = get_the_terms($post_id,'event-venue');
64
+
65
+ if ( empty($venue) || is_wp_error( $venue ) )
66
+ return false;
67
+
68
+ $venue = array_pop($venue);
69
+
70
+ return $venue->slug;
71
+ }
72
+
73
+
74
+
75
+ /**
76
+ * A utility function for getting the venue ID from a venue ID or slug.
77
+ * Useful for when we don't know which is being passed to us, but we want the ID.
78
+ * IDs **must** be cast as integers
79
+ * @since 1.6
80
+ *
81
+ * @param mixed $venue_slug_or_id The venue ID as an integer. Or Slug as string. Uses venue of current event if empty.
82
+ * @return int The corresponding venue (event-venue term) ID or false if not found.
83
+ */
84
+ function eo_get_venue_id_by_slugorid($venue_slug_or_id=''){
85
+
86
+ $venue = $venue_slug_or_id;
87
+
88
+ if( empty($venue) )
89
+ return eo_get_venue();
90
+
91
+ if( is_int($venue) )
92
+ return (int) $venue;
93
+
94
+ $venue = eo_get_venue_by('slug', $venue);
95
+
96
+ if( $venue )
97
+ return (int) $venue->term_id;
98
+
99
+ return false;
100
+ }
101
+
102
+
103
+ /**
104
+ * Get all venue data from database by venue field and data. This acts as a simple wrapper for {@see `get_term_by()`}
105
+ *
106
+ * Warning: `$value` is not escaped for 'name' `$field`. You must do it yourself, if required.
107
+ *
108
+ * If `$value` does not exist for that `$field`, the return value will be false other the term will be returned.
109
+ *
110
+ * ###Example
111
+ * Get the venue ID by slug (A better way is to use {@see `eo_get_venue_id_by_slugorid()`}
112
+ *
113
+ * $venue = eo_get_venue_by('slug','my-venue-slug');
114
+ * if( $venue )
115
+ * $venue_id = (int) $venue->term_id;
116
+ *
117
+ *
118
+ * @uses get_term_by()
119
+ * @since 1.6
120
+ *
121
+ * @param string $field Either 'slug', 'name', or 'id'
122
+ * @param string|int $value Search for this term value
123
+ * @param string $output Constant OBJECT, ARRAY_A, or ARRAY_N
124
+ * @param string $filter Optional, default is raw or no WordPress defined filter will applied.
125
+ * @return mixed Term Row from database. Will return false if $taxonomy does not exist or $term was not found.
126
+ */
127
+ function eo_get_venue_by($field,$value,$output = OBJECT, $filter = 'raw' ){
128
+ $venue = get_term_by($field, $value, 'event-venue',$output, $filter);
129
+ return $venue;
130
+ }
131
+
132
+
133
+ /**
134
+ * Returns the name of the venue of an event.
135
+ * If used without any arguments uses the venue of the current event.
136
+ *
137
+ * Returns the name of a venue specified by it's slug or ID. If used inside the loop, it can return the name of the current post's venue. If specifying the venue by ID, **the ID must be an integer**.
138
+ *
139
+ * This function behaves differently to {@see `eo_get_venue_slug()`} which takes the event ID, rather than venue ID or slug, as an optional argument.
140
+ *
141
+ * @since 1.0.0
142
+ *
143
+ * @param int|string $venue_slug_or_id The venue ID (as an integer) or slug (as a string). Uses venue of current event if empty.
144
+ * @return string The name of the corresponding venue
145
+ */
146
+ function eo_get_venue_name($venue_slug_or_id=''){
147
+ $venue_id = eo_get_venue_id_by_slugorid($venue_slug_or_id);
148
+ $venue = get_term($venue_id,'event-venue');
149
+
150
+ if ( empty($venue) || is_wp_error( $venue ) )
151
+ return false;
152
+
153
+ return $venue->name;
154
+ }
155
+
156
+ /**
157
+ * Echos the venue of the event
158
+ *
159
+ * @uses eo_get_venue_name()
160
+ * @param (int) venue id or (string) venue slug
161
+ *
162
+ * @since 1.0.0
163
+ */
164
+ function eo_venue_name($venue_slug_or_id=''){
165
+ echo eo_get_venue_name($venue_slug_or_id);
166
+ }
167
+
168
+
169
+ /**
170
+ * Returns the description of the description of an event.
171
+ * If used with any arguments uses the venue of the current event.
172
+ *
173
+ * Returns the description of a venue specified by it's slug or ID. When used without an argument it uses the event specified in the `global $post` (i.e. the current event in the Loop). If specifying the
174
+ * venue by ID, **the ID must be an integer**.
175
+ *
176
+ * @since 1.0.0
177
+ *
178
+ * @param int|string $venue_slug_or_id The venue ID (as an integer) or slug (as a string). Uses venue of current event if empty.
179
+ * @return string The description. of the corresponding venue
180
+ */
181
+ function eo_get_venue_description($venue_slug_or_id=''){
182
+ $venue_id = eo_get_venue_id_by_slugorid($venue_slug_or_id);
183
+ $description = eo_get_venue_meta($venue_id,'_description');
184
+ $description = wptexturize($description);
185
+ $description = convert_chars($description);
186
+ $description = wpautop($description);
187
+ $description = shortcode_unautop($description);
188
+ $description = do_shortcode($description);
189
+ return $description;
190
+ }
191
+
192
+ /**
193
+ * Prints the name of the description of an event.
194
+ * Can be used inside the loop to output the
195
+ * venue id of the current event.
196
+ * @since 1.0.0
197
+ * @uses eo_get_venue_description()
198
+ *
199
+ * @param int|string $venue_slug_or_id The venue ID (as an integer) or slug (as a string). Uses venue of current event if empty.
200
+ */
201
+ function eo_venue_description($venue_slug_or_id=''){
202
+ echo eo_get_venue_description($venue_slug_or_id);
203
+ }
204
+
205
+
206
+
207
+ /**
208
+ * Returns an latitude-longtitude array (keys 'lat', 'lng')
209
+ * If used with any arguments uses the venue of the current event.
210
+ *
211
+ * Returns a latitude-longitude array of a venue specified by it's slug or ID. When used without an argument it uses the event specified in the `global $post` (i.e. the current event in the Loop). If
212
+ * specifying the venue by ID, **the ID must be an integer**.
213
+ * @since 1.0.0
214
+ *
215
+ * @param int|string $venue_slug_or_id The venue ID (as an integer) or slug (as a string). Uses venue of current event if empty.
216
+ * @return array Array with keys 'lat' and 'lng' with corresponding float values.
217
+ */
218
+ function eo_get_venue_latlng($venue_slug_or_id=''){
219
+ $lat = eo_get_venue_lat($venue_slug_or_id);
220
+ $lng = eo_get_venue_lng($venue_slug_or_id);
221
+ return array('lat'=>$lat,'lng'=>$lng);
222
+ }
223
+
224
+ /**
225
+ * Returns the latitude co-ordinate of a venue.
226
+ * If used with any arguments uses the venue of the current event.
227
+ *
228
+ * Returns the latitude of a venue specified by it's slug or ID. When used without an argument it uses the event specified in the `global $post` (i.e. the current event in the Loop). If specifying the
229
+ * specifying the venue by ID, **the ID must be an integer**.
230
+ * @since 1.0.0
231
+ *
232
+ * @param int|string $venue_slug_or_id The venue ID (as an integer) or slug (as a string). Uses venue of current event if empty.
233
+ * @return float The latitude of the venue as a float. 0 If it doesn't exist.
234
+ */
235
+ function eo_get_venue_lat($venue_slug_or_id=''){
236
+ $venue_id = eo_get_venue_id_by_slugorid($venue_slug_or_id);
237
+ $lat = eo_get_venue_meta($venue_id,'_lat');
238
+ $lat = ! empty($lat) ? $lat : 0;
239
+ return $lat;
240
+ }
241
+
242
+ /**
243
+ * Returns the longtitude co-ordinate of a venue.
244
+ * If used with any arguments uses the venue of the current event.
245
+ *
246
+ * Returns the longtitude of a venue specified by it's slug or ID. When used without an argument it uses the event specified in the `global $post` (i.e. the current event in the Loop). If specifying the
247
+ * specifying the venue by ID, **the ID must be an integer**.
248
+ * @since 1.0.0
249
+ *
250
+ * @param int|string $venue_slug_or_id The venue ID (as an integer) or slug (as a string). Uses venue of current event if empty.
251
+ * @return float The longtitude of the venue as a float. 0 If it doesn't exist.
252
+ */
253
+ function eo_get_venue_lng($venue_slug_or_id=''){
254
+ $venue_id = eo_get_venue_id_by_slugorid($venue_slug_or_id);
255
+ $lng = eo_get_venue_meta($venue_id,'_lng');
256
+ $lng = ! empty($lng) ? $lng : 0;
257
+ return $lng;
258
+ }
259
+
260
+
261
+ /**
262
+ * Prints the latitude co-ordinate of a venue.
263
+ * If used with any arguments uses the venue of the current event.
264
+ * @uses eo_get_venue_lat()
265
+ * @since 1.0.0
266
+ *
267
+ * @param int|string $venue_slug_or_id The venue ID (as an integer) or slug (as a string). Uses venue of current event if empty.
268
+ */
269
+ function eo_venue_lat($venue_slug_or_id=''){
270
+ echo eo_get_venue_lat($venue_slug_or_id);
271
+ }
272
+
273
+
274
+ /**
275
+ * Prints the longtitude co-ordinate of a venue.
276
+ * If used with any arguments uses the venue of the current event.
277
+ * @uses eo_get_venue_lng()
278
+ * @since 1.0.0
279
+ *
280
+ * @param int|string $venue_slug_or_id The venue ID (as an integer) or slug (as a string). Uses venue of current event if empty.
281
+ */
282
+ function eo_venue_lng($venue_slug_or_id=''){
283
+ echo eo_get_venue_lng($venue_slug_or_id);
284
+ }
285
+
286
+
287
+ /**
288
+ * Returns the permalink of a venue
289
+ * If used with any arguments uses the venue of the current event.
290
+ * @uses get_term_link()
291
+ * @since 1.0.0
292
+ *
293
+ * @param int|string $venue_slug_or_id The venue ID (as an integer) or slug (as a string). Uses venue of current event if empty.
294
+ * @return string Link of the venue page
295
+ */
296
+ function eo_get_venue_link($venue_slug_or_id=''){
297
+ $venue_id = eo_get_venue_id_by_slugorid($venue_slug_or_id);
298
+ return get_term_link( $venue_id, 'event-venue' );
299
+ }
300
+
301
+
302
+ /**
303
+ * Prints the permalink of a venue
304
+ * If used with any arguments uses the venue of the current event.
305
+ * @uses eo_get_venue_link()
306
+ * @since 1.0.0
307
+ *
308
+ * @param int|string $venue_slug_or_id The venue ID (as an integer) or slug (as a string). Uses venue of current event if empty.
309
+ */
310
+ function eo_venue_link($venue_slug_or_id=''){
311
+ $venue_id = eo_get_venue_id_by_slugorid($venue_slug_or_id);
312
+ echo eo_get_venue_link($venue_slug_or_id);
313
+ }
314
+
315
+
316
+ /**
317
+ * Returns an array with address details of the event's venue.
318
+ * The keys consist of
319
+ *
320
+ * * 'address'
321
+ * * 'city'
322
+ * * 'state' - the state/province/county of the venue
323
+ * * 'postcode'
324
+ * * 'country'
325
+ *
326
+ * If used with any arguments uses the venue of the current event.
327
+ * @since 1.0.0
328
+ *
329
+ * @param int|string $venue_slug_or_id The venue ID (as an integer) or slug (as a string). Uses venue of current event if empty.
330
+ * @return array Array of venue address details
331
+ */
332
+ function eo_get_venue_address($venue_slug_or_id=''){
333
+ $address=array();
334
+ $venue_id = eo_get_venue_id_by_slugorid($venue_slug_or_id);
335
+ $address_keys = array_keys(_eventorganiser_get_venue_address_fields());
336
+ foreach( $address_keys as $meta_key ){
337
+ $key = trim($meta_key,'_');
338
+ $address[$key] = eo_get_venue_meta($venue_id,$meta_key);
339
+ }
340
+ return $address;
341
+ }
342
+
343
+
344
+ /**
345
+ * Retrieve array of venues. Acts as a wrapper for {@link http://codex.wordpress.org/Function_Reference/get_terms get_terms()}, except hide_empty defaults to false.
346
+ *
347
+ * The list of arguments that `$args` can contain, which will overwrite the defaults:
348
+ *
349
+ * * **orderby** - Default is 'name'. Can be name, count, term_group, slug or nothing
350
+ * (will use venue/term ID), Passing a custom value other than these will cause it to
351
+ * order based on the custom value.
352
+ * * **order** - Default is ASC. Can use DESC.
353
+ * * **hide_empty** - Default is 0 (false)
354
+ * * **exclude** - Default is an empty array. An array, comma- or space-delimited string
355
+ * of term ids to exclude from the return array. If 'include' is non-empty,
356
+ * 'exclude' is ignored.
357
+ * * **include** - Default is an empty array. An array, comma- or space-delimited string
358
+ * of term ids to include in the return array.
359
+ * * **number** - The maximum number of terms to return. Default is to return them all.
360
+ * * **offset** - The number by which to offset the terms query.
361
+ * * **fields** - Default is 'all', which returns an array of term objects.
362
+ * If 'fields' is 'ids' or 'names', returns an array of integers or strings, respectively.
363
+ * * **slug** - Returns terms whose "slug" matches this value. Default is empty string.
364
+ * * **search** - Returned terms' names will contain the value of 'search',
365
+ * * **case-insensitive**. Default is an empty string.
366
+ *
367
+ * ###Example
368
+ *
369
+ * $venues = eo_get_venues();
370
+ *
371
+ * if( $venues ){
372
+ * echo '<ul>';
373
+ * foreach($venues as $venue):
374
+ * $venue_id = (int) $venue->term_id;
375
+ * printf('<li> <a href="%s">%s</a>', eo_get_venue_link($venue_id), esc_html($venue->name));
376
+ * endforeach;
377
+ * echo '</ul>';
378
+ * }
379
+ *
380
+ * @uses get_terms()
381
+ * @link https://gist.github.com/3902494 Gist for creating an archive page of all the venues
382
+ * @link http://codex.wordpress.org/Function_Reference/get_terms get_terms()
383
+ * @since 1.0.0
384
+ *
385
+ * @param string|array $args The values of what to search for when returning venues
386
+ * @return array List of Term (venue) Objects
387
+ */
388
+ function eo_get_venues($args=array()){
389
+ $args = wp_parse_args( $args, array('hide_empty'=>0, 'fields'=>'all') );
390
+ $venues = get_terms('event-venue',$args);
391
+ if( $venues ){
392
+ //Ensure IDs are cast as integers {@link https://github.com/stephenh1988/Event-Organiser/issues/21}
393
+ if( $args['fields'] == 'ids' ){
394
+ $venues = array_map('intval', $venues);
395
+ }elseif( $args['fields'] == 'all' ){
396
+ foreach( $venues as $venue)
397
+ $venue->term_id = (int)$venue->term_id;
398
+ }
399
+ }
400
+ return $venues;
401
+ }
402
+
403
+
404
+ /**
405
+ * Updates new venue in the database.
406
+ *
407
+ * Calls {@see `wp_insert_term()`} to update the taxonomy term
408
+ * Updates venue meta data to database (for 'core' meta keys)
409
+ * The $args is an array - the same as that accepted by {@link http://codex.wordpress.org/Function_Reference/wp_update_term wp_update_term()}
410
+ * The $args array can also accept the following keys:
411
+ *
412
+ * * description
413
+ * * address
414
+ * * city
415
+ * * state
416
+ * * postcode
417
+ * * country
418
+ * * latitude
419
+ * * longtitude
420
+ *
421
+ * @since 1.4.0
422
+ *
423
+ * @uses wp_update_term() to update venue (taxonomy) term
424
+ * @uses do_action() Calls 'eventorganiser_save_venue' hook with the venue id
425
+ *
426
+ * @param int $venue_id The Term ID of the venue to update
427
+ * @param array $args Array as accepted by wp_update_term and including the 'core' metadata
428
+ * @return array|WP_Error Array of term ID and term-taxonomy ID or a WP_Error on error
429
+ */
430
+ function eo_update_venue($venue_id, $args=array()){
431
+
432
+ $term_args = array_intersect_key($args, array('name'=>'','term_id'=>'','term_group'=>'','term_taxonomy_id'=>'','alias_of'=>'','parent'=>0,'slug'=>'','count'=>''));
433
+ $meta_args = array_intersect_key($args, array('description'=>'','address'=>'','postcode'=>'','city'=>'','state'=>'','country'=>'','latitude'=>'','longtitude'=>''));
434
+ $venue_id = (int) $venue_id;
435
+
436
+
437
+ //Update taxonomy table
438
+ $resp = wp_update_term($venue_id,'event-venue', $term_args);
439
+
440
+ if( is_wp_error($resp) ){
441
+ return $resp;
442
+ }
443
+
444
+ $venue_id = (int) $resp['term_id'];
445
+
446
+ foreach( $meta_args as $key => $value ){
447
+ switch($key):
448
+ case 'latitude':
449
+ $meta_key = '_lat';
450
+ break;
451
+ case 'longtitude':
452
+ $meta_key = '_lng';
453
+ break;
454
+ default:
455
+ $meta_key = '_'.$key;
456
+ break;
457
+ endswitch;
458
+
459
+ $validated_value = eventorganiser_sanitize_meta($meta_key, $value);
460
+
461
+ update_metadata('eo_venue', $venue_id, $meta_key, $validated_value);
462
+ }
463
+ do_action('eventorganiser_save_venue',$venue_id);
464
+
465
+ return array('term_id' => $venue_id, 'term_taxonomy_id' => $resp['term_taxonomy_id']);
466
+ }
467
+
468
+
469
+ /**
470
+ * Adds a new venue to the database.
471
+ *
472
+ * Calls {@see `wp_insert_term()`} to create the taxonomy term
473
+ * Adds venue meta data to database (for 'core' meta keys)
474
+ *
475
+ * The $args is an array - the same as that accepted by {@link http://codex.wordpress.org/Function_Reference/wp_update_term wp_update_term()}
476
+ * The $args array can also accept the following keys:
477
+ *
478
+ * * description
479
+ * * address
480
+ * * city
481
+ * * state
482
+ * * postcode
483
+ * * country
484
+ * * latitude
485
+ * * longtitude
486
+ *
487
+ * @since 1.4.0
488
+ *
489
+ * @uses `wp_insert_term()` to create venue (taxonomy) term
490
+ * @uses do_action() Calls 'eventorganiser_insert_venue' hook with the venue id
491
+ * @uses do_action() Calls 'eventorganiser_save_venue' hook with the venue id
492
+ * @link http://codex.wordpress.org/Function_Reference/wp_insert_term wp_insert_term()
493
+ *
494
+ * @param string $name the venue to insert
495
+ * @param array $args Array as accepted by wp_update_term and including the 'core' metadata
496
+ * @return array|WP_Error Array of term ID and term-taxonomy ID or a WP_Error on error
497
+ */
498
+ function eo_insert_venue($name, $args=array()){
499
+ $term_args = array_intersect_key($args, array('name'=>'','term_id'=>'','term_group'=>'','term_taxonomy_id'=>'','alias_of'=>'','parent'=>0,'slug'=>'','count'=>''));
500
+ $meta_args = array_intersect_key($args, array('description'=>'','address'=>'','postcode'=>'','city'=>'','state'=>'','country'=>'','latitude'=>'','longtitude'=>''));
501
+
502
+ $resp = wp_insert_term($name,'event-venue',$term_args);
503
+
504
+ if(is_wp_error($resp)){
505
+ return $resp;
506
+ }
507
+
508
+ $venue_id = (int) $resp['term_id'];
509
+
510
+ foreach( $meta_args as $key => $value ){
511
+ switch($key):
512
+ case 'latitude':
513
+ $meta_key = '_lat';
514
+ break;
515
+ case 'longtitude':
516
+ $meta_key = '_lng';
517
+ break;
518
+ default:
519
+ $meta_key = '_'.$key;
520
+ break;
521
+ endswitch;
522
+
523
+ $validated_value = eventorganiser_sanitize_meta($meta_key, $value);
524
+
525
+ if( !empty($validated_value) )
526
+ add_metadata('eo_venue', $venue_id, $meta_key, $validated_value, true);
527
+ }
528
+
529
+ do_action('eventorganiser_insert_venue',$venue_id);
530
+ do_action('eventorganiser_save_venue',$venue_id);
531
+
532
+ return array('term_id' => $venue_id, 'term_taxonomy_id' => $resp['term_taxonomy_id']);
533
+ }
534
+
535
+ /**
536
+ * Deletes a venue in the database.
537
+ *
538
+ * Calls {@see `wp_delete_term()`} to delete the taxonomy term
539
+ * Deletes all the venue's meta
540
+ *
541
+ * @since 1.4.0
542
+ *
543
+ * @uses wp_delete_term to delete venue (taxonomy) term
544
+ * @uses do_action() Calls 'eventorganiser_delete_venue' hook with the venue id
545
+ *
546
+ * @param int $venue_id the Term ID of the venue to update
547
+ * @return bool|WP_Error false or error on failure. True after sucessfully deleting the venue and its meta data.
548
+ */
549
+ function eo_delete_venue($venue_id){
550
+ global $wpdb;
551
+ $resp =wp_delete_term( $venue_id, 'event-venue');
552
+ if( is_wp_error($resp) || false === $resp ){
553
+ return $resp;
554
+ }
555
+ $venue_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->eo_venuemeta WHERE eo_venue_id = %d ", $venue_id ));
556
+
557
+ if ( !empty($venue_meta_ids) ) {
558
+ $in_venue_meta_ids = "'" . implode("', '", $venue_meta_ids) . "'";
559
+ $wpdb->query( "DELETE FROM $wpdb->eo_venuemeta WHERE meta_id IN($in_venue_meta_ids)" );
560
+ }
561
+ do_action('eventorganiser_delete_venue',$venue_id);
562
+
563
+ return true;
564
+ }
565
+
566
+ /**
567
+ * Returns the mark-up for a Google map of the venue (and enqueues scripts).
568
+ * Accepts an arguments array corresponding to the attributes supported by the shortcode.
569
+ * @since 1.6
570
+ *
571
+ * @link http://www.stephenharris.info/2012/event-organiser-1-6-whats-new/ Examples of using eo_get_venue_map()
572
+ *
573
+ * @param mixed $venue_slug_or_id The venue ID as an integer. Or Slug as string. Uses venue of current event if empty.
574
+ * @return string The markup of the map. False is no venue found.
575
+ */
576
+ function eo_get_venue_map($venue_slug_or_id='', $args=array()){
577
+
578
+ //Cast as array to allow multi venue support
579
+ if( $venue_slug_or_id == '%all%' || is_array($venue_slug_or_id) && in_array('%all%',$venue_slug_or_id) ){
580
+ $all_venues = eo_get_venues();
581
+ if( $all_venues )
582
+ $venue_slug_or_id = array_map('intval',wp_list_pluck($all_venues,'term_id'));
583
+
584
+ }
585
+ if( !is_array($venue_slug_or_id) )
586
+ $venue_slug_or_id = array( $venue_slug_or_id );
587
+
588
+ $venue_ids = array_map('eo_get_venue_id_by_slugorid',$venue_slug_or_id);
589
+
590
+ //Map properties
591
+ $args = shortcode_atts( array(
592
+ 'zoom' => 15, 'scrollwheel'=>true, 'zoomcontrol'=>true, 'rotatecontrol'=>true,
593
+ 'pancontrol'=>true, 'overviewmapcontrol'=>true, 'streetviewcontrol'=>true,
594
+ 'maptypecontrol'=>true, 'draggable'=>true,'maptypeid' => 'ROADMAP',
595
+ 'width' => '100%','height' => '200px','class' => '',
596
+ 'tooltip'=>false
597
+ ), $args );
598
+
599
+ //Cast zoom as integer
600
+ $args['zoom'] = (int) $args['zoom'];
601
+
602
+ //Escape attributes
603
+ $width = esc_attr($args['width']);
604
+ $height = esc_attr($args['height']);
605
+ $class = esc_attr($args['class']);
606
+
607
+ $args['maptypeid'] = strtoupper($args['maptypeid']);
608
+
609
+ //If class is selected use that style, otherwise use specified height and width
610
+ if( !empty($class) ){
611
+ $class .= " eo-venue-map googlemap";
612
+ $style = "";
613
+ }else{
614
+ $class = "eo-venue-map googlemap";
615
+ $style = "style='height:".$height.";width:".$width.";' ";
616
+ }
617
+
618
+ $venue_ids = array_filter($venue_ids);
619
+
620
+ if( empty($venue_ids) )
621
+ return false;
622
+
623
+ //Set up venue locations for map
624
+ foreach( $venue_ids as $venue_id ){
625
+
626
+ //Venue lat/lng array
627
+ $latlng = eo_get_venue_latlng($venue_id);
628
+
629
+ //Venue tooltip description
630
+ $tooltip_content = '<strong>'.eo_get_venue_name($venue_id).'</strong>';
631
+ $address = array_filter(eo_get_venue_address($venue_id));
632
+ if( !empty($address) )
633
+ $tooltip_content .='</br>'.implode(', ',$address);
634
+
635
+ $tooltip_content = apply_filters('eventorganiser_venue_tooltip',$tooltip_content,$venue_id);
636
+
637
+ $locations[] =array('lat'=>$latlng['lat'],'lng'=>$latlng['lng'], 'tooltipContent'=>$tooltip_content);
638
+ }
639
+
640
+ //This could be improved
641
+ EventOrganiser_Shortcodes::$map[] = array_merge($args, array('locations'=>$locations) );
642
+ EventOrganiser_Shortcodes::$add_script = true;
643
+ $id = count(EventOrganiser_Shortcodes::$map);
644
+
645
+ return "<div class='".$class."' id='eo_venue_map-{$id}' ".$style."></div>";
646
+ }
647
+
648
+
649
+ /**
650
+ * Retrieve post meta field for a venue.
651
+ *
652
+ * This function returns the values of the venue meta with the specified key from the specified venue. (Specified by the venue ID - the taxonomy term ID).
653
+ *
654
+ * @since 1.5.0
655
+ * @link http://wp-event-organiser.com/documentation/developers/venue-meta-data-and-metaboxes/ How to create custom fields for venues
656
+ *
657
+ * @param int $venue_id Venue (term) ID.
658
+ * @param string $key Optional. The meta key to retrieve. By default, returns data for all keys.
659
+ * @param bool $single Whether to return a single value.
660
+ * @return mixed Will be an array if $single is false. Will be value of meta data field if $single
661
+ * is true.
662
+ */
663
+ function eo_get_venue_meta($venue_id, $key ='', $single=true){
664
+ return get_metadata('eo_venue', $venue_id, $key, $single);
665
+ }
666
+
667
+
668
+ /**
669
+ * Add meta data field to a venue
670
+ *
671
+ * You should avoid the following 'core' keys:
672
+ *
673
+ * * _description
674
+ * * _address
675
+ * * _city
676
+ * * _state
677
+ * * _postcode
678
+ * * _country
679
+ * * _latitude
680
+ * * _longtitude
681
+ *
682
+ * It is **strongly** recommended that you prefix your keys with and underscore.
683
+ *
684
+ * @since 1.5.0
685
+ * @link http://wp-event-organiser.com/documentation/developers/venue-meta-data-and-metaboxes/ How to create custom fields for venues
686
+ *
687
+ * @param int $venue_id Venue (term) ID.
688
+ * @param string $key Metadata name.
689
+ * @param mixed $value Metadata value.
690
+ * @param bool $unique Optional, default is false. Whether the same key should not be added.
691
+ * @return bool False for failure. True for success.
692
+ */
693
+ function eo_add_venue_meta($venue_id, $key, $value, $unique = false ){
694
+ return add_metadata('eo_venue',$venue_id, $key, $value, $unique);
695
+ }
696
+
697
+ /**
698
+ * Update venue meta field based on venue (term) ID.
699
+ *
700
+ * Use the $prev_value parameter to differentiate between meta fields with the
701
+ * same key and venue ID. This may be used in place of {@see `eo_add_venue_meta()`} function. The first thing this function will do is make sure that `$meta_key` already exists on `$venue_id`. If it does not, `add_post_meta($venue_id, $meta_key, $meta_value)` is called instead and its result is returned. Returns meta_id if the meta doesn't exist, otherwise returns true on success and false on failure.
702
+ *
703
+ * If the meta field for the venue does not exist, it will be added.
704
+ *
705
+ * You should avoid the following 'core' keys:
706
+ *
707
+ * * _description
708
+ * * _address
709
+ * * _city
710
+ * * _state
711
+ * * _postcode
712
+ * * _country
713
+ * * _latitude
714
+ * * _longtitude
715
+ *
716
+ * It is **strongly** recommended that you prefix your keys with and underscore.
717
+ *
718
+ * @since 1.5.0
719
+ * @link http://wp-event-organiser.com/documentation/developers/venue-meta-data-and-metaboxes/ How to create custom fields for venues
720
+ *
721
+ * @param int $venue_id Venue (term) ID.
722
+ * @param string $key Metadata key.
723
+ * @param mixed $value Metadata value.
724
+ * @param mixed $prev_value Optional. Previous value to check before removing.
725
+ * @return bool False on failure, true if success.
726
+ */
727
+ function eo_update_venue_meta($venue_id, $key, $value, $prev_value=''){
728
+ return update_metadata('eo_venue', $venue_id, $key, $value, $prev_value);
729
+ }
730
+
731
+ /**
732
+ * Remove metadata matching criteria from a venue.
733
+ *
734
+ * You can match based on the key, or key and value. Removing based on key and
735
+ * value, will keep from removing duplicate metadata with the same key. It also
736
+ * allows removing all metadata matching key, if needed.
737
+ *
738
+ * @since 1.5.0
739
+ * @link http://wp-event-organiser.com/documentation/developers/venue-meta-data-and-metaboxes/ How to create custom fields for venues
740
+ *
741
+ * @param int $venue_id Venue (term) ID.
742
+ * @param string $key Metadata name.
743
+ * @param mixed $value Optional. Metadata value.
744
+ * @return bool False for failure. True for success.
745
+ */
746
+ function eo_delete_venue_meta($venue_id, $key, $value = '', $delete_all = false ){
747
+ return delete_metadata('eo_venue',$venue_id, $key, $value, $delete_all);
748
+ }
749
+
750
+ /**
751
+ * Sanitizes (or validates) the metadata (expects raw) before being inserted into the databse.
752
+ *
753
+ * @since 1.4.0
754
+ * @access private
755
+ *
756
+ * @param string The key of the meta data
757
+ * @param mixed The meta data being validated.
758
+ * @return mixed The validated value. False if the key is not recognised.
759
+ */
760
+ function eventorganiser_sanitize_meta($key,$value){
761
+
762
+ switch($key):
763
+ case '_description':
764
+ $value = wp_filter_post_kses($value);
765
+ break;
766
+ case '_lat':
767
+ case '_lng':
768
+ //Cast as float and then string: make sure string uses . not , for decimal point
769
+ $value = floatval($value);
770
+ $value = number_format($value, 6);
771
+ break;
772
+ default:
773
+ $address_keys = _eventorganiser_get_venue_address_fields();
774
+ if( isset($address_keys[$key]) )
775
+ $value = sanitize_text_field($value);
776
+ else
777
+ $value = false;
778
+ endswitch;
779
+
780
+ return $value;
781
+ }
782
+
783
+ /**
784
+ *@ignore
785
+ *@access private
786
+ */
787
+ function _eventorganiser_get_venue_address_fields(){
788
+ //Keys *must* be prefixed by a '_'.
789
+ $address_fields = array(
790
+ '_address'=> __('Address','eventorganiser'),
791
+ '_city'=> __('City','eventorganiser'),
792
+ '_state'=> __('State / Province','eventorganiser'),
793
+ '_postcode'=> __('Post Code','eventorganiser'),
794
+ '_country'=> __('Country','eventorganiser'),
795
+ );
796
+
797
+ return apply_filters('eventorganiser_venue_address_fields', $address_fields);
798
+ }
799
+
800
+
801
+ /**
802
+ *@ignore
803
+ *@access private
804
+ */
805
+ function eventorganiser_venue_dropdown($post_id=0,$args){
806
+ $venues = get_terms('event-venue', array('hide_empty'=>false));
807
+ $current = (int) eo_get_venue($post_id);
808
+
809
+ $id = (!empty($args['id']) ? 'id="'.esc_attr($args['id']).'"' : '');
810
+ $name = (!empty($args['name']) ? 'name="'.esc_attr($args['name']).'"' : '');
811
+ ?>
812
+ <select <?php echo $id.' '.$name; ?>>
813
+ <option><?php _e("Select a venue",'eventorganiser');?></option>
814
+ <?php foreach ($venues as $venue):?>
815
+ <option <?php selected($venue->term_id,$current);?> value="<?php echo $venue->term_id;?>"><?php echo $venue->name; ?></option>
816
+ <?php endforeach;?>
817
+ </select><?php
818
+ }
819
+ /**
820
+ *@ignore
821
+ *@access private
822
+ */
823
+ function eo_event_venue_dropdown( $args = '' ) {
824
+ $defaults = array(
825
+ 'show_option_all' =>'',
826
+ 'echo' => 1,
827
+ 'selected' => 0,
828
+ 'name' => 'event-venue',
829
+ 'id' => '',
830
+ 'class' => 'postform event-organiser event-venue-dropdown event-dropdown',
831
+ 'tab_index' => 0,
832
+ );
833
+
834
+ $defaults['selected'] = (is_tax('event-venue') ? get_query_var('event-venue') : 0);
835
+ $r = wp_parse_args( $args, $defaults );
836
+ $r['taxonomy']='event-venue';
837
+ extract( $r );
838
+
839
+ $tab_index_attribute = '';
840
+ if ( (int) $tab_index > 0 )
841
+ $tab_index_attribute = " tabindex=\"$tab_index\"";
842
+
843
+ $categories = get_terms($taxonomy, $r );
844
+ $name = esc_attr( $name );
845
+ $class = esc_attr( $class );
846
+ $id = $id ? esc_attr( $id ) : $name;
847
+
848
+ $output = "<select style='width:150px' name='$name' id='$id' class='$class' $tab_index_attribute>\n";
849
+
850
+ if ( $show_option_all ) {
851
+ $output .= '<option '.selected($selected,0,false).' value="0">'.$show_option_all.'</option>';
852
+ }
853
+
854
+ if ( ! empty( $categories ) ) {
855
+ foreach ($categories as $term):
856
+ $output .= '<option value="'.$term->slug.'"'.selected($selected,$term->slug,false).'>'.$term->name.'</option>';
857
+ endforeach;
858
+ }
859
+ $output .= "</select>\n";
860
+
861
+ if ( $echo )
862
+ echo $output;
863
+
864
+ return $output;
865
+ }
866
+
867
+ ?>
includes/event.php ADDED
@@ -0,0 +1,715 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *@package event-functions
5
+ */
6
+ /**
7
+ * This functions updates a post of event type, with data given in the $post_data
8
+ * and event data given in $event_data. Returns the post_id.
9
+ *
10
+ * Triggers {@see `eventorganiser_save_event`} passing event (post) ID
11
+ *
12
+ * The event data array can contain
13
+ *
14
+ * * `schedule` => (custom | once | daily | weekly | monthly | yearly) -- specifies the reoccurrence pattern
15
+ * * `schedule_meta` =>
16
+ * * For monthly schedules,
17
+ * * (string) BYMONTHDAY=XX to repeat on XXth day of month, e.g. BYMONTHDAY=01 to repeat on the first of every month.
18
+ * * (string) BYDAY=ND. N= 1|2|3|4|-1 (first, second, third, fourth, last). D is day of week SU|MO|TU|WE|TH|FR|SA. E.g. BYDAY=2TU (repeat on second tuesday)
19
+ * * For weekly schedules,
20
+ * * (array) Days to repeat on: (SU,MO,TU,WE,TH,FR,SA). e.g. set to array('SU','TU') to repeat on Tuesdays & Sundays.
21
+ * * Can be left blank to repeat weekly from the start date.
22
+ * * `frequency` => (int) positive integer, sets frequency of reoccurrence (every 2 days, or every 3 days etc)
23
+ * * `all_day` => 1 if its an all day event, 0 if not
24
+ * * `start` => start date (of first occurrence) as a datetime object
25
+ * * `end` => end date (of first occurrence) as a datetime object
26
+ * * `schedule_last` => **START** date of last occurrence (or upper-bound thereof) as a datetime object
27
+ * * `include` => array of datetime objects to include in the schedule
28
+ * * `exclude` => array of datetime objects to exclude in the schedule
29
+ *
30
+ * @since 1.5
31
+ * @uses wp_insert_post()
32
+ *
33
+ * @param int $post_id - the event (post) ID for the event you want to update
34
+ * @param array $event_data - array of event data
35
+ * @param array $post_data - array of data to be used by wp_update_post.
36
+ * @return int $post_id - the post ID of the updated event
37
+ */
38
+ function eo_update_event($post_id, $event_data=array(), $post_data=array() ){
39
+
40
+ $post_id = (int) $post_id;
41
+
42
+ if( empty($post_id) )
43
+ return new WP_Error('eo_error','Empty post ID.');
44
+
45
+ if( !empty($event_data['venue']) || !empty($event_data['category']) ){
46
+ $post_data['taxonomy']['event-venue'] = isset($event_data['venue']) ? $event_data['venue'] : null;
47
+ $post_data['taxonomy']['event-category'] = isset($event_data['category']) ? $event_data['category'] : null;
48
+ unset($event_data['venue']);
49
+ unset($event_data['category']);
50
+ }
51
+
52
+ $event_data = apply_filters( 'eventorganiser_update_event_event_data', $event_data, $post_id, $post_data, $event_data );
53
+ $post_data = apply_filters( 'eventorganiser_update_event_post_data', $post_data, $post_id, $post_data, $event_data );
54
+
55
+ if( !empty($post_data) ){
56
+ $post_data['ID'] = $post_id;
57
+ wp_update_post( $post_data );
58
+ }
59
+
60
+ //Get previous data, parse with data to be updated
61
+ $prev = eo_get_event_schedule($post_id);
62
+ $event_data = wp_parse_args( $event_data, $prev );
63
+
64
+ //If schedule is 'once' and dates are included - set to 'custom':
65
+ if( ( empty($event_data['schedule']) || 'once' == $event_data['schedule'] ) && !empty($event_data['include']) ){
66
+ $event_data['schedule'] = 'custom';
67
+ }
68
+
69
+ //Do we need to delete existing dates from db?
70
+ $delete_existing = false;
71
+ $diff = array();
72
+ if( $prev ){
73
+ foreach ( $prev as $key => $prev_value ){
74
+ if( $event_data[$key] != $prev_value ){
75
+ if('monthly' == $event_data['schedule'] && $key =='schedule_meta'){
76
+ if( $event_data['occurs_by'] != $prev['occurs_by'] ){
77
+ $diff[]=$key;
78
+ $delete_existing = true;
79
+ break;
80
+ }
81
+ }else{
82
+
83
+ //If one off event / custom, don't worry about 'schedule_last'
84
+ if( $key == 'schedule_last' && in_array( $event_data['schedule'], array( 'once', 'custom' ) ) )
85
+ continue;
86
+
87
+ $diff[]=$key;
88
+ $delete_existing = true;
89
+ break;
90
+ }
91
+ }
92
+ }
93
+ }
94
+
95
+ //Need to replace occurrences
96
+ if( $delete_existing || !empty( $event_data['force_regenerate_dates'] ) ){
97
+ //Generate occurrences
98
+ $event_data = _eventorganiser_generate_occurrences($event_data);
99
+
100
+ if( is_wp_error($event_data) )
101
+ return $event_data;
102
+
103
+ //Delete old dates
104
+ eo_delete_event_occurrences($post_id);
105
+
106
+ //Insert new ones and update meta
107
+ $re = _eventorganiser_insert_occurrences($post_id,$event_data);
108
+ }
109
+
110
+ do_action( 'eventorganiser_save_event', $post_id );
111
+ return $post_id;
112
+ }
113
+
114
+
115
+ /**
116
+ * This functions inserts a post of event type, with data given in the $post_data
117
+ * and event data given in $event_data. Returns the post ID.
118
+ *
119
+ * Triggers {@see `eventorganiser_save_event`} passing event (post) ID
120
+ *
121
+ * The event data array can contain
122
+ *
123
+ * * `schedule` => (custom | once | daily | weekly | monthly | yearly) -- specifies the reoccurrence pattern
124
+ * * `schedule_meta` =>
125
+ * * For monthly schedules,
126
+ * * (string) BYMONTHDAY=XX to repeat on XXth day of month, e.g. BYMONTHDAY=01 to repeat on the first of every month.
127
+ * * (string) BYDAY=ND. N= 1|2|3|4|-1 (first, second, third, fourth, last). D is day of week SU|MO|TU|WE|TH|FR|SA. E.g. BYDAY=2TU (repeat on second tuesday)
128
+ * * For weekly schedules,
129
+ * * (array) Days to repeat on: (SU,MO,TU,WE,TH,FR,SA). e.g. set to array('SU','TU') to repeat on Tuesdays & Sundays.
130
+ * * Can be left blank to repeat weekly from the start date.
131
+ * * `frequency` => (int) positive integer, sets frequency of reoccurrence (every 2 days, or every 3 days etc)
132
+ * * `all_day` => 1 if its an all day event, 0 if not
133
+ * * `start` => start date (of first occurrence) as a datetime object
134
+ * * `end` => end date (of first occurrence) as a datetime object
135
+ * * `schedule_last` => **START** date of last occurrence (or upper-bound thereof) as a datetime object
136
+ * * `include` => array of datetime objects to include in the schedule
137
+ * * `exclude` => array of datetime objects to exclude in the schedule
138
+ *
139
+ * @since 1.5
140
+ * @link http://www.stephenharris.info/2012/front-end-event-posting/ Tutorial on front-end event posting
141
+ * @uses wp_insert_post()
142
+ *
143
+ * @param array $post_data array of data to be used by wp_insert_post.
144
+ * @param array $event_data array of event data
145
+ * @return int the post ID of the updated event
146
+ */
147
+ function eo_insert_event($post_data=array(),$event_data=array()){
148
+ global $wpdb;
149
+
150
+ if( !empty($event_data['venue'] ) ){
151
+ $post_data['tax_input']['event-venue'] = $event_data['venue'];
152
+ unset($event_data['venue']);
153
+ }
154
+ if( !empty($event_data['category'] ) ){
155
+ $post_data['tax_input']['event-category'] = $event_data['category'];
156
+ unset($event_data['category']);
157
+ }
158
+
159
+ //If schedule is 'once' and dates are included - set to 'custom':
160
+ if( ( empty($event_data['schedule']) || 'once' == $event_data['schedule'] ) && !empty($event_data['include']) ){
161
+ $event_data['schedule'] = 'custom';
162
+ }
163
+
164
+ $event_data = _eventorganiser_generate_occurrences($event_data);
165
+
166
+ if( is_wp_error($event_data) )
167
+ return $event_data;
168
+
169
+ $event_data = apply_filters( 'eventorganiser_insert_event_event_data', $event_data, $post_data, $event_data );
170
+ $post_data = apply_filters( 'eventorganiser_insert_event_post_data', $post_data, $post_data, $event_data );
171
+
172
+ //Finally we create event (first create the post in WP)
173
+ $post_input = array_merge(array('post_title'=>'untitled event'), $post_data, array('post_type'=>'event'));
174
+ $post_id = wp_insert_post($post_input, true);
175
+
176
+ //Did the event insert correctly?
177
+ if ( is_wp_error( $post_id) )
178
+ return $post_id;
179
+
180
+ _eventorganiser_insert_occurrences($post_id, $event_data);
181
+
182
+ //Action used to break cache & trigger Pro actions (& by other plug-ins?)
183
+ do_action( 'eventorganiser_save_event', $post_id );
184
+ return $post_id;
185
+ }
186
+
187
+ /**
188
+ * Deletes all occurrences for an event (removes them from the eo_events table).
189
+ * Triggers {@see `eventorganiser_delete_event`} (this action is used to break the caches).
190
+ * @since 1.5
191
+ *
192
+ * @param int $post_id the event's (post) ID to be deleted
193
+ */
194
+ function eo_delete_event_occurrences($post_id){
195
+ global $wpdb;
196
+
197
+ do_action( 'eventorganiser_delete_event', $post_id ); //Deprecated - do not use!
198
+ do_action( 'eventorganiser_delete_event_occurrences', $post_id );
199
+ $del = $wpdb->get_results($wpdb->prepare("DELETE FROM $wpdb->eo_events WHERE post_id=%d",$post_id));
200
+ }
201
+ add_action( 'delete_post', 'eo_delete_event_occurrences', 10 );
202
+
203
+ /**
204
+ * This is a private function - handles the insertion of dates into the database. Use eo_insert_event or eo_update_event instead.
205
+ * @access private
206
+ * @ignore
207
+ *
208
+ * @param int $post_id The post ID of the event
209
+ * @param array $event_data Array of event data, including schedule meta (saved as post meta), duration and occurrences
210
+ * @return int $post_id
211
+ */
212
+ function _eventorganiser_insert_occurrences($post_id, $event_data){
213
+ global $wpdb;
214
+ extract($event_data);
215
+
216
+ //Get duration
217
+ $duration=false;
218
+ if( function_exists('date_diff') ){
219
+ $duration = date_diff($start,$end);
220
+
221
+ /* Storing a DateInterval object can cause errors. Serialize it.
222
+ Thanks to Mathieu Parisot, Mathias & Dave Page */
223
+ $event_data['duration'] = maybe_serialize( $duration );
224
+ }
225
+
226
+ //Work around for PHP < 5.3
227
+ $seconds = round(abs($start->format('U') - $end->format('U')));
228
+ $days = floor($seconds/86400);// 86400 = 60*60*24 seconds in a normal day
229
+ $sec_diff = $seconds - $days*86400;
230
+ $duration_str = '+'.$days.'days '.$sec_diff.' seconds';
231
+ $event_data['duration_str'] =$duration_str;
232
+
233
+ $occurrence_array = array();
234
+
235
+ foreach( $occurrences as $counter=> $occurrence ):
236
+ $occurrence_end = clone $occurrence;
237
+ if( $duration ){
238
+ $occurrence_end->add($duration);
239
+ }else{
240
+ $occurrence_end->modify($duration_str);
241
+ }
242
+
243
+ $occurrence_input =array(
244
+ 'post_id'=>$post_id,
245
+ 'StartDate'=>$occurrence->format('Y-m-d'),
246
+ 'StartTime'=>$occurrence->format('H:i:s'),
247
+ 'EndDate'=>$occurrence_end->format('Y-m-d'),
248
+ 'FinishTime'=>$end->format('H:i:s'),
249
+ 'event_occurrence' => $counter,
250
+ );
251
+
252
+ $wpdb->insert($wpdb->eo_events, $occurrence_input);
253
+ $occurrence_array[$wpdb->insert_id] = $occurrence->format('Y-m-d H:i:s');
254
+
255
+ //Add to occurrence cache: TODO use post meta
256
+ $occurrence_cache[$wpdb->insert_id] = array(
257
+ 'start' =>$occurrence,
258
+ 'end' => new DateTime($occurrence_end->format('Y-m-d').' '.$end->format('H:i:s'), eo_get_blog_timezone())
259
+ );
260
+ endforeach;
261
+
262
+ //Set occurrence cache
263
+ wp_cache_set( 'eventorganiser_occurrences_'.$post_id, $occurrence_cache );
264
+
265
+ unset($event_data['occurrences']);
266
+ $event_data['_occurrences'] = $occurrence_array;
267
+
268
+ if( !empty($include) )
269
+ $event_data['include'] = array_map('eo_format_datetime', $include, array_fill(0, count($include), 'Y-m-d H:i:s') );
270
+ if( !empty($exclude) )
271
+ $event_data['exclude'] = array_map('eo_format_datetime', $exclude, array_fill(0, count($exclude), 'Y-m-d H:i:s') );
272
+
273
+ unset($event_data['start']);
274
+ unset($event_data['end']);
275
+ unset($event_data['schedule_start']);
276
+ unset($event_data['schedule_last']);
277
+
278
+ update_post_meta( $post_id,'_eventorganiser_event_schedule', $event_data);
279
+ update_post_meta( $post_id,'_eventorganiser_schedule_start_start', $start->format('Y-m-d H:i:s'));
280
+ update_post_meta( $post_id,'_eventorganiser_schedule_start_finish', $end->format('Y-m-d H:i:s'));
281
+ update_post_meta( $post_id,'_eventorganiser_schedule_last_start', $occurrence->format('Y-m-d H:i:s'));
282
+ update_post_meta( $post_id,'_eventorganiser_schedule_last_finish', $occurrence_end->format('Y-m-d H:i:s'));
283
+ return $post_id;
284
+ }
285
+
286
+
287
+ /**
288
+ * Gets schedule meta from the database (post meta)
289
+ * Datetimes are converted to DateTime objects, in blog's currenty timezone
290
+ *
291
+ * Event details include
292
+ *
293
+ * * `schedule` => (custom | once | daily | weekly | monthly | yearly) -- specifies the reoccurrence pattern
294
+ * * `schedule_meta` =>
295
+ * * For monthly schedules,
296
+ * * (string) BYMONTHDAY=XX to repeat on XXth day of month, e.g. BYMONTHDAY=01 to repeat on the first of every month.
297
+ * * (string) BYDAY=ND. N= 1|2|3|4|-1 (first, second, third, fourth, last). D is day of week SU|MO|TU|WE|TH|FR|SA. E.g. BYDAY=2TU (repeat on second tuesday)
298
+ * * For weekly schedules,
299
+ * * (array) Days to repeat on: (SU,MO,TU,WE,TH,FR,SA). e.g. set to array('SU','TU') to repeat on Tuesdays & Sundays.
300
+ * * `occurs_by` - For use with monthly schedules: how the event reoccurs: BYDAY or BYMONTHDAY
301
+ * * `frequency` => (int) positive integer, sets frequency of reoccurrence (every 2 days, or every 3 days etc)
302
+ * * `all_day` => 1 if its an all day event, 0 if not
303
+ * * `start` => start date (of first occurrence) as a datetime object
304
+ * * `end` => end date (of first occurrence) as a datetime object
305
+ * * `schedule_last` => **START** date of last occurrence as a datetime object
306
+ * * `include` => array of datetime objects to include in the schedule
307
+ * * `exclude` => array of datetime objects to exclude in the schedule
308
+ *
309
+ * @param int $post_id - The post ID of the event
310
+ * @return array event schedule details
311
+ */
312
+ function eo_get_event_schedule( $post_id=0 ){
313
+
314
+ $post_id = (int) ( empty($post_id) ? get_the_ID() : $post_id);
315
+
316
+ if( empty($post_id) )
317
+ return false;
318
+
319
+ $event_details = get_post_meta( $post_id,'_eventorganiser_event_schedule',true);
320
+ $event_details = wp_parse_args($event_details, array(
321
+ 'schedule'=>'once',
322
+ 'schedule_meta'=>'',
323
+ 'frequency'=>1,
324
+ 'all_day'=>0,
325
+ 'duration_str'=>'',
326
+ 'include'=>array(),
327
+ 'exclude'=>array(),
328
+ '_occurrences'=>array(),
329
+ ));
330
+
331
+ $tz = eo_get_blog_timezone();
332
+ $event_details['start'] = new DateTime(get_post_meta( $post_id,'_eventorganiser_schedule_start_start', true), $tz);
333
+ $event_details['end'] = new DateTime(get_post_meta( $post_id,'_eventorganiser_schedule_start_finish', true), $tz);
334
+ $event_details['schedule_start'] = clone $event_details['start'];
335
+ $event_details['schedule_last'] = new DateTime(get_post_meta( $post_id,'_eventorganiser_schedule_last_start', true), $tz);
336
+ $event_details['schedule_finish'] = new DateTime(get_post_meta( $post_id,'_eventorganiser_schedule_last_finish', true), $tz);
337
+
338
+ if( !empty($event_details['_occurrences']) ){
339
+ $event_details['_occurrences'] = array_map('eventorganiser_date_create', $event_details['_occurrences']);
340
+ }
341
+
342
+ if( !empty($event_details['include']) ){
343
+ $event_details['include'] = array_map('eventorganiser_date_create', $event_details['include'] );
344
+ }
345
+ if( !empty($event_details['exclude']) ){
346
+ $event_details['exclude'] = array_map('eventorganiser_date_create',$event_details['exclude'] );
347
+ }
348
+
349
+ if($event_details['schedule'] == 'weekly'){
350
+ $event_details['occurs_by'] = '';
351
+ }elseif($event_details['schedule'] == 'monthly'){
352
+ $bymonthday = preg_match('/BYMONTHDAY=/',$event_details['schedule_meta']);
353
+ $event_details['occurs_by'] = ($bymonthday ? 'BYMONTHDAY' : 'BYDAY');
354
+ }else{
355
+ $event_details['occurs_by'] ='';
356
+ }
357
+
358
+ return apply_filters( 'eventorganiser_get_event_schedule', $event_details, $post_id );
359
+ }
360
+
361
+
362
+ /**
363
+ * This is a private function - handles the generation of occurrence dates from the schedule data
364
+ * @access private
365
+ * @ignore
366
+ *
367
+ * @param array $event_data - Array containing the event's schedule data
368
+ * @return array $event_data - Array containing the event's schedule data including 'occurrences', an array of DateTimes
369
+ */
370
+ function _eventorganiser_generate_occurrences( $event_data=array() ){
371
+
372
+ $event_defaults = array(
373
+ 'start'=>'',
374
+ 'end'=>'',
375
+ 'all_day'=>0,
376
+ 'schedule'=>'once',
377
+ 'schedule_meta'=>'',
378
+ 'frequency'=>1,
379
+ 'schedule_last'=>'',
380
+ 'exclude'=>array(),
381
+ 'include'=>array(),
382
+ );
383
+ extract(wp_parse_args($event_data, $event_defaults));
384
+
385
+ $occurrences =array(); //occurrences array
386
+
387
+ //Make sure the same doesn't appear in both $include/$exclude. Is this really needed?
388
+ $exclude = array_udiff($exclude, $include, '_eventorganiser_compare_dates');
389
+ $include = array_udiff($include, $exclude, '_eventorganiser_compare_dates');
390
+
391
+ //Check dates are supplied and are valid
392
+ if( !($start instanceof DateTime) )
393
+ return new WP_Error('eo_error',__('Start date not provided.','eventorganiser'));
394
+
395
+ if( !($end instanceof DateTime) )
396
+ $end = clone $start;
397
+
398
+ if( !($schedule_last instanceof DateTime) )
399
+ $schedule_last = clone $start;
400
+
401
+ //Check dates are in chronological order
402
+ if($end < $start)
403
+ return new WP_Error('eo_error',__('Start date occurs after end date.','eventorganiser'));
404
+
405
+ if($schedule_last < $start)
406
+ return new WP_Error('eo_error',__('Schedule end date is before is before the start date.','eventorganiser'));
407
+
408
+ //Now set timezones
409
+ $timezone = eo_get_blog_timezone();
410
+ $start->setTimezone($timezone);
411
+ $end->setTimezone($timezone);
412
+ $schedule_last->setTimezone($timezone);
413
+ $H = intval($start->format('H'));
414
+ $i = intval($start->format('i'));
415
+
416
+ //White list schedule
417
+ if( !in_array($schedule, array('once','daily','weekly','monthly','yearly','custom')) )
418
+ return new WP_Error('eo_error',__('Schedule not recognised.','eventorganiser'));
419
+
420
+ //Ensure event frequency is a positive integer. Else set to 1.
421
+ $frequency = max(absint($frequency),1);
422
+ $all_day = (int) $all_day ;
423
+
424
+ $start_days =array();
425
+ $workaround='';
426
+ $icaldays = array('SU','MO','TU','WE','TH','FR','SA');
427
+ $weekdays = array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday');
428
+ $ical2day = array('SU'=>'Sunday','MO'=>'Monday','TU'=>'Tuesday',
429
+ 'WE'=>'Wednesday','TH'=>'Thursday','FR'=>'Friday','SA'=>'Saturday',);
430
+
431
+ //Set up schedule
432
+ switch( $schedule ) :
433
+ case 'once':
434
+ case 'custom':
435
+ $frequency =1;
436
+ $schedule_meta ='';
437
+ $schedule_start = clone $start;
438
+ $schedule_last = clone $start;
439
+ $start_days[] = clone $start;
440
+ $workaround = 'once';//Not strictly a workaround.
441
+ break;
442
+
443
+ case 'daily':
444
+ $interval = "+".$frequency."day";
445
+ $start_days[] = clone $start;
446
+ break;
447
+
448
+ case 'weekly':
449
+ $schedule_meta = array_filter($schedule_meta);
450
+ if( !empty($schedule_meta) && is_array($schedule_meta) ):
451
+ foreach ($schedule_meta as $day):
452
+ $start_day = clone $start;
453
+ $start_day->modify($ical2day[$day]);
454
+ $start_days[] = $start_day;
455
+ endforeach;
456
+ else:
457
+ $start_days[] = clone $start;
458
+ endif;
459
+
460
+ $interval = "+".$frequency."week";
461
+ break;
462
+
463
+ case 'monthly':
464
+ $start_days[] = clone $start;
465
+ $rule_value = explode('=',$schedule_meta,2);
466
+ $rule =$rule_value[0];
467
+ $values = explode(',',$rule_value[1]);//Should only be one value, but may support more in future
468
+ $values = array_filter($values);
469
+
470
+ if( $rule=='BYMONTHDAY' ):
471
+ $date = (int) $start_days[0]->format('d');
472
+ $interval = "+".$frequency."month";
473
+
474
+ if($date >= 29)
475
+ $workaround = 'short months'; //This case deals with 29/30/31 of month
476
+
477
+ $schedule_meta = 'BYMONTHDAY='.$date;
478
+
479
+ else:
480
+ if( empty($values) ){
481
+ $date = (int) $start_days[0]->format('d');
482
+ $n = ceil($date/7); // nth weekday of month.
483
+ $day_num = intval($start_days[0]->format('w')); //0 (Sun) - 6(Sat)
484
+
485
+ }else{
486
+ //expect e.g. array( 2MO )
487
+ preg_match('/^(-?\d{1,2})([a-zA-Z]{2})/' ,$values[0],$matches);
488
+ $n=(int) $matches[1];
489
+ $day_num = array_search($matches[2],$icaldays);//(Sun) - 6(Sat)
490
+ }
491
+
492
+ if($n==5) $n= -1; //If 5th, interpret it as last.
493
+ $ordinal = array('1'=>"first",'2'=>"second",'3'=>"third",'4'=>"fourth",'-1'=>"last");
494
+
495
+ if( !isset($ordinal[$n]) )
496
+ return new WP_Error('eo_error',__('Invalid monthly schedule (invalid ordinal)','eventorganiser'));
497
+
498
+ $ical_day = $icaldays[$day_num]; //ical day from day_num (SU - SA)
499
+ $day = $weekdays[$day_num];//Full day name from day_num (Sunday -Monday)
500
+ $schedule_meta = 'BYDAY='.$n.$ical_day; //E.g. BYDAY=2MO
501
+ $interval = $ordinal[$n].' '.$day.' of +'.$frequency.' month'; //E.g. second monday of +1 month
502
+
503
+ //Work around for PHP <5.3
504
+ if(!function_exists('date_diff')){
505
+ $workaround = 'php5.2';
506
+ }
507
+ endif;
508
+ break;
509
+
510
+ case 'yearly':
511
+ $start_days[] = clone $start;
512
+ if( '29-02' == $start_days[0]->format('d-m') )
513
+ $workaround = 'leap year';
514
+
515
+ $interval = "+".$frequency."year";
516
+ break;
517
+ endswitch; //End $schedule switch
518
+
519
+
520
+ //Now we have setup and validated the schedules - loop through and generate occurrences
521
+ foreach($start_days as $index => $start_day):
522
+ $current = clone $start_day;
523
+
524
+ switch($workaround):
525
+ //Not really a workaround. Just add the occurrence and finish.
526
+ case 'once':
527
+ $current->setTime($H,$i );
528
+ $occurrences[] = clone $current;
529
+ break;
530
+
531
+ //Loops for monthly events that require php5.3 functionality
532
+ case 'php5.2':
533
+ while( $current <= $schedule_last ):
534
+ $current->setTime($H,$i );
535
+ $occurrences[] = clone $current;
536
+ $current = _eventorganiser_php52_modify($current,$interval);
537
+ endwhile;
538
+ break;
539
+
540
+ //Loops for monthly events on the 29th/30th/31st
541
+ case 'short months':
542
+ $day_int =intval($start_day->format('d'));
543
+
544
+ //Set the first month
545
+ $current_month= clone $start_day;
546
+ $current_month = date_create($current_month->format('Y-m-1'));
547
+
548
+ while( $current_month<=$schedule_last ):
549
+ $month_int = intval($current_month->format('m'));
550
+ $year_int = intval($current_month->format('Y'));
551
+
552
+ if( checkdate($month_int , $day_int , $year_int) ){
553
+ $current = new DateTime($day_int.'-'.$month_int.'-'.$year_int, $timezone);
554
+ $current->setTime($H,$i );
555
+ $occurrences[] = clone $current;
556
+ }
557
+ $current_month->modify($interval);
558
+ endwhile;
559
+ break;
560
+
561
+ //To be used for yearly events occuring on Feb 29
562
+ case 'leap year':
563
+ $current_year = clone $current;
564
+ $current_year->modify('-1 day');
565
+
566
+ while($current_year<=$schedule_last):
567
+ $is_leap_year = (int) $current_year->format('L');
568
+
569
+ if( $is_leap_year ){
570
+ $current = clone $current_year;
571
+ $current->modify('+1 day');
572
+ $current->setTime($H,$i );
573
+ $occurrences[] = clone $current;
574
+ }
575
+
576
+ $current_year->modify($interval);
577
+ endwhile;
578
+ break;
579
+
580
+ default:
581
+ while($current <= $schedule_last):
582
+ $current->setTime($H,$i );
583
+ $occurrences[] = clone $current;
584
+ $current->modify($interval);
585
+ endwhile;
586
+ break;
587
+
588
+ endswitch;//End 'workaround' switch;
589
+ endforeach;
590
+
591
+ //Now schedule meta is set up and occurrences are generated.
592
+
593
+ //Add inclusions, removes exceptions and duplicates
594
+ $occurrences = array_merge($occurrences, $include);
595
+ $occurrences = array_udiff($occurrences, $exclude, '_eventorganiser_compare_dates');
596
+ $occurrences = _eventorganiser_remove_duplicates($occurrences);
597
+
598
+ //Sort occurrences
599
+ sort($occurrences);
600
+ $schedule_start = clone $occurrences[0];
601
+ $schedule_last = clone end($occurrences);
602
+
603
+ $_event_data = array(
604
+ 'start'=>$start,
605
+ 'end'=>$end,
606
+ 'all_day'=>$all_day,
607
+ 'schedule'=>$schedule,
608
+ 'schedule_meta'=>$schedule_meta,
609
+ 'frequency'=>$frequency,
610
+ 'schedule_start'=>$schedule_start,
611
+ 'schedule_last'=>$schedule_last,
612
+ 'exclude'=>$exclude,
613
+ 'include'=>$include,
614
+ 'occurrences'=>$occurrences
615
+ );
616
+
617
+ return apply_filters( 'eventorganiser_generate_occurrences', $_event_data, $event_details );
618
+ }
619
+
620
+ /**
621
+ * Generates the ICS RRULE fromthe event schedule data.
622
+ * @access private
623
+ * @ignore
624
+ * @since 1.0.0
625
+ *
626
+ * @param int $post_id The event (post) ID. Uses current event if empty.
627
+ * @return string The RRULE to be used in an ICS calendar
628
+ */
629
+ function eventorganiser_generate_ics_rrule($post_id=0){
630
+
631
+ $post_id = (int) ( empty($post_id) ? get_the_ID() : $post_id);
632
+
633
+ $rrule = eo_get_event_schedule($post_id);
634
+ if( !$rrule )
635
+ return false;
636
+
637
+ extract($rrule);
638
+
639
+ $format = ( $all_day ? 'Ymd' : 'Ymd\THis\Z' );
640
+
641
+ $schedule_last->setTimezone(new DateTimeZone('UTC'));
642
+ $schedule_last = $schedule_last->format($format);
643
+
644
+ switch($schedule):
645
+ case 'once':
646
+ return false;
647
+
648
+ case 'yearly':
649
+ return "FREQ=YEARLY;INTERVAL=".$frequency.";UNTIL=".$schedule_last;
650
+
651
+ case 'monthly':
652
+ $reoccurrence_rule = "FREQ=MONTHLY;INTERVAL=".$frequency.";";
653
+ $reoccurrence_rule.=$schedule_meta.";";
654
+ $reoccurrence_rule.= "UNTIL=".$schedule_last;
655
+ return $reoccurrence_rule;
656
+
657
+ case 'weekly':
658
+ return "FREQ=WEEKLY;INTERVAL=".$frequency.";BYDAY=".implode(',',$schedule_meta).";UNTIL=".$schedule_last;
659
+
660
+ case 'daily':
661
+ return "FREQ=DAILY;INTERVAL=".$frequency.";UNTIL=".$schedule_last;
662
+
663
+ default:
664
+ endswitch;
665
+ return false;
666
+ }
667
+
668
+ /**
669
+ * Removes a single occurrence and adds it to the event's 'excluded' dates.
670
+ * @access private
671
+ * @ignore
672
+ * @since 1.5
673
+ *
674
+ * @param int $post_id The event (post) ID
675
+ * @param int $event_id The event occurrence ID
676
+ * @return bool|WP_Error True on success, WP_Error object on failure
677
+ */
678
+ function _eventorganiser_remove_occurrence($post_id=0, $event_id=0){
679
+ global $wpdb;
680
+
681
+ $remove = $wpdb->get_row($wpdb->prepare(
682
+ "SELECT {$wpdb->eo_events}.StartDate, {$wpdb->eo_events}.StartTime
683
+ FROM {$wpdb->eo_events}
684
+ WHERE post_id=%d AND event_id=%d",$post_id,$event_id));
685
+
686
+ if( !$remove )
687
+ return new WP_Error('eo_notice', '<strong>'.__("Occurrence note deleted. Occurrence not found",'eventorganiser').'</strong>');
688
+
689
+ $date = trim($remove->StartDate).' '.trim($remove->StartTime);
690
+
691
+ $event_details = get_post_meta( $post_id,'_eventorganiser_event_schedule',true);
692
+
693
+ if( ($key = array_search($date,$event_details['include'])) === false){
694
+ //If the date was not manually included, add it to the 'exclude' array
695
+ $event_details['exclude'][] = $date;
696
+ }else{
697
+ //If the date was manually included, just remove it from the included dates
698
+ unset($event_details['include'][$key]);
699
+ }
700
+
701
+ //Remove the date from the occurrences
702
+ if( isset($event_details['_occurrences'][$event_id]) ){
703
+ unset($event_details['_occurrences'][$event_id]);
704
+ }
705
+
706
+ //Update post meta and delete date from events table
707
+ update_post_meta( $post_id,'_eventorganiser_event_schedule',$event_details);
708
+ $del = $wpdb->get_results($wpdb->prepare("DELETE FROM {$wpdb->eo_events} WHERE post_id=%d AND event_id=%d",$post_id,$event_id));
709
+
710
+ //Clear cache
711
+ _eventorganiser_delete_calendar_cache();
712
+
713
+ return true;
714
+ }
715
+ ?>
js/admin-calendar.js ADDED
@@ -0,0 +1,1199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function ($) {
2
+ $(document).ready(function () {
3
+
4
+ /* Calendar Dialogs */
5
+ $('#eo-dialog-tabs').tabs();
6
+ $('.eo-dialog').dialog({
7
+ autoOpen: false,
8
+ width: 527,
9
+ modal:true
10
+ });
11
+ $('#events-meta').parent().find('.ui-dialog-titlebar-close').appendTo('.ui-tabs-nav').closest('.ui-dialog').children('.ui-dialog-titlebar').remove();
12
+
13
+ /* Time Format from screen option */
14
+ var format = ($('#eofc_time_format').is(":checked") ? 'HH:mm' : 'h:mmtt');
15
+ if( $.cookie('eo_admin_cal_last_viewed_date') ){
16
+ var initial_date = new Date($.cookie('eo_admin_cal_last_viewed_date'));
17
+ }else{
18
+ var initial_date = new Date();
19
+ }
20
+
21
+ /* Calendar */
22
+ var calendar = jQuery('#eo_admin_calendar').fullCalendar({
23
+ firstDay: parseInt(EO_Ajax.startday),
24
+ date:initial_date.getDate(),
25
+ month:initial_date.getMonth(),
26
+ year: initial_date.getFullYear(),
27
+ defaultView: ($.cookie('eo_admin_cal_last_view') ? $.cookie('eo_admin_cal_last_view') : 'month'),
28
+ editable: false,
29
+ lazyFetching: 'true',
30
+ eventColor: '#21759B',
31
+ theme: true,
32
+ customButtons:{
33
+ category: eventorganiser_cat_dropdown,
34
+ venue: eventorganiser_venue_dropdown,
35
+ 'goto': eventorganiser_mini_calendar
36
+ },
37
+ buttonText: {
38
+ today: EO_Ajax.locale.today,
39
+ month: EO_Ajax.locale.month,
40
+ week: EO_Ajax.locale.week,
41
+ day: EO_Ajax.locale.day,
42
+ cat: EO_Ajax.locale.cat,
43
+ venue: EO_Ajax.locale.venue
44
+ },
45
+ monthNames: EO_Ajax.locale.monthNames,
46
+ monthNamesShort: EO_Ajax.locale.monthAbbrev,
47
+ dayNames: EO_Ajax.locale.dayNames,
48
+ dayNamesShort: EO_Ajax.locale.dayAbbrev,
49
+ header: {
50
+ left: 'title',
51
+ center: 'category venue',
52
+ right: 'prev goto today next'
53
+ },
54
+ buttonIcons: false,
55
+ buttonui: true,
56
+ events: function (start, end, callback) {
57
+ jQuery.ajax({
58
+ url: EO_Ajax.ajaxurl + "?action=event-admin-cal",
59
+ dataType: 'JSON',
60
+ data: {
61
+ start: jQuery.fullCalendar.formatDate(start, 'yyyy-MM-dd'),
62
+ end: jQuery.fullCalendar.formatDate(end, 'yyyy-MM-dd')
63
+ },
64
+ success: function (data) {
65
+ callback(data)
66
+ }
67
+ })
68
+ },
69
+ categories: EO_Ajax.categories,
70
+ venues: EO_Ajax.venues,
71
+ selectable: true,
72
+ selectHelper: true,
73
+ eventRender: function (event, element) {
74
+ var cat = jQuery(".filter-category .eo-cal-filter").val();
75
+ var venue = jQuery(".filter-venue .eo-cal-filter").val();
76
+ if (typeof cat !== "undefined" && cat != '' && (jQuery.inArray(cat, event.category) < 0)) {
77
+ return '<div></div>'
78
+ }
79
+ if (typeof venue !== "undefined" && venue != '' && venue != event.venue) {
80
+ return '<div></div>'
81
+ }
82
+ },
83
+ viewDisplay: function (element) {
84
+ var date = jQuery.fullCalendar.formatDate( element.start,'yyyy-MM-dd');
85
+ var view = element.name;
86
+
87
+ //Expire cooke after 10 minutes
88
+ var expires_date = new Date();
89
+ expires_date = new Date(expires_date.getTime() + (10 * 60 * 1000));
90
+ $.cookie('eo_admin_cal_last_viewed_date', date,{ expires: expires_date });
91
+ $.cookie('eo_admin_cal_last_view', view,{ expires: expires_date });
92
+ },
93
+ weekMode: 'variable',
94
+ aspectRatio: 1.50,
95
+ loading: function (bool) {
96
+ if (bool) jQuery('#loading').show();
97
+ else jQuery('#loading').hide()
98
+ },
99
+ timeFormat:format,
100
+ axisFormat: format,
101
+ eventClick: function (event, jsevent, view) {
102
+ jsevent.preventDefault();
103
+ jQuery("#eo-dialog-tabs ul li").each(function(){
104
+ var id = $(this).attr('id').substring(14);
105
+ jQuery("#eo-dialog-tabs #"+$(this).attr('id')+'-content').html(event[id]);
106
+ });
107
+
108
+ $('#events-meta').dialog('open');
109
+ },
110
+ select: function (startDate, endDate, allDay, jsEvent, view) {
111
+ if (EO_Ajax.perm_edit) {
112
+ jsEvent.preventDefault();
113
+ var fc_format = 'yyyy-MM-dd';
114
+ var options = jQuery(this)[0].calendar.options;
115
+ var start_date = jQuery.fullCalendar.formatDate(startDate, fc_format);
116
+ var start_time = jQuery.fullCalendar.formatDate(startDate, 'HH:mm');
117
+ var end_date = jQuery.fullCalendar.formatDate(endDate, fc_format);
118
+ var end_time = jQuery.fullCalendar.formatDate(endDate, 'HH:mm');
119
+ if (allDay) {
120
+ format = 'ddd, dS MMMM';
121
+ allDay = 1
122
+ } else {
123
+ format = 'ddd, dS MMMM h(:mm)tt';
124
+ allDay = 0
125
+ }
126
+ if (start_date == end_date) {
127
+ var the_date = jQuery.fullCalendar.formatDate(startDate, format, options);
128
+ if (!allDay) {
129
+ the_date = the_date + ' &mdash; ' + jQuery.fullCalendar.formatDate(endDate, 'h(:mm)tt', options)
130
+ }
131
+ } else {
132
+ var the_date = jQuery.fullCalendar.formatDate(startDate, format, options) + ' &mdash; ' + jQuery.fullCalendar.formatDate(endDate, format, options)
133
+ }
134
+ $("#eo_event_create_cal input[name='eo_event[event_title]']").val('');
135
+ $("#eo_event_create_cal input.ui-autocomplete-input").val('');
136
+ $("#eo_event_create_cal textarea[name='eo_event[event_content]']").val('');
137
+ $("#eo_event_create_cal input[name='eo_event[StartDate]']").val(start_date);
138
+ $("#eo_event_create_cal input[name='eo_event[StartTime]']").val(start_time);
139
+ $("#eo_event_create_cal input[name='eo_event[EndDate]']").val(end_date);
140
+ $("#eo_event_create_cal input[name='eo_event[FinishTime]']").val(end_time);
141
+ $("#eo_event_create_cal input[name='eo_event[allday]']").val(allDay);
142
+ $("#eo_event_create_cal td#date").html(the_date);
143
+ $('#eo_event_create_cal').dialog('open');
144
+ $("form.eo_cal input[type='submit']").removeAttr('disabled');
145
+ $("form.eo_cal input#reset").click(function (event) {
146
+ $('#eo_event_create_cal').dialog('close');
147
+ })
148
+ }
149
+ }
150
+ });
151
+
152
+ /* Update time format screen option */
153
+ $('#eofc_time_format').change(function () {
154
+ format = ($('#eofc_time_format').is(":checked") ? 'HH:mm' : 'h:mmtt');
155
+ calendar.fullCalendar('option', 'timeFormat', format);
156
+ $.post(ajaxurl, {
157
+ action: 'eofc-format-time',
158
+ is24: $('#eofc_time_format').is(":checked")
159
+ });
160
+ });
161
+
162
+
163
+ /* View tabs */
164
+ $('.view-button').click(function (event) {
165
+ event.preventDefault();
166
+ $('.view-button').removeClass('active');
167
+ calendar.fullCalendar('changeView', $(this).attr('id'));
168
+ $(this).addClass('active')
169
+ });
170
+
171
+ /* GoTo 'mini calendar' */
172
+ function eventorganiser_mini_calendar(){
173
+ var element = $("<span class='fc-header-goto'><input type='hidden' id='miniCalendar'/></span>");
174
+ return element;
175
+ }
176
+ $('#miniCalendar').datepicker({
177
+ dateFormat: 'DD, d MM, yy',
178
+ firstDay: parseInt(EO_Ajax.startday),
179
+ changeMonth: true,
180
+ monthNamesShort: EO_Ajax.locale.monthAbbrev,
181
+ dayNamesMin: EO_Ajax.locale.dayAbbrev,
182
+ changeYear: true,
183
+ showOn: 'button',
184
+ buttonText: EO_Ajax.locale.gotodate,
185
+ onSelect: function (dateText, dp) {
186
+ calendar.fullCalendar('gotoDate', new Date(Date.parse(dateText)))
187
+ }
188
+ });
189
+ $('button.ui-datepicker-trigger').button();
190
+
191
+ /* Venue & Category Filters */
192
+ function eventorganiser_cat_dropdown(options){
193
+
194
+ var terms = options.categories;
195
+
196
+ var html="<select class='eo-cal-filter' id='eo-event-cat'>";
197
+ html+="<option value=''>"+options.buttonText.cat+"</option>";
198
+ for (var i=0;i<terms.length; i++) {
199
+ html+= "<option class='cat-slug-"+terms[i].slug+" cat' value='"+terms[i].slug+"'>"+terms[i].name+"</option>";
200
+ }
201
+ html+="</select>";
202
+
203
+ return $("<span class='fc-header-dropdown filter-category'></span>").append(html);
204
+ }
205
+
206
+ function eventorganiser_venue_dropdown(options){
207
+
208
+ var venues = options.venues;
209
+
210
+ var html="<select class='eo-cal-filter' id='eo-event-venue'>";
211
+ html+="<option value=''>"+options.buttonText.venue+"</option>";
212
+
213
+ for (var i=0; i<venues.length; i++){
214
+ html+= "<option value='"+venues[i].term_id+"'>"+venues[i].name+"</option>";
215
+ }
216
+ html+="</select>";
217
+
218
+ return $("<span class='fc-header-dropdown filter-venue'></span>").append(html);
219
+ }
220
+ $(".eo-cal-filter").change(function () {
221
+ calendar.fullCalendar('rerenderEvents')
222
+ });
223
+ $('.filter-venue .eo-cal-filter').selectmenu({
224
+ wrapperElement: "<span class='fc-header-filter'></span>"
225
+ });
226
+ $('.filter-category .eo-cal-filter').selectmenu({
227
+ wrapperElement: "<span class='fc-header-filter'></span>",
228
+ icons: [{find: '.cat'} ]
229
+ });
230
+ var w = $('#eo-event-venue-button').width() + 30;
231
+ $('#eo-event-venue-button').width(w + 'px');
232
+ $('#eo-event-venue-menu').width(w + 'px');
233
+ var w2 = $('#eo-event-cat-button').width() + 30;
234
+ $('#eo-event-cat-button').width(w2 + 'px');
235
+ $('#eo-event-cat-menu').width(w2 + 'px');
236
+ });
237
+ })(jQuery);
238
+ /*
239
+ * jQuery UI Selectmenu version 1.3.0
240
+ *
241
+ * Copyright (c) 2009-2010 filament group, http://filamentgroup.com
242
+ * Copyright (c) 2010-2012 Felix Nagel, http://www.felixnagel.com
243
+ * Licensed under the MIT (MIT-LICENSE.txt)
244
+ *
245
+ * https://github.com/fnagel/jquery-ui/wiki/Selectmenu
246
+ */
247
+
248
+ (function($) {
249
+ /**
250
+ *@constructor
251
+ */
252
+ $.widget("ui.selectmenu", {
253
+ options: {
254
+ appendTo: "body",
255
+ typeAhead: 1000,
256
+ style: 'dropdown',
257
+ positionOptions: {
258
+ my: "left top",
259
+ at: "left bottom",
260
+ offset: null
261
+ },
262
+ width: null,
263
+ menuWidth: null,
264
+ handleWidth: 26,
265
+ maxHeight: null,
266
+ icons: null,
267
+ format: null,
268
+ escapeHtml: false,
269
+ bgImage: function() {}
270
+ },
271
+
272
+ /**
273
+ *@constructor
274
+ */
275
+ _create: function() {
276
+ var self = this, o = this.options;
277
+
278
+ // set a default id value, generate a new random one if not set by developer
279
+ var selectmenuId = (this.element.attr( 'id' ) || 'ui-selectmenu-' + Math.random().toString( 16 ).slice( 2, 10 )).replace(/(:|\.)/g,'')
280
+
281
+ // quick array of button and menu id's
282
+ this.ids = [ selectmenuId, selectmenuId + '-button', selectmenuId + '-menu' ];
283
+
284
+ // define safe mouseup for future toggling
285
+ this._safemouseup = true;
286
+ this.isOpen = false;
287
+
288
+ // create menu button wrapper
289
+ this.newelement = $( '<a />', {
290
+ 'class': this.widgetBaseClass + ' ui-widget ui-state-default ui-corner-all',
291
+ 'id' : this.ids[ 1 ],
292
+ 'role': 'button',
293
+ 'href': '#nogo',
294
+ 'tabindex': this.element.attr( 'disabled' ) ? 1 : 0,
295
+ 'aria-haspopup': true,
296
+ 'aria-owns': this.ids[ 2 ]
297
+ });
298
+ this.newelementWrap = $( "<span />" )
299
+ .append( this.newelement )
300
+ .insertAfter( this.element );
301
+
302
+ // transfer tabindex
303
+ var tabindex = this.element.attr( 'tabindex' );
304
+ if ( tabindex ) {
305
+ this.newelement.attr( 'tabindex', tabindex );
306
+ }
307
+
308
+ // save reference to select in data for ease in calling methods
309
+ this.newelement.data( 'selectelement', this.element );
310
+
311
+ // menu icon
312
+ this.selectmenuIcon = $( '<span class="' + this.widgetBaseClass + '-icon ui-icon"></span>' )
313
+ .prependTo( this.newelement );
314
+
315
+ // append status span to button
316
+ this.newelement.prepend( '<span class="' + self.widgetBaseClass + '-status" />' );
317
+
318
+ // make associated form label trigger focus
319
+ this.element.bind({
320
+ 'click.selectmenu': function( event ) {
321
+ self.newelement.focus();
322
+ event.preventDefault();
323
+ }
324
+ });
325
+
326
+ // click toggle for menu visibility
327
+ this.newelement
328
+ .bind('mousedown.selectmenu', function(event) {
329
+ self._toggle(event, true);
330
+ // make sure a click won't open/close instantly
331
+ if (o.style == "popup") {
332
+ self._safemouseup = false;
333
+ setTimeout(function() { self._safemouseup = true; }, 300);
334
+ }
335
+ return false;
336
+ })
337
+ .bind('click.selectmenu', function() {
338
+ return false;
339
+ })
340
+ .bind("keydown.selectmenu", function(event) {
341
+ var ret = false;
342
+ switch (event.keyCode) {
343
+ case $.ui.keyCode.ENTER:
344
+ ret = true;
345
+ break;
346
+ case $.ui.keyCode.SPACE:
347
+ self._toggle(event);
348
+ break;
349
+ case $.ui.keyCode.UP:
350
+ if (event.altKey) {
351
+ self.open(event);
352
+ } else {
353
+ self._moveSelection(-1);
354
+ }
355
+ break;
356
+ case $.ui.keyCode.DOWN:
357
+ if (event.altKey) {
358
+ self.open(event);
359
+ } else {
360
+ self._moveSelection(1);
361
+ }
362
+ break;
363
+ case $.ui.keyCode.LEFT:
364
+ self._moveSelection(-1);
365
+ break;
366
+ case $.ui.keyCode.RIGHT:
367
+ self._moveSelection(1);
368
+ break;
369
+ case $.ui.keyCode.TAB:
370
+ ret = true;
371
+ break;
372
+ case $.ui.keyCode.PAGE_UP:
373
+ case $.ui.keyCode.HOME:
374
+ self.index(0);
375
+ break;
376
+ case $.ui.keyCode.PAGE_DOWN:
377
+ case $.ui.keyCode.END:
378
+ self.index(self._optionLis.length);
379
+ break;
380
+ default:
381
+ ret = true;
382
+ }
383
+ return ret;
384
+ })
385
+ .bind('keypress.selectmenu', function(event) {
386
+ if (event.which > 0) {
387
+ self._typeAhead(event.which, 'mouseup');
388
+ }
389
+ return true;
390
+ })
391
+ .bind('mouseover.selectmenu', function() {
392
+ if (!o.disabled) $(this).addClass('ui-state-hover');
393
+ })
394
+ .bind('mouseout.selectmenu', function() {
395
+ if (!o.disabled) $(this).removeClass('ui-state-hover');
396
+ })
397
+ .bind('focus.selectmenu', function() {
398
+ if (!o.disabled) $(this).addClass('ui-state-focus');
399
+ })
400
+ .bind('blur.selectmenu', function() {
401
+ if (!o.disabled) $(this).removeClass('ui-state-focus');
402
+ });
403
+
404
+ // document click closes menu
405
+ $(document).bind("mousedown.selectmenu-" + this.ids[0], function(event) {
406
+ if ( self.isOpen ) {
407
+ self.close( event );
408
+ }
409
+ });
410
+
411
+ // change event on original selectmenu
412
+ this.element
413
+ .bind("click.selectmenu", function() {
414
+ self._refreshValue();
415
+ })
416
+ // FIXME: newelement can be null under unclear circumstances in IE8
417
+ // TODO not sure if this is still a problem (fnagel 20.03.11)
418
+ .bind("focus.selectmenu", function() {
419
+ if (self.newelement) {
420
+ self.newelement[0].focus();
421
+ }
422
+ });
423
+
424
+ // set width when not set via options
425
+ if (!o.width) {
426
+ o.width = this.element.outerWidth();
427
+ }
428
+ // set menu button width
429
+ this.newelement.width(o.width);
430
+
431
+ // hide original selectmenu element
432
+ this.element.hide();
433
+
434
+ // create menu portion, append to body
435
+ this.list = $( '<ul />', {
436
+ 'class': 'ui-widget ui-widget-content',
437
+ 'aria-hidden': true,
438
+ 'role': 'listbox',
439
+ 'aria-labelledby': this.ids[1],
440
+ 'id': this.ids[2]
441
+ });
442
+ this.listWrap = $( "<div />", {
443
+ 'class': self.widgetBaseClass + '-menu'
444
+ }).append( this.list ).appendTo( o.appendTo );
445
+
446
+ // transfer menu click to menu button
447
+ this.list
448
+ .bind("keydown.selectmenu", function(event) {
449
+ var ret = false;
450
+ switch (event.keyCode) {
451
+ case $.ui.keyCode.UP:
452
+ if (event.altKey) {
453
+ self.close(event, true);
454
+ } else {
455
+ self._moveFocus(-1);
456
+ }
457
+ break;
458
+ case $.ui.keyCode.DOWN:
459
+ if (event.altKey) {
460
+ self.close(event, true);
461
+ } else {
462
+ self._moveFocus(1);
463
+ }
464
+ break;
465
+ case $.ui.keyCode.LEFT:
466
+ self._moveFocus(-1);
467
+ break;
468
+ case $.ui.keyCode.RIGHT:
469
+ self._moveFocus(1);
470
+ break;
471
+ case $.ui.keyCode.HOME:
472
+ self._moveFocus(':first');
473
+ break;
474
+ case $.ui.keyCode.PAGE_UP:
475
+ self._scrollPage('up');
476
+ break;
477
+ case $.ui.keyCode.PAGE_DOWN:
478
+ self._scrollPage('down');
479
+ break;
480
+ case $.ui.keyCode.END:
481
+ self._moveFocus(':last');
482
+ break;
483
+ case $.ui.keyCode.ENTER:
484
+ case $.ui.keyCode.SPACE:
485
+ self.close(event, true);
486
+ $(event.target).parents('li:eq(0)').trigger('mouseup');
487
+ break;
488
+ case $.ui.keyCode.TAB:
489
+ ret = true;
490
+ self.close(event, true);
491
+ $(event.target).parents('li:eq(0)').trigger('mouseup');
492
+ break;
493
+ case $.ui.keyCode.ESCAPE:
494
+ self.close(event, true);
495
+ break;
496
+ default:
497
+ ret = true;
498
+ }
499
+ return ret;
500
+ })
501
+ .bind('keypress.selectmenu', function(event) {
502
+ if (event.which > 0) {
503
+ self._typeAhead(event.which, 'focus');
504
+ }
505
+ return true;
506
+ })
507
+ // this allows for using the scrollbar in an overflowed list
508
+ .bind( 'mousedown.selectmenu mouseup.selectmenu', function() { return false; });
509
+
510
+ // needed when window is resized
511
+ $(window).bind( "resize.selectmenu-" + this.ids[0], $.proxy( self.close, this ) );
512
+ },
513
+
514
+ /**
515
+ *@constructor
516
+ */
517
+ _init: function() {
518
+ var self = this, o = this.options;
519
+
520
+ // serialize selectmenu element options
521
+ var selectOptionData = [];
522
+ this.element.find('option').each(function() {
523
+ var opt = $(this);
524
+ selectOptionData.push({
525
+ value: opt.attr('value'),
526
+ text: self._formatText(opt.text(), opt),
527
+ selected: opt.attr('selected'),
528
+ disabled: opt.attr('disabled'),
529
+ classes: opt.attr('class'),
530
+ typeahead: opt.attr('typeahead'),
531
+ parentOptGroup: opt.parent('optgroup'),
532
+ bgImage: o.bgImage.call(opt)
533
+ });
534
+ });
535
+
536
+ // active state class is only used in popup style
537
+ var activeClass = (self.options.style == "popup") ? " ui-state-active" : "";
538
+
539
+ // empty list so we can refresh the selectmenu via selectmenu()
540
+ this.list.html("");
541
+
542
+ // write li's
543
+ if (selectOptionData.length) {
544
+ for (var i = 0; i < selectOptionData.length; i++) {
545
+ var thisLiAttr = { role : 'presentation' };
546
+ if ( selectOptionData[ i ].disabled ) {
547
+ thisLiAttr[ 'class' ] = this.namespace + '-state-disabled';
548
+ }
549
+ var thisAAttr = {
550
+ html: selectOptionData[i].text || '&nbsp;',
551
+ href : '#nogo',
552
+ tabindex : -1,
553
+ role : 'option',
554
+ 'aria-selected' : false
555
+ };
556
+ if ( selectOptionData[ i ].disabled ) {
557
+ thisAAttr[ 'aria-disabled' ] = selectOptionData[ i ].disabled;
558
+ }
559
+ if ( selectOptionData[ i ].typeahead ) {
560
+ thisAAttr[ 'typeahead' ] = selectOptionData[ i ].typeahead;
561
+ }
562
+ var thisA = $('<a/>', thisAAttr)
563
+ .bind('focus.selectmenu', function() {
564
+ $(this).parent().mouseover();
565
+ })
566
+ .bind('blur.selectmenu', function() {
567
+ $(this).parent().mouseout();
568
+ });
569
+ var thisLi = $('<li/>', thisLiAttr)
570
+ .append(thisA)
571
+ .data('index', i)
572
+ .addClass(selectOptionData[i].classes)
573
+ .data('optionClasses', selectOptionData[i].classes || '')
574
+ .bind("mouseup.selectmenu", function(event) {
575
+ if (self._safemouseup && !self._disabled(event.currentTarget) && !self._disabled($( event.currentTarget ).parents( "ul>li." + self.widgetBaseClass + "-group " )) ) {
576
+ self.index($(this).data('index'));
577
+ self.select(event);
578
+ self.close(event, true);
579
+ }
580
+ return false;
581
+ })
582
+ .bind("click.selectmenu", function() {
583
+ return false;
584
+ })
585
+ .bind('mouseover.selectmenu', function() {
586
+ // no hover if diabled
587
+ if (!$(this).hasClass(self.namespace + '-state-disabled') && !$(this).parent("ul").parent("li").hasClass(self.namespace + '-state-disabled')) {
588
+ self._selectedOptionLi().addClass(activeClass);
589
+ self._focusedOptionLi().removeClass(self.widgetBaseClass + '-item-focus ui-state-hover');
590
+ $(this).removeClass('ui-state-active').addClass(self.widgetBaseClass + '-item-focus ui-state-hover');
591
+ }
592
+ })
593
+ .bind('mouseout.selectmenu', function() {
594
+ if ($(this).is(self._selectedOptionLi())) {
595
+ $(this).addClass(activeClass);
596
+ }
597
+ $(this).removeClass(self.widgetBaseClass + '-item-focus ui-state-hover');
598
+ });
599
+
600
+ // optgroup or not...
601
+ if ( selectOptionData[i].parentOptGroup.length ) {
602
+ var optGroupName = self.widgetBaseClass + '-group-' + this.element.find( 'optgroup' ).index( selectOptionData[i].parentOptGroup );
603
+ if (this.list.find( 'li.' + optGroupName ).length ) {
604
+ this.list.find( 'li.' + optGroupName + ':last ul' ).append( thisLi );
605
+ } else {
606
+ $(' <li role="presentation" class="' + self.widgetBaseClass + '-group ' + optGroupName + (selectOptionData[i].parentOptGroup.attr("disabled") ? ' ' + this.namespace + '-state-disabled" aria-disabled="true"' : '"' ) + '><span class="' + self.widgetBaseClass + '-group-label">' + selectOptionData[i].parentOptGroup.attr('label') + '</span><ul></ul></li> ')
607
+ .appendTo( this.list )
608
+ .find( 'ul' )
609
+ .append( thisLi );
610
+ }
611
+ } else {
612
+ thisLi.appendTo(this.list);
613
+ }
614
+
615
+ // append icon if option is specified
616
+ if (o.icons) {
617
+ for (var j in o.icons) {
618
+ if (thisLi.is(o.icons[j].find)) {
619
+ thisLi
620
+ .data('optionClasses', selectOptionData[i].classes + ' ' + self.widgetBaseClass + '-hasIcon')
621
+ .addClass(self.widgetBaseClass + '-hasIcon');
622
+ var iconClass = o.icons[j].icon || "";
623
+ thisLi
624
+ .find('a:eq(0)')
625
+ .prepend('<span class="' + self.widgetBaseClass + '-item-icon ui-icon ' + iconClass + '"></span>');
626
+ if (selectOptionData[i].bgImage) {
627
+ thisLi.find('span').css('background-image', selectOptionData[i].bgImage);
628
+ }
629
+ }
630
+ }
631
+ }
632
+ }
633
+ } else {
634
+ $('<li role="presentation"><a href="#nogo" tabindex="-1" role="option"></a></li>').appendTo(this.list);
635
+ }
636
+ // we need to set and unset the CSS classes for dropdown and popup style
637
+ var isDropDown = ( o.style == 'dropdown' );
638
+ this.newelement
639
+ .toggleClass( self.widgetBaseClass + '-dropdown', isDropDown )
640
+ .toggleClass( self.widgetBaseClass + '-popup', !isDropDown );
641
+ this.list
642
+ .toggleClass( self.widgetBaseClass + '-menu-dropdown ui-corner-bottom', isDropDown )
643
+ .toggleClass( self.widgetBaseClass + '-menu-popup ui-corner-all', !isDropDown )
644
+ // add corners to top and bottom menu items
645
+ .find( 'li:first' )
646
+ .toggleClass( 'ui-corner-top', !isDropDown )
647
+ .end().find( 'li:last' )
648
+ .addClass( 'ui-corner-bottom' );
649
+ this.selectmenuIcon
650
+ .toggleClass( 'ui-icon-triangle-1-s', isDropDown )
651
+ .toggleClass( 'ui-icon-triangle-2-n-s', !isDropDown );
652
+
653
+ // set menu width to either menuWidth option value, width option value, or select width
654
+ if ( o.style == 'dropdown' ) {
655
+ this.list.width( o.menuWidth ? o.menuWidth : o.width );
656
+ } else {
657
+ this.list.width( o.menuWidth ? o.menuWidth : o.width - o.handleWidth );
658
+ }
659
+
660
+ // reset height to auto
661
+ this.list.css( 'height', 'auto' );
662
+ var listH = this.listWrap.height();
663
+ var winH = $( window ).height();
664
+ // calculate default max height
665
+ var maxH = o.maxHeight ? Math.min( o.maxHeight, winH ) : winH / 3;
666
+ if ( listH > maxH ) this.list.height( maxH );
667
+
668
+ // save reference to actionable li's (not group label li's)
669
+ this._optionLis = this.list.find( 'li:not(.' + self.widgetBaseClass + '-group)' );
670
+
671
+ // transfer disabled state
672
+ if ( this.element.attr( 'disabled' ) ) {
673
+ this.disable();
674
+ } else {
675
+ this.enable();
676
+ }
677
+
678
+ // update value
679
+ this._refreshValue();
680
+
681
+ // set selected item so movefocus has intial state
682
+ this._selectedOptionLi().addClass(this.widgetBaseClass + '-item-focus');
683
+
684
+ // needed when selectmenu is placed at the very bottom / top of the page
685
+ clearTimeout(this.refreshTimeout);
686
+ this.refreshTimeout = window.setTimeout(function () {
687
+ self._refreshPosition();
688
+ }, 200);
689
+ },
690
+
691
+ destroy: function() {
692
+ this.element.removeData( this.widgetName )
693
+ .removeClass( this.widgetBaseClass + '-disabled' + ' ' + this.namespace + '-state-disabled' )
694
+ .removeAttr( 'aria-disabled' )
695
+ .unbind( ".selectmenu" );
696
+
697
+ $( window ).unbind( ".selectmenu-" + this.ids[0] );
698
+ $( document ).unbind( ".selectmenu-" + this.ids[0] );
699
+
700
+ this.newelementWrap.remove();
701
+ this.listWrap.remove();
702
+
703
+ // unbind click event and show original select
704
+ this.element
705
+ .unbind(".selectmenu")
706
+ .show();
707
+
708
+ // call widget destroy function
709
+ $.Widget.prototype.destroy.apply(this, arguments);
710
+ },
711
+
712
+ _typeAhead: function( code, eventType ) {
713
+ var self = this,
714
+ c = String.fromCharCode(code).toLowerCase(),
715
+ matchee = null,
716
+ nextIndex = null;
717
+
718
+ // Clear any previous timer if present
719
+ if ( self._typeAhead_timer ) {
720
+ window.clearTimeout( self._typeAhead_timer );
721
+ self._typeAhead_timer = undefined;
722
+ }
723
+
724
+ // Store the character typed
725
+ self._typeAhead_chars = (self._typeAhead_chars === undefined ? "" : self._typeAhead_chars).concat(c);
726
+
727
+ // Detect if we are in cyciling mode or direct selection mode
728
+ if ( self._typeAhead_chars.length < 2 ||
729
+ (self._typeAhead_chars.substr(-2, 1) === c && self._typeAhead_cycling) ) {
730
+ self._typeAhead_cycling = true;
731
+
732
+ // Match only the first character and loop
733
+ matchee = c;
734
+ }
735
+ else {
736
+ // We won't be cycling anymore until the timer expires
737
+ self._typeAhead_cycling = false;
738
+
739
+ // Match all the characters typed
740
+ matchee = self._typeAhead_chars;
741
+ }
742
+
743
+ // We need to determine the currently active index, but it depends on
744
+ // the used context: if it's in the element, we want the actual
745
+ // selected index, if it's in the menu, just the focused one
746
+ // I copied this code from _moveSelection() and _moveFocus()
747
+ // respectively --thg2k
748
+ var selectedIndex = (eventType !== 'focus' ?
749
+ this._selectedOptionLi().data('index') :
750
+ this._focusedOptionLi().data('index')) || 0;
751
+
752
+ for (var i = 0; i < this._optionLis.length; i++) {
753
+ var thisText = this._optionLis.eq(i).text().substr(0, matchee.length).toLowerCase();
754
+
755
+ if ( thisText === matchee ) {
756
+ if ( self._typeAhead_cycling ) {
757
+ if ( nextIndex === null )
758
+ nextIndex = i;
759
+
760
+ if ( i > selectedIndex ) {
761
+ nextIndex = i;
762
+ break;
763
+ }
764
+ } else {
765
+ nextIndex = i;
766
+ }
767
+ }
768
+ }
769
+
770
+ if ( nextIndex !== null ) {
771
+ // Why using trigger() instead of a direct method to select the
772
+ // index? Because we don't what is the exact action to do, it
773
+ // depends if the user is typing on the element or on the popped
774
+ // up menu
775
+ this._optionLis.eq(nextIndex).find("a").trigger( eventType );
776
+ }
777
+
778
+ self._typeAhead_timer = window.setTimeout(function() {
779
+ self._typeAhead_timer = undefined;
780
+ self._typeAhead_chars = undefined;
781
+ self._typeAhead_cycling = undefined;
782
+ }, self.options.typeAhead);
783
+ },
784
+
785
+ // returns some usefull information, called by callbacks only
786
+ _uiHash: function() {
787
+ var index = this.index();
788
+ return {
789
+ index: index,
790
+ option: $("option", this.element).get(index),
791
+ value: this.element[0].value
792
+ };
793
+ },
794
+
795
+ open: function(event) {
796
+ var self = this, o = this.options;
797
+ if ( self.newelement.attr("aria-disabled") != 'true' ) {
798
+ self._closeOthers(event);
799
+ self.newelement.addClass('ui-state-active');
800
+
801
+ self.list.attr('aria-hidden', false);
802
+ self.listWrap.addClass( self.widgetBaseClass + '-open' );
803
+
804
+ var selected = this._selectedOptionLi();
805
+ if ( o.style == "dropdown" ) {
806
+ self.newelement.removeClass('ui-corner-all').addClass('ui-corner-top');
807
+ } else {
808
+ // center overflow and avoid flickering
809
+ this.list
810
+ .css("left", -5000)
811
+ .scrollTop( this.list.scrollTop() + selected.position().top - this.list.outerHeight()/2 + selected.outerHeight()/2 )
812
+ .css("left","auto");
813
+ }
814
+
815
+ self._refreshPosition();
816
+
817
+ var link = selected.find("a");
818
+ if (link.length) link[0].focus();
819
+
820
+ self.isOpen = true;
821
+ self._trigger("open", event, self._uiHash());
822
+ }
823
+ },
824
+ /**
825
+ *@constructor
826
+ */
827
+ close: function(event, retainFocus) {
828
+ if ( this.newelement.is('.ui-state-active') ) {
829
+ this.newelement
830
+ .removeClass('ui-state-active');
831
+ this.listWrap.removeClass(this.widgetBaseClass + '-open');
832
+ this.list.attr('aria-hidden', true);
833
+ if ( this.options.style == "dropdown" ) {
834
+ this.newelement.removeClass('ui-corner-top').addClass('ui-corner-all');
835
+ }
836
+ if ( retainFocus ) {
837
+ this.newelement.focus();
838
+ }
839
+ this.isOpen = false;
840
+ this._trigger("close", event, this._uiHash());
841
+ }
842
+ },
843
+
844
+ change: function(event) {
845
+ this.element.trigger("change");
846
+ this._trigger("change", event, this._uiHash());
847
+ },
848
+
849
+ select: function(event) {
850
+ if (this._disabled(event.currentTarget)) { return false; }
851
+ this._trigger("select", event, this._uiHash());
852
+ },
853
+
854
+ widget: function() {
855
+ return this.listWrap.add( this.newelementWrap );
856
+ },
857
+
858
+ _closeOthers: function(event) {
859
+ $('.' + this.widgetBaseClass + '.ui-state-active').not(this.newelement).each(function() {
860
+ $(this).data('selectelement').selectmenu('close', event);
861
+ });
862
+ $('.' + this.widgetBaseClass + '.ui-state-hover').trigger('mouseout');
863
+ },
864
+
865
+ _toggle: function(event, retainFocus) {
866
+ if ( this.isOpen ) {
867
+ this.close(event, retainFocus);
868
+ } else {
869
+ this.open(event);
870
+ }
871
+ },
872
+
873
+ _formatText: function(text, opt) {
874
+ if (this.options.format) {
875
+ text = this.options.format(text, opt);
876
+ } else if (this.options.escapeHtml) {
877
+ text = $('<div />').text(text).html();
878
+ }
879
+ return text;
880
+ },
881
+
882
+ _selectedIndex: function() {
883
+ return this.element[0].selectedIndex;
884
+ },
885
+
886
+ _selectedOptionLi: function() {
887
+ return this._optionLis.eq(this._selectedIndex());
888
+ },
889
+
890
+ _focusedOptionLi: function() {
891
+ return this.list.find('.' + this.widgetBaseClass + '-item-focus');
892
+ },
893
+
894
+ _moveSelection: function(amt, recIndex) {
895
+ // do nothing if disabled
896
+ if (!this.options.disabled) {
897
+ var currIndex = parseInt(this._selectedOptionLi().data('index') || 0, 10);
898
+ var newIndex = currIndex + amt;
899
+ // do not loop when using up key
900
+
901
+ if (newIndex < 0) {
902
+ newIndex = 0;
903
+ }
904
+ if (newIndex > this._optionLis.size() - 1) {
905
+ newIndex = this._optionLis.size() - 1;
906
+ }
907
+ // Occurs when a full loop has been made
908
+ if (newIndex === recIndex) { return false; }
909
+
910
+ if (this._optionLis.eq(newIndex).hasClass( this.namespace + '-state-disabled' )) {
911
+ // if option at newIndex is disabled, call _moveFocus, incrementing amt by one
912
+ (amt > 0) ? ++amt : --amt;
913
+ this._moveSelection(amt, newIndex);
914
+ } else {
915
+ this._optionLis.eq(newIndex).trigger('mouseover').trigger('mouseup');
916
+ }
917
+ }
918
+ },
919
+ /**
920
+ *@constructor
921
+ */
922
+ _moveFocus: function(amt, recIndex) {
923
+ if (!isNaN(amt)) {
924
+ var currIndex = parseInt(this._focusedOptionLi().data('index') || 0, 10);
925
+ var newIndex = currIndex + amt;
926
+ } else {
927
+ var newIndex = parseInt(this._optionLis.filter(amt).data('index'), 10);
928
+ }
929
+
930
+ if (newIndex < 0) {
931
+ newIndex = 0;
932
+ }
933
+ if (newIndex > this._optionLis.size() - 1) {
934
+ newIndex = this._optionLis.size() - 1;
935
+ }
936
+
937
+ //Occurs when a full loop has been made
938
+ if (newIndex === recIndex) { return false; }
939
+
940
+ var activeID = this.widgetBaseClass + '-item-' + Math.round(Math.random() * 1000);
941
+
942
+ this._focusedOptionLi().find('a:eq(0)').attr('id', '');
943
+
944
+ if (this._optionLis.eq(newIndex).hasClass( this.namespace + '-state-disabled' )) {
945
+ // if option at newIndex is disabled, call _moveFocus, incrementing amt by one
946
+ (amt > 0) ? ++amt : --amt;
947
+ this._moveFocus(amt, newIndex);
948
+ } else {
949
+ this._optionLis.eq(newIndex).find('a:eq(0)').attr('id',activeID).focus();
950
+ }
951
+
952
+ this.list.attr('aria-activedescendant', activeID);
953
+ },
954
+
955
+ _scrollPage: function(direction) {
956
+ var numPerPage = Math.floor(this.list.outerHeight() / this._optionLis.first().outerHeight());
957
+ numPerPage = (direction == 'up' ? -numPerPage : numPerPage);
958
+ this._moveFocus(numPerPage);
959
+ },
960
+ /**
961
+ *@constructor
962
+ */
963
+ _setOption: function(key, value) {
964
+ this.options[key] = value;
965
+ // set
966
+ if (key == 'disabled') {
967
+ if (value) this.close();
968
+ this.element
969
+ .add(this.newelement)
970
+ .add(this.list)[value ? 'addClass' : 'removeClass'](
971
+ this.widgetBaseClass + '-disabled' + ' ' +
972
+ this.namespace + '-state-disabled')
973
+ .attr("aria-disabled", value);
974
+ }
975
+ },
976
+
977
+ disable: function(index, type){
978
+ // if options is not provided, call the parents disable function
979
+ if ( typeof( index ) == 'undefined' ) {
980
+ this._setOption( 'disabled', true );
981
+ } else {
982
+ if ( type == "optgroup" ) {
983
+ this._disableOptgroup(index);
984
+ } else {
985
+ this._disableOption(index);
986
+ }
987
+ }
988
+ },
989
+
990
+ enable: function(index, type) {
991
+ // if options is not provided, call the parents enable function
992
+ if ( typeof( index ) == 'undefined' ) {
993
+ this._setOption('disabled', false);
994
+ } else {
995
+ if ( type == "optgroup" ) {
996
+ this._enableOptgroup(index);
997
+ } else {
998
+ this._enableOption(index);
999
+ }
1000
+ }
1001
+ },
1002
+
1003
+ _disabled: function(elem) {
1004
+ return $(elem).hasClass( this.namespace + '-state-disabled' );
1005
+ },
1006
+
1007
+ _disableOption: function(index) {
1008
+ var optionElem = this._optionLis.eq(index);
1009
+ if (optionElem) {
1010
+ optionElem.addClass(this.namespace + '-state-disabled')
1011
+ .find("a").attr("aria-disabled", true);
1012
+ this.element.find("option").eq(index).attr("disabled", "disabled");
1013
+ }
1014
+ },
1015
+
1016
+ _enableOption: function(index) {
1017
+ var optionElem = this._optionLis.eq(index);
1018
+ if (optionElem) {
1019
+ optionElem.removeClass( this.namespace + '-state-disabled' )
1020
+ .find("a").attr("aria-disabled", false);
1021
+ this.element.find("option").eq(index).removeAttr("disabled");
1022
+ }
1023
+ },
1024
+
1025
+ _disableOptgroup: function(index) {
1026
+ var optGroupElem = this.list.find( 'li.' + this.widgetBaseClass + '-group-' + index );
1027
+ if (optGroupElem) {
1028
+ optGroupElem.addClass(this.namespace + '-state-disabled')
1029
+ .attr("aria-disabled", true);
1030
+ this.element.find("optgroup").eq(index).attr("disabled", "disabled");
1031
+ }
1032
+ },
1033
+
1034
+ _enableOptgroup: function(index) {
1035
+ var optGroupElem = this.list.find( 'li.' + this.widgetBaseClass + '-group-' + index );
1036
+ if (optGroupElem) {
1037
+ optGroupElem.removeClass(this.namespace + '-state-disabled')
1038
+ .attr("aria-disabled", false);
1039
+ this.element.find("optgroup").eq(index).removeAttr("disabled");
1040
+ }
1041
+ },
1042
+ /**
1043
+ *@constructor
1044
+ */
1045
+ index: function(newIndex) {
1046
+ if (arguments.length) {
1047
+ if (!this._disabled($(this._optionLis[newIndex])) && newIndex != this._selectedIndex()) {
1048
+ this.element[0].selectedIndex = newIndex;
1049
+ this._refreshValue();
1050
+ this.change();
1051
+ } else {
1052
+ return false;
1053
+ }
1054
+ } else {
1055
+ return this._selectedIndex();
1056
+ }
1057
+ },
1058
+ /**
1059
+ *@constructor
1060
+ */
1061
+ value: function(newValue) {
1062
+ if (arguments.length && newValue != this.element[0].value) {
1063
+ this.element[0].value = newValue;
1064
+ this._refreshValue();
1065
+ this.change();
1066
+ } else {
1067
+ return this.element[0].value;
1068
+ }
1069
+ },
1070
+
1071
+ _refreshValue: function() {
1072
+ var activeClass = (this.options.style == "popup") ? " ui-state-active" : "";
1073
+ var activeID = this.widgetBaseClass + '-item-' + Math.round(Math.random() * 1000);
1074
+ // deselect previous
1075
+ this.list
1076
+ .find('.' + this.widgetBaseClass + '-item-selected')
1077
+ .removeClass(this.widgetBaseClass + "-item-selected" + activeClass)
1078
+ .find('a')
1079
+ .attr('aria-selected', 'false')
1080
+ .attr('id', '');
1081
+ // select new
1082
+ this._selectedOptionLi()
1083
+ .addClass(this.widgetBaseClass + "-item-selected" + activeClass)
1084
+ .find('a')
1085
+ .attr('aria-selected', 'true')
1086
+ .attr('id', activeID);
1087
+
1088
+ // toggle any class brought in from option
1089
+ var currentOptionClasses = (this.newelement.data('optionClasses') ? this.newelement.data('optionClasses') : "");
1090
+ var newOptionClasses = (this._selectedOptionLi().data('optionClasses') ? this._selectedOptionLi().data('optionClasses') : "");
1091
+ this.newelement
1092
+ .removeClass(currentOptionClasses)
1093
+ .data('optionClasses', newOptionClasses)
1094
+ .addClass( newOptionClasses )
1095
+ .find('.' + this.widgetBaseClass + '-status')
1096
+ .html(
1097
+ this._selectedOptionLi()
1098
+ .find('a:eq(0)')
1099
+ .html()
1100
+ );
1101
+
1102
+ this.list.attr('aria-activedescendant', activeID);
1103
+ },
1104
+
1105
+ _refreshPosition: function() {
1106
+ var o = this.options;
1107
+
1108
+ // if its a pop-up we need to calculate the position of the selected li
1109
+ if ( o.style == "popup" && !o.positionOptions.offset ) {
1110
+ var selected = this._selectedOptionLi();
1111
+ var _offset = "0 " + ( this.list.offset().top - selected.offset().top - ( this.newelement.outerHeight() + selected.outerHeight() ) / 2);
1112
+ }
1113
+ this.listWrap
1114
+ .removeAttr('style')
1115
+ .zIndex( this.element.zIndex() + 1 )
1116
+ .position({
1117
+ // set options for position plugin
1118
+ of: o.positionOptions.of || this.newelement,
1119
+ my: o.positionOptions.my,
1120
+ at: o.positionOptions.at,
1121
+ offset: o.positionOptions.offset || _offset,
1122
+ collision: o.positionOptions.collision || (o.style == "popup" ? 'fit' :'flip')
1123
+ });
1124
+ }
1125
+ });
1126
+
1127
+ })(jQuery);
1128
+ /*!
1129
+ * jQuery Cookie Plugin v1.3
1130
+ * https://github.com/carhartl/jquery-cookie
1131
+ *
1132
+ * Copyright 2011, Klaus Hartl
1133
+ * Dual licensed under the MIT or GPL Version 2 licenses.
1134
+ * http://www.opensource.org/licenses/mit-license.php
1135
+ * http://www.opensource.org/licenses/GPL-2.0
1136
+ */
1137
+ (function ($, document, undefined) {
1138
+
1139
+ var pluses = /\+/g;
1140
+
1141
+ function raw(s) {
1142
+ return s;
1143
+ }
1144
+
1145
+ function decoded(s) {
1146
+ return decodeURIComponent(s.replace(pluses, ' '));
1147
+ }
1148
+
1149
+ var config = $.cookie = function (key, value, options) {
1150
+
1151
+ // write
1152
+ if (value !== undefined) {
1153
+ options = $.extend({}, config.defaults, options);
1154
+
1155
+ if (value === null) {
1156
+ options.expires = -1;
1157
+ }
1158
+
1159
+ if (typeof options.expires === 'number') {
1160
+ var days = options.expires, t = options.expires = new Date();
1161
+ t.setDate(t.getDate() + days);
1162
+ }
1163
+
1164
+ value = config.json ? JSON.stringify(value) : String(value);
1165
+
1166
+ return (document.cookie = [
1167
+ encodeURIComponent(key), '=', config.raw ? value : encodeURIComponent(value),
1168
+ options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
1169
+ options.path ? '; path=' + options.path : '',
1170
+ options.domain ? '; domain=' + options.domain : '',
1171
+ options.secure ? '; secure' : ''
1172
+ ].join(''));
1173
+ }
1174
+
1175
+ // read
1176
+ var decode = config.raw ? raw : decoded;
1177
+ var cookies = document.cookie.split('; ');
1178
+ for (var i = 0, l = cookies.length; i < l; i++) {
1179
+ var parts = cookies[i].split('=');
1180
+ if (decode(parts.shift()) === key) {
1181
+ var cookie = decode(parts.join('='));
1182
+ return config.json ? JSON.parse(cookie) : cookie;
1183
+ }
1184
+ }
1185
+
1186
+ return null;
1187
+ };
1188
+
1189
+ config.defaults = {};
1190
+
1191
+ $.removeCookie = function (key, options) {
1192
+ if ($.cookie(key) !== null) {
1193
+ $.cookie(key, null, options);
1194
+ return true;
1195
+ }
1196
+ return false;
1197
+ };
1198
+
1199
+ })(jQuery, document);
js/admin-calendar.min.js ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function(b){b(document).ready(function(){function a(){return b("<span class='fc-header-goto'><input type='hidden' id='miniCalendar'/></span>")}function c(e){var i=e.categories,f="<select class='eo-cal-filter' id='eo-event-cat'>";f+="<option value=''>"+e.buttonText.cat+"</option>";for(e=0;e<i.length;e++)f+="<option class='cat-slug-"+i[e].slug+" cat' value='"+i[e].slug+"'>"+i[e].name+"</option>";f+="</select>";return b("<span class='fc-header-dropdown filter-category'></span>").append(f)}function d(e){var i=
2
+ e.venues,f="<select class='eo-cal-filter' id='eo-event-venue'>";f+="<option value=''>"+e.buttonText.venue+"</option>";for(e=0;e<i.length;e++)f+="<option value='"+i[e].term_id+"'>"+i[e].name+"</option>";f+="</select>";return b("<span class='fc-header-dropdown filter-venue'></span>").append(f)}b("#eo-dialog-tabs").tabs();b(".eo-dialog").dialog({autoOpen:false,width:527,modal:true});b("#events-meta").parent().find(".ui-dialog-titlebar-close").appendTo(".ui-tabs-nav").closest(".ui-dialog").children(".ui-dialog-titlebar").remove();
3
+ var h=b("#eofc_time_format").is(":checked")?"HH:mm":"h:mmtt",g=b.cookie("eo_admin_cal_last_viewed_date")?new Date(b.cookie("eo_admin_cal_last_viewed_date")):new Date,j=jQuery("#eo_admin_calendar").fullCalendar({firstDay:parseInt(EO_Ajax.startday),date:g.getDate(),month:g.getMonth(),year:g.getFullYear(),defaultView:b.cookie("eo_admin_cal_last_view")?b.cookie("eo_admin_cal_last_view"):"month",editable:false,lazyFetching:"true",eventColor:"#21759B",theme:true,customButtons:{category:c,venue:d,"goto":a},
4
+ buttonText:{today:EO_Ajax.locale.today,month:EO_Ajax.locale.month,week:EO_Ajax.locale.week,day:EO_Ajax.locale.day,cat:EO_Ajax.locale.cat,venue:EO_Ajax.locale.venue},monthNames:EO_Ajax.locale.monthNames,monthNamesShort:EO_Ajax.locale.monthAbbrev,dayNames:EO_Ajax.locale.dayNames,dayNamesShort:EO_Ajax.locale.dayAbbrev,header:{left:"title",center:"category venue",right:"prev goto today next"},buttonIcons:false,buttonui:true,events:function(e,i,f){jQuery.ajax({url:EO_Ajax.ajaxurl+"?action=event-admin-cal",
5
+ dataType:"JSON",data:{start:jQuery.fullCalendar.formatDate(e,"yyyy-MM-dd"),end:jQuery.fullCalendar.formatDate(i,"yyyy-MM-dd")},success:function(k){f(k)}})},categories:EO_Ajax.categories,venues:EO_Ajax.venues,selectable:true,selectHelper:true,eventRender:function(e){var i=jQuery(".filter-category .eo-cal-filter").val(),f=jQuery(".filter-venue .eo-cal-filter").val();if(typeof i!=="undefined"&&i!=""&&jQuery.inArray(i,e.category)<0)return"<div></div>";if(typeof f!=="undefined"&&f!=""&&f!=e.venue)return"<div></div>"},
6
+ viewDisplay:function(e){var i=jQuery.fullCalendar.formatDate(e.start,"yyyy-MM-dd");e=e.name;var f=new Date;f=new Date(f.getTime()+6E5);b.cookie("eo_admin_cal_last_viewed_date",i,{expires:f});b.cookie("eo_admin_cal_last_view",e,{expires:f})},weekMode:"variable",aspectRatio:1.5,loading:function(e){e?jQuery("#loading").show():jQuery("#loading").hide()},timeFormat:h,axisFormat:h,eventClick:function(e,i){i.preventDefault();jQuery("#eo-dialog-tabs ul li").each(function(){var f=b(this).attr("id").substring(14);
7
+ jQuery("#eo-dialog-tabs #"+b(this).attr("id")+"-content").html(e[f])});b("#events-meta").dialog("open")},select:function(e,i,f,k){if(EO_Ajax.perm_edit){k.preventDefault();k=jQuery(this)[0].calendar.options;var l=jQuery.fullCalendar.formatDate(e,"yyyy-MM-dd"),m=jQuery.fullCalendar.formatDate(e,"HH:mm"),n=jQuery.fullCalendar.formatDate(i,"yyyy-MM-dd"),o=jQuery.fullCalendar.formatDate(i,"HH:mm");if(f){h="ddd, dS MMMM";f=1}else{h="ddd, dS MMMM h(:mm)tt";f=0}if(l==n){e=jQuery.fullCalendar.formatDate(e,
8
+ h,k);f||(e=e+" &mdash; "+jQuery.fullCalendar.formatDate(i,"h(:mm)tt",k))}else e=jQuery.fullCalendar.formatDate(e,h,k)+" &mdash; "+jQuery.fullCalendar.formatDate(i,h,k);b("#eo_event_create_cal input[name='eo_event[event_title]']").val("");b("#eo_event_create_cal input.ui-autocomplete-input").val("");b("#eo_event_create_cal textarea[name='eo_event[event_content]']").val("");b("#eo_event_create_cal input[name='eo_event[StartDate]']").val(l);b("#eo_event_create_cal input[name='eo_event[StartTime]']").val(m);
9
+ b("#eo_event_create_cal input[name='eo_event[EndDate]']").val(n);b("#eo_event_create_cal input[name='eo_event[FinishTime]']").val(o);b("#eo_event_create_cal input[name='eo_event[allday]']").val(f);b("#eo_event_create_cal td#date").html(e);b("#eo_event_create_cal").dialog("open");b("form.eo_cal input[type='submit']").removeAttr("disabled");b("form.eo_cal input#reset").click(function(){b("#eo_event_create_cal").dialog("close")})}}});b("#eofc_time_format").change(function(){h=b("#eofc_time_format").is(":checked")?
10
+ "HH:mm":"h:mmtt";j.fullCalendar("option","timeFormat",h);b.post(ajaxurl,{action:"eofc-format-time",is24:b("#eofc_time_format").is(":checked")})});b(".view-button").click(function(e){e.preventDefault();b(".view-button").removeClass("active");j.fullCalendar("changeView",b(this).attr("id"));b(this).addClass("active")});b("#miniCalendar").datepicker({dateFormat:"DD, d MM, yy",firstDay:parseInt(EO_Ajax.startday),changeMonth:true,monthNamesShort:EO_Ajax.locale.monthAbbrev,dayNamesMin:EO_Ajax.locale.dayAbbrev,
11
+ changeYear:true,showOn:"button",buttonText:EO_Ajax.locale.gotodate,onSelect:function(e){j.fullCalendar("gotoDate",new Date(Date.parse(e)))}});b("button.ui-datepicker-trigger").button();b(".eo-cal-filter").change(function(){j.fullCalendar("rerenderEvents")});b(".filter-venue .eo-cal-filter").selectmenu({wrapperElement:"<span class='fc-header-filter'></span>"});b(".filter-category .eo-cal-filter").selectmenu({wrapperElement:"<span class='fc-header-filter'></span>",icons:[{find:".cat"}]});g=b("#eo-event-venue-button").width()+
12
+ 30;b("#eo-event-venue-button").width(g+"px");b("#eo-event-venue-menu").width(g+"px");g=b("#eo-event-cat-button").width()+30;b("#eo-event-cat-button").width(g+"px");b("#eo-event-cat-menu").width(g+"px")})})(jQuery);
13
+ (function(b){b.widget("ui.selectmenu",{options:{appendTo:"body",typeAhead:1E3,style:"dropdown",positionOptions:{my:"left top",at:"left bottom",offset:null},width:null,menuWidth:null,handleWidth:26,maxHeight:null,icons:null,format:null,escapeHtml:false,bgImage:function(){}},_create:function(){var a=this,c=this.options,d=(this.element.attr("id")||"ui-selectmenu-"+Math.random().toString(16).slice(2,10)).replace(/(:|\.)/g,"");this.ids=[d,d+"-button",d+"-menu"];this._safemouseup=true;this.isOpen=false;
14
+ this.newelement=b("<a />",{"class":this.widgetBaseClass+" ui-widget ui-state-default ui-corner-all",id:this.ids[1],role:"button",href:"#nogo",tabindex:this.element.attr("disabled")?1:0,"aria-haspopup":true,"aria-owns":this.ids[2]});this.newelementWrap=b("<span />").append(this.newelement).insertAfter(this.element);(d=this.element.attr("tabindex"))&&this.newelement.attr("tabindex",d);this.newelement.data("selectelement",this.element);this.selectmenuIcon=b('<span class="'+this.widgetBaseClass+'-icon ui-icon"></span>').prependTo(this.newelement);
15
+ this.newelement.prepend('<span class="'+a.widgetBaseClass+'-status" />');this.element.bind({"click.selectmenu":function(h){a.newelement.focus();h.preventDefault()}});this.newelement.bind("mousedown.selectmenu",function(h){a._toggle(h,true);if(c.style=="popup"){a._safemouseup=false;setTimeout(function(){a._safemouseup=true},300)}return false}).bind("click.selectmenu",function(){return false}).bind("keydown.selectmenu",function(h){var g=false;switch(h.keyCode){case b.ui.keyCode.ENTER:g=true;break;case b.ui.keyCode.SPACE:a._toggle(h);
16
+ break;case b.ui.keyCode.UP:h.altKey?a.open(h):a._moveSelection(-1);break;case b.ui.keyCode.DOWN:h.altKey?a.open(h):a._moveSelection(1);break;case b.ui.keyCode.LEFT:a._moveSelection(-1);break;case b.ui.keyCode.RIGHT:a._moveSelection(1);break;case b.ui.keyCode.TAB:g=true;break;case b.ui.keyCode.PAGE_UP:case b.ui.keyCode.HOME:a.index(0);break;case b.ui.keyCode.PAGE_DOWN:case b.ui.keyCode.END:a.index(a._optionLis.length);break;default:g=true}return g}).bind("keypress.selectmenu",function(h){h.which>0&&
17
+ a._typeAhead(h.which,"mouseup");return true}).bind("mouseover.selectmenu",function(){c.disabled||b(this).addClass("ui-state-hover")}).bind("mouseout.selectmenu",function(){c.disabled||b(this).removeClass("ui-state-hover")}).bind("focus.selectmenu",function(){c.disabled||b(this).addClass("ui-state-focus")}).bind("blur.selectmenu",function(){c.disabled||b(this).removeClass("ui-state-focus")});b(document).bind("mousedown.selectmenu-"+this.ids[0],function(h){a.isOpen&&a.close(h)});this.element.bind("click.selectmenu",
18
+ function(){a._refreshValue()}).bind("focus.selectmenu",function(){a.newelement&&a.newelement[0].focus()});if(!c.width)c.width=this.element.outerWidth();this.newelement.width(c.width);this.element.hide();this.list=b("<ul />",{"class":"ui-widget ui-widget-content","aria-hidden":true,role:"listbox","aria-labelledby":this.ids[1],id:this.ids[2]});this.listWrap=b("<div />",{"class":a.widgetBaseClass+"-menu"}).append(this.list).appendTo(c.appendTo);this.list.bind("keydown.selectmenu",function(h){var g=false;
19
+ switch(h.keyCode){case b.ui.keyCode.UP:h.altKey?a.close(h,true):a._moveFocus(-1);break;case b.ui.keyCode.DOWN:h.altKey?a.close(h,true):a._moveFocus(1);break;case b.ui.keyCode.LEFT:a._moveFocus(-1);break;case b.ui.keyCode.RIGHT:a._moveFocus(1);break;case b.ui.keyCode.HOME:a._moveFocus(":first");break;case b.ui.keyCode.PAGE_UP:a._scrollPage("up");break;case b.ui.keyCode.PAGE_DOWN:a._scrollPage("down");break;case b.ui.keyCode.END:a._moveFocus(":last");break;case b.ui.keyCode.ENTER:case b.ui.keyCode.SPACE:a.close(h,
20
+ true);b(h.target).parents("li:eq(0)").trigger("mouseup");break;case b.ui.keyCode.TAB:g=true;a.close(h,true);b(h.target).parents("li:eq(0)").trigger("mouseup");break;case b.ui.keyCode.ESCAPE:a.close(h,true);break;default:g=true}return g}).bind("keypress.selectmenu",function(h){h.which>0&&a._typeAhead(h.which,"focus");return true}).bind("mousedown.selectmenu mouseup.selectmenu",function(){return false});b(window).bind("resize.selectmenu-"+this.ids[0],b.proxy(a.close,this))},_init:function(){var a=this,
21
+ c=this.options,d=[];this.element.find("option").each(function(){var f=b(this);d.push({value:f.attr("value"),text:a._formatText(f.text(),f),selected:f.attr("selected"),disabled:f.attr("disabled"),classes:f.attr("class"),typeahead:f.attr("typeahead"),parentOptGroup:f.parent("optgroup"),bgImage:c.bgImage.call(f)})});var h=a.options.style=="popup"?" ui-state-active":"";this.list.html("");if(d.length)for(var g=0;g<d.length;g++){var j={role:"presentation"};if(d[g].disabled)j["class"]=this.namespace+"-state-disabled";
22
+ var e={html:d[g].text||"&nbsp;",href:"#nogo",tabindex:-1,role:"option","aria-selected":false};if(d[g].disabled)e["aria-disabled"]=d[g].disabled;if(d[g].typeahead)e.typeahead=d[g].typeahead;e=b("<a/>",e).bind("focus.selectmenu",function(){b(this).parent().mouseover()}).bind("blur.selectmenu",function(){b(this).parent().mouseout()});j=b("<li/>",j).append(e).data("index",g).addClass(d[g].classes).data("optionClasses",d[g].classes||"").bind("mouseup.selectmenu",function(f){if(a._safemouseup&&!a._disabled(f.currentTarget)&&
23
+ !a._disabled(b(f.currentTarget).parents("ul>li."+a.widgetBaseClass+"-group "))){a.index(b(this).data("index"));a.select(f);a.close(f,true)}return false}).bind("click.selectmenu",function(){return false}).bind("mouseover.selectmenu",function(){if(!b(this).hasClass(a.namespace+"-state-disabled")&&!b(this).parent("ul").parent("li").hasClass(a.namespace+"-state-disabled")){a._selectedOptionLi().addClass(h);a._focusedOptionLi().removeClass(a.widgetBaseClass+"-item-focus ui-state-hover");b(this).removeClass("ui-state-active").addClass(a.widgetBaseClass+
24
+ "-item-focus ui-state-hover")}}).bind("mouseout.selectmenu",function(){b(this).is(a._selectedOptionLi())&&b(this).addClass(h);b(this).removeClass(a.widgetBaseClass+"-item-focus ui-state-hover")});if(d[g].parentOptGroup.length){e=a.widgetBaseClass+"-group-"+this.element.find("optgroup").index(d[g].parentOptGroup);this.list.find("li."+e).length?this.list.find("li."+e+":last ul").append(j):b(' <li role="presentation" class="'+a.widgetBaseClass+"-group "+e+(d[g].parentOptGroup.attr("disabled")?" "+this.namespace+
25
+ '-state-disabled" aria-disabled="true"':'"')+'><span class="'+a.widgetBaseClass+'-group-label">'+d[g].parentOptGroup.attr("label")+"</span><ul></ul></li> ").appendTo(this.list).find("ul").append(j)}else j.appendTo(this.list);if(c.icons)for(var i in c.icons)if(j.is(c.icons[i].find)){j.data("optionClasses",d[g].classes+" "+a.widgetBaseClass+"-hasIcon").addClass(a.widgetBaseClass+"-hasIcon");e=c.icons[i].icon||"";j.find("a:eq(0)").prepend('<span class="'+a.widgetBaseClass+"-item-icon ui-icon "+e+'"></span>');
26
+ d[g].bgImage&&j.find("span").css("background-image",d[g].bgImage)}}else b('<li role="presentation"><a href="#nogo" tabindex="-1" role="option"></a></li>').appendTo(this.list);g=c.style=="dropdown";this.newelement.toggleClass(a.widgetBaseClass+"-dropdown",g).toggleClass(a.widgetBaseClass+"-popup",!g);this.list.toggleClass(a.widgetBaseClass+"-menu-dropdown ui-corner-bottom",g).toggleClass(a.widgetBaseClass+"-menu-popup ui-corner-all",!g).find("li:first").toggleClass("ui-corner-top",!g).end().find("li:last").addClass("ui-corner-bottom");
27
+ this.selectmenuIcon.toggleClass("ui-icon-triangle-1-s",g).toggleClass("ui-icon-triangle-2-n-s",!g);c.style=="dropdown"?this.list.width(c.menuWidth?c.menuWidth:c.width):this.list.width(c.menuWidth?c.menuWidth:c.width-c.handleWidth);this.list.css("height","auto");g=this.listWrap.height();i=b(window).height();i=c.maxHeight?Math.min(c.maxHeight,i):i/3;g>i&&this.list.height(i);this._optionLis=this.list.find("li:not(."+a.widgetBaseClass+"-group)");this.element.attr("disabled")?this.disable():this.enable();
28
+ this._refreshValue();this._selectedOptionLi().addClass(this.widgetBaseClass+"-item-focus");clearTimeout(this.refreshTimeout);this.refreshTimeout=window.setTimeout(function(){a._refreshPosition()},200)},destroy:function(){this.element.removeData(this.widgetName).removeClass(this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").removeAttr("aria-disabled").unbind(".selectmenu");b(window).unbind(".selectmenu-"+this.ids[0]);b(document).unbind(".selectmenu-"+this.ids[0]);this.newelementWrap.remove();
29
+ this.listWrap.remove();this.element.unbind(".selectmenu").show();b.Widget.prototype.destroy.apply(this,arguments)},_typeAhead:function(a,c){var d=this,h=String.fromCharCode(a).toLowerCase(),g=a=null;if(d._typeAhead_timer){window.clearTimeout(d._typeAhead_timer);d._typeAhead_timer=undefined}d._typeAhead_chars=(d._typeAhead_chars===undefined?"":d._typeAhead_chars).concat(h);if(d._typeAhead_chars.length<2||d._typeAhead_chars.substr(-2,1)===h&&d._typeAhead_cycling){d._typeAhead_cycling=true;a=h}else{d._typeAhead_cycling=
30
+ false;a=d._typeAhead_chars}h=(c!=="focus"?this._selectedOptionLi().data("index"):this._focusedOptionLi().data("index"))||0;for(var j=0;j<this._optionLis.length;j++)if(this._optionLis.eq(j).text().substr(0,a.length).toLowerCase()===a)if(d._typeAhead_cycling){if(g===null)g=j;if(j>h){g=j;break}}else g=j;g!==null&&this._optionLis.eq(g).find("a").trigger(c);d._typeAhead_timer=window.setTimeout(function(){d._typeAhead_timer=undefined;d._typeAhead_chars=undefined;d._typeAhead_cycling=undefined},d.options.typeAhead)},
31
+ _uiHash:function(){var a=this.index();return{index:a,option:b("option",this.element).get(a),value:this.element[0].value}},open:function(a){var c=this,d=this.options;if(c.newelement.attr("aria-disabled")!="true"){c._closeOthers(a);c.newelement.addClass("ui-state-active");c.list.attr("aria-hidden",false);c.listWrap.addClass(c.widgetBaseClass+"-open");var h=this._selectedOptionLi();d.style=="dropdown"?c.newelement.removeClass("ui-corner-all").addClass("ui-corner-top"):this.list.css("left",-5000).scrollTop(this.list.scrollTop()+
32
+ h.position().top-this.list.outerHeight()/2+h.outerHeight()/2).css("left","auto");c._refreshPosition();d=h.find("a");d.length&&d[0].focus();c.isOpen=true;c._trigger("open",a,c._uiHash())}},close:function(a,c){if(this.newelement.is(".ui-state-active")){this.newelement.removeClass("ui-state-active");this.listWrap.removeClass(this.widgetBaseClass+"-open");this.list.attr("aria-hidden",true);this.options.style=="dropdown"&&this.newelement.removeClass("ui-corner-top").addClass("ui-corner-all");c&&this.newelement.focus();
33
+ this.isOpen=false;this._trigger("close",a,this._uiHash())}},change:function(a){this.element.trigger("change");this._trigger("change",a,this._uiHash())},select:function(a){if(this._disabled(a.currentTarget))return false;this._trigger("select",a,this._uiHash())},widget:function(){return this.listWrap.add(this.newelementWrap)},_closeOthers:function(a){b("."+this.widgetBaseClass+".ui-state-active").not(this.newelement).each(function(){b(this).data("selectelement").selectmenu("close",a)});b("."+this.widgetBaseClass+
34
+ ".ui-state-hover").trigger("mouseout")},_toggle:function(a,c){this.isOpen?this.close(a,c):this.open(a)},_formatText:function(a,c){if(this.options.format)a=this.options.format(a,c);else if(this.options.escapeHtml)a=b("<div />").text(a).html();return a},_selectedIndex:function(){return this.element[0].selectedIndex},_selectedOptionLi:function(){return this._optionLis.eq(this._selectedIndex())},_focusedOptionLi:function(){return this.list.find("."+this.widgetBaseClass+"-item-focus")},_moveSelection:function(a,
35
+ c){if(!this.options.disabled){var d=parseInt(this._selectedOptionLi().data("index")||0,10)+a;if(d<0)d=0;if(d>this._optionLis.size()-1)d=this._optionLis.size()-1;if(d===c)return false;if(this._optionLis.eq(d).hasClass(this.namespace+"-state-disabled")){a>0?++a:--a;this._moveSelection(a,d)}else this._optionLis.eq(d).trigger("mouseover").trigger("mouseup")}},_moveFocus:function(a,c){var d=isNaN(a)?parseInt(this._optionLis.filter(a).data("index"),10):parseInt(this._focusedOptionLi().data("index")||0,
36
+ 10)+a;if(d<0)d=0;if(d>this._optionLis.size()-1)d=this._optionLis.size()-1;if(d===c)return false;c=this.widgetBaseClass+"-item-"+Math.round(Math.random()*1E3);this._focusedOptionLi().find("a:eq(0)").attr("id","");if(this._optionLis.eq(d).hasClass(this.namespace+"-state-disabled")){a>0?++a:--a;this._moveFocus(a,d)}else this._optionLis.eq(d).find("a:eq(0)").attr("id",c).focus();this.list.attr("aria-activedescendant",c)},_scrollPage:function(a){var c=Math.floor(this.list.outerHeight()/this._optionLis.first().outerHeight());
37
+ c=a=="up"?-c:c;this._moveFocus(c)},_setOption:function(a,c){this.options[a]=c;if(a=="disabled"){c&&this.close();this.element.add(this.newelement).add(this.list)[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").attr("aria-disabled",c)}},disable:function(a,c){if(typeof a=="undefined")this._setOption("disabled",true);else c=="optgroup"?this._disableOptgroup(a):this._disableOption(a)},enable:function(a,c){if(typeof a=="undefined")this._setOption("disabled",
38
+ false);else c=="optgroup"?this._enableOptgroup(a):this._enableOption(a)},_disabled:function(a){return b(a).hasClass(this.namespace+"-state-disabled")},_disableOption:function(a){var c=this._optionLis.eq(a);if(c){c.addClass(this.namespace+"-state-disabled").find("a").attr("aria-disabled",true);this.element.find("option").eq(a).attr("disabled","disabled")}},_enableOption:function(a){var c=this._optionLis.eq(a);if(c){c.removeClass(this.namespace+"-state-disabled").find("a").attr("aria-disabled",false);
39
+ this.element.find("option").eq(a).removeAttr("disabled")}},_disableOptgroup:function(a){var c=this.list.find("li."+this.widgetBaseClass+"-group-"+a);if(c){c.addClass(this.namespace+"-state-disabled").attr("aria-disabled",true);this.element.find("optgroup").eq(a).attr("disabled","disabled")}},_enableOptgroup:function(a){var c=this.list.find("li."+this.widgetBaseClass+"-group-"+a);if(c){c.removeClass(this.namespace+"-state-disabled").attr("aria-disabled",false);this.element.find("optgroup").eq(a).removeAttr("disabled")}},
40
+ index:function(a){if(arguments.length)if(!this._disabled(b(this._optionLis[a]))&&a!=this._selectedIndex()){this.element[0].selectedIndex=a;this._refreshValue();this.change()}else return false;else return this._selectedIndex()},value:function(a){if(arguments.length&&a!=this.element[0].value){this.element[0].value=a;this._refreshValue();this.change()}else return this.element[0].value},_refreshValue:function(){var a=this.options.style=="popup"?" ui-state-active":"",c=this.widgetBaseClass+"-item-"+Math.round(Math.random()*
41
+ 1E3);this.list.find("."+this.widgetBaseClass+"-item-selected").removeClass(this.widgetBaseClass+"-item-selected"+a).find("a").attr("aria-selected","false").attr("id","");this._selectedOptionLi().addClass(this.widgetBaseClass+"-item-selected"+a).find("a").attr("aria-selected","true").attr("id",c);a=this.newelement.data("optionClasses")?this.newelement.data("optionClasses"):"";var d=this._selectedOptionLi().data("optionClasses")?this._selectedOptionLi().data("optionClasses"):"";this.newelement.removeClass(a).data("optionClasses",
42
+ d).addClass(d).find("."+this.widgetBaseClass+"-status").html(this._selectedOptionLi().find("a:eq(0)").html());this.list.attr("aria-activedescendant",c)},_refreshPosition:function(){var a=this.options;if(a.style=="popup"&&!a.positionOptions.offset){var c=this._selectedOptionLi();c="0 "+(this.list.offset().top-c.offset().top-(this.newelement.outerHeight()+c.outerHeight())/2)}this.listWrap.removeAttr("style").zIndex(this.element.zIndex()+1).position({of:a.positionOptions.of||this.newelement,my:a.positionOptions.my,
43
+ at:a.positionOptions.at,offset:a.positionOptions.offset||c,collision:a.positionOptions.collision||(a.style=="popup"?"fit":"flip")})}})})(jQuery);
44
+ (function(b,a,c){function d(e){return e}function h(e){return decodeURIComponent(e.replace(g," "))}var g=/\+/g,j=b.cookie=function(e,i,f){if(i!==c){f=b.extend({},j.defaults,f);if(i===null)f.expires=-1;if(typeof f.expires==="number"){var k=f.expires,l=f.expires=new Date;l.setDate(l.getDate()+k)}i=j.json?JSON.stringify(i):String(i);return a.cookie=[encodeURIComponent(e),"=",j.raw?i:encodeURIComponent(i),f.expires?"; expires="+f.expires.toUTCString():"",f.path?"; path="+f.path:"",f.domain?"; domain="+
45
+ f.domain:"",f.secure?"; secure":""].join("")}i=j.raw?d:h;f=a.cookie.split("; ");k=0;for(l=f.length;k<l;k++){var m=f[k].split("=");if(i(m.shift())===e){e=i(m.join("="));return j.json?JSON.parse(e):e}}return null};j.defaults={};b.removeCookie=function(e,i){if(b.cookie(e)!==null){b.cookie(e,null,i);return true}return false}})(jQuery,document);
js/event-manager.js ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ( function( window, undefined ) {
2
+ "use strict";
3
+ var document = window.document;
4
+
5
+ /**
6
+ * Handles managing all events for whatever you plug it into. Priorities for hooks are based on lowest to highest in
7
+ * that, lowest priority hooks are fired first.
8
+ */
9
+ var EventManager = function() {
10
+ /**
11
+ * Maintain a reference to the object scope so 'this' never becomes confusing.
12
+ */
13
+ var SELF = this;
14
+
15
+ /**
16
+ * Contains the hooks that get registered with this EventManager. The array for storage utilizes a "flat"
17
+ * object literal such that looking up the hook utilizes the native object literal hash.
18
+ */
19
+ var STORAGE = {
20
+ actions : {},
21
+ filters : {}
22
+ };
23
+
24
+ /**
25
+ * Adds an action to the event manager.
26
+ *
27
+ * @param action Must contain namespace.identifier
28
+ * @param callback Must be a valid callback function before this action is added
29
+ * @param priority Defaults to 10
30
+ */
31
+ SELF.addAction = function( action, callback, priority ) {
32
+ if( _validateNamespace( action ) === false || typeof callback !== 'function' ) {
33
+ return SELF;
34
+ }
35
+
36
+ priority = parseInt( ( priority || 10 ), 10 );
37
+ _addHook( 'actions', action, callback, priority );
38
+ return SELF;
39
+ };
40
+
41
+ /**
42
+ * Performs an action if it exists. You can pass as many arguments as you want to this function; the only rule is
43
+ * that the first argument must always be the action.
44
+ */
45
+ SELF.doAction = function( /* action, arg1, arg2, ... */ ) {
46
+ var args = Array.prototype.slice.call( arguments );
47
+ var action = args.shift();
48
+
49
+ if( _validateNamespace( action ) === false ) {
50
+ return SELF;
51
+ }
52
+
53
+ _runHook( 'actions', action, args );
54
+
55
+ return SELF;
56
+ };
57
+
58
+ /**
59
+ * Removes the specified action if it contains a namespace.identifier & exists.
60
+ *
61
+ * @param action The action to remove
62
+ */
63
+ SELF.removeAction = function( action ) {
64
+ if( _validateNamespace( action ) === false ) {
65
+ return SELF;
66
+ }
67
+
68
+ _removeHook( 'actions', action );
69
+ return SELF;
70
+ };
71
+
72
+ /**
73
+ * Adds a filter to the event manager.
74
+ *
75
+ * @param filter Must contain namespace.identifier
76
+ * @param callback Must be a valid callback function before this action is added
77
+ * @param priority Defaults to 10
78
+ */
79
+ SELF.addFilter = function( filter, callback, priority ) {
80
+ if( _validateNamespace( filter ) === false || typeof callback !== 'function' ) {
81
+ return SELF;
82
+ }
83
+
84
+ priority = parseInt( ( priority || 10 ), 10 );
85
+ _addHook( 'filters', filter, callback, priority );
86
+ return SELF;
87
+ };
88
+
89
+ /**
90
+ * Performs a filter if it exists. You should only ever pass 1 argument to be filtered. The only rule is that
91
+ * the first argument must always be the filter.
92
+ */
93
+ SELF.applyFilter = function( filter, param ) {
94
+ if( _validateNamespace( filter ) === false ) {
95
+ return SELF;
96
+ }
97
+
98
+ return _runHook( 'filters', filter, param );
99
+ };
100
+
101
+ /**
102
+ * Removes the specified filter if it contains a namespace.identifier & exists.
103
+ *
104
+ * @param filter The action to remove
105
+ */
106
+ SELF.removeFilter = function( filter ) {
107
+ if( _validateNamespace( filter ) === false ) {
108
+ return SELF;
109
+ }
110
+
111
+ _removeHook( 'filters', filter );
112
+ return SELF;
113
+ };
114
+
115
+ /**
116
+ * Removes the specified hook by resetting the value of it.
117
+ *
118
+ * @param type Type of hook, either 'actions' or 'filters'
119
+ * @param hook The hook (namespace.identifier) to remove
120
+ * @private
121
+ */
122
+ var _removeHook = function( type, hook ) {
123
+ if( STORAGE[ type ][ hook ] ) {
124
+ STORAGE[ type ][ hook ] = [];
125
+ }
126
+ };
127
+
128
+ /**
129
+ * Validates that the hook has both a namespace and an identifier.
130
+ *
131
+ * @param hook The hook we are checking for namespace and identifier for.
132
+ * @return {Boolean} False if it does not contain both or is incorrect. True if it has an appropriate namespace & identifier.
133
+ * @private
134
+ */
135
+ var _validateNamespace = function( hook ) {
136
+ if( typeof hook !== 'string' ) {
137
+ return false;
138
+ }
139
+ var identifier = hook.replace( /^\s+|\s+$/i, '' ).split( '.' );
140
+ var namespace = identifier.shift();
141
+ identifier = identifier.join( '.' );
142
+
143
+ return ( namespace !== '' && identifier !== '' );
144
+ };
145
+
146
+ /**
147
+ * Adds the hook to the appropriate storage container
148
+ *
149
+ * @param type 'actions' or 'filters'
150
+ * @param hook The hook (namespace.identifier) to add to our event manager
151
+ * @param callback The function that will be called when the hook is executed.
152
+ * @param priority The priority of this hook. Must be an integer.
153
+ * @private
154
+ */
155
+ var _addHook = function( type, hook, callback, priority ) {
156
+ var hookObject = {
157
+ callback : callback,
158
+ priority : priority
159
+ };
160
+
161
+ // Utilize 'prop itself' : http://jsperf.com/hasownproperty-vs-in-vs-undefined/19
162
+ var hooks = STORAGE[ type ][ hook ];
163
+ if( hooks ) {
164
+ hooks.push( hookObject );
165
+ hooks = _hookInsertSort( hooks );
166
+ }
167
+ else {
168
+ hooks = [ hookObject ];
169
+ }
170
+
171
+ STORAGE[ type ][ hook ] = hooks;
172
+ };
173
+
174
+ /**
175
+ * Use an insert sort for keeping our hooks organized based on priority. This function is ridiculously faster
176
+ * than bubble sort, etc: http://jsperf.com/javascript-sort
177
+ *
178
+ * @param hooks The custom array containing all of the appropriate hooks to perform an insert sort on.
179
+ * @private
180
+ */
181
+ var _hookInsertSort = function( hooks ) {
182
+ var tmpHook, j, prevHook;
183
+ for( var i = 1, len = hooks.length; i < len; i++ ) {
184
+ tmpHook = hooks[ i ];
185
+ j = i;
186
+ while( ( prevHook = hooks[ j - 1 ] ) && prevHook.priority > tmpHook.priority ) {
187
+ hooks[ j ] = hooks[ j - 1 ];
188
+ --j;
189
+ }
190
+ hooks[ j ] = tmpHook;
191
+ }
192
+
193
+ return hooks;
194
+ };
195
+
196
+ /**
197
+ * Runs the specified hook. If it is an action, the value is not modified but if it is a filter, it is.
198
+ *
199
+ * @param type 'actions' or 'filters'
200
+ * @param hook The hook ( namespace.identifier ) to be ran.
201
+ * @param args Arguments to pass to the action/filter. If it's a filter, args is actually a single parameter.
202
+ * @private
203
+ */
204
+ var _runHook = function( type, hook, args ) {
205
+ var hooks = STORAGE[ type ][ hook ];
206
+ if( typeof hooks === 'undefined' ) {
207
+ if( type === 'filters' ) {
208
+ return args;
209
+ }
210
+ return false;
211
+ }
212
+
213
+ for( var i = 0, len = hooks.length; i < len; i++ ) {
214
+ if( type === 'actions' ) {
215
+ hooks[ i ].callback.apply( undefined, args );
216
+ }
217
+ else {
218
+ args = hooks[ i ].callback.apply( undefined, [ args ] );
219
+ }
220
+ }
221
+
222
+ if( type === 'actions' ) {
223
+ return true;
224
+ }
225
+
226
+ return args;
227
+ };
228
+
229
+ };
230
+
231
+ window.wp = window.wp || {};
232
+ window.wp.hooks = new EventManager();
233
+
234
+ } )( window );
js/event-manager.min.js ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ /**
2
+ * Module: WP-JS-Hooks
3
+ * Props: Carl Danley & 10up
4
+ */
5
+ (function(t,n){"use strict";t.document;var r=function(){var t=this,r={actions:{},filters:{}};t.addAction=function(n,r,i){return o(n)===!1||"function"!=typeof r?t:(i=parseInt(i||10,10),e("actions",n,r,i),t)},t.doAction=function(){var n=Array.prototype.slice.call(arguments),r=n.shift();return o(r)===!1?t:(a("actions",r,n),t)},t.removeAction=function(n){return o(n)===!1?t:(i("actions",n),t)},t.addFilter=function(n,r,i){return o(n)===!1||"function"!=typeof r?t:(i=parseInt(i||10,10),e("filters",n,r,i),t)},t.applyFilter=function(n,r){return o(n)===!1?t:a("filters",n,r)},t.removeFilter=function(n){return o(n)===!1?t:(i("filters",n),t)};var i=function(t,n){r[t][n]&&(r[t][n]=[])},o=function(t){if("string"!=typeof t)return!1;var n=t.replace(/^\s+|\s+$/i,"").split("."),r=n.shift();return n=n.join("."),""!==r&&""!==n},e=function(t,n,i,o){var e={callback:i,priority:o},a=r[t][n];a?(a.push(e),a=c(a)):a=[e],r[t][n]=a},c=function(t){for(var n,r,i,o=1,e=t.length;e>o;o++){for(n=t[o],r=o;(i=t[r-1])&&i.priority>n.priority;)t[r]=t[r-1],--r;t[r]=n}return t},a=function(t,i,o){var e=r[t][i];if(e===n)return"filters"===t?o:!1;for(var c=0,a=e.length;a>c;c++)"actions"===t?e[c].callback.apply(n,o):o=e[c].callback.apply(n,[o]);return"actions"===t?!0:o}};t.wp=t.wp||{},t.wp.hooks=new r})(window);
js/event.js ADDED
@@ -0,0 +1,2219 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var eo_occurrences_by_rule = new Array;
2
+ jQuery(document).ready(function($) {
3
+
4
+ //Workaround for indexOf in IE 7&8
5
+ if (!Array.prototype.indexOf)
6
+ {
7
+ Array.prototype.indexOf = function(elt /*, from*/)
8
+ {
9
+ var len = this.length;
10
+
11
+ var from = Number(arguments[1]) || 0;
12
+ from = (from < 0)
13
+ ? Math.ceil(from)
14
+ : Math.floor(from);
15
+ if (from < 0)
16
+ from += len;
17
+
18
+ for (; from < len; from++)
19
+ {
20
+ if (from in this &&
21
+ this[from] === elt)
22
+ return from;
23
+ }
24
+ return -1;
25
+ };
26
+ }
27
+
28
+ var eo_venue_obj;
29
+ if ($('#eo_occurrence_includes').length > 0) {
30
+ eo_include_dates = $('#eo_occurrence_includes').val().split(",");
31
+ eo_exclude_dates = $('#eo_occurrence_excludes').val().split(",");
32
+ }
33
+
34
+ // Update textareas to reflect inclusion, exclusion arrays
35
+ function eo_update_inc_ex_Input() {
36
+ $('#eo_occurrence_includes').val(eo_include_dates.join(',\r\n'));
37
+ $('#eo_occurrence_excludes').val(eo_exclude_dates.join(',\r\n'));
38
+ }
39
+
40
+ $(document).ready(function() {
41
+ //Venue picker - combobox
42
+ $.widget("ui.combobox", {
43
+ _create: function () {
44
+ var input;
45
+ var c = this.element.hide();
46
+ var d = c.children(":selected");
47
+ var e = d.val() ? d.text() : "";
48
+ var wrapper = $("<span>").addClass("ui-combobox eo-venue-input").insertAfter(c);
49
+ input = $("<input>").appendTo(wrapper).val(e).addClass("ui-combobox-input").autocomplete({
50
+ delay: 0,
51
+ minLength: 0,
52
+ source: function (a, b) {
53
+ $.getJSON(EO_Ajax_Event.ajaxurl + "?callback=?&action=eo-search-venue", a, function (a) {
54
+ var venues = $.map(a, function (a) {
55
+ a.label = a.name;
56
+ return a;
57
+ });
58
+ b(venues);
59
+ });
60
+ },
61
+ select: function (a, b) {
62
+ if ($("tr.venue_row").length > 0) {
63
+ if (b.item.term_id == 0) {
64
+ $("tr.venue_row").hide();
65
+ $("#eventorganiser_event_detail tr.eo-add-new-venue").hide();
66
+ } else {
67
+ $("tr.venue_row").show();
68
+ $("#eventorganiser_event_detail tr.eo-add-new-venue").hide();
69
+ }
70
+ eo_initialize_map(b.item.venue_lat, b.item.venue_lng)
71
+ $("#eo_venue_Lat").val(b.item.venue_lat);
72
+ $("#eo_venue_Lng").val(b.item.venue_lng);
73
+ }
74
+ $("#venue_select").removeAttr("selected");
75
+ $("#venue_select").val(b.item.term_id);
76
+ }
77
+ }).addClass("ui-widget-content ui-corner-left");
78
+ input.data("autocomplete")._renderItem = function (a, b) {
79
+ if (b.term_id == 0 ) {
80
+ return $("<li></li>").data("item.autocomplete", b).append("<a>" + b.label + "</a>").appendTo(a);
81
+ }
82
+ //Clean address
83
+ var address_array = [b.venue_address, b.venue_city, b.venue_state,b.venue_postcode,b.venue_country];
84
+ address_array = $.grep(address_array,function(n){
85
+ return(n);
86
+ });
87
+
88
+ return $("<li></li>").data("item.autocomplete", b)
89
+ .append("<a>" + b.label + "</br> <span style='font-size: 0.8em'><em>" +address_array.join(', ')+ "</span></em></a>")
90
+ .appendTo(a);
91
+ };
92
+
93
+ var button_wrappers = $("<span>").addClass("eo-venue-combobox-buttons").appendTo(wrapper);
94
+ $("<a style='vertical-align: top;margin: 0px -1px;padding: 0px;height: 21px;'>").attr("title", "Show All Items").appendTo(button_wrappers).button({
95
+ icons: {
96
+ primary: "ui-icon-triangle-1-s"
97
+ },
98
+ text: false
99
+ }).removeClass("ui-corner-all").addClass("ui-corner-right ui-combobox-toggle ui-combobox-button").click(function () {
100
+ if (input.autocomplete("widget").is(":visible")) {
101
+ input.autocomplete("close");
102
+ return
103
+ }
104
+ $(this).blur();
105
+ input.autocomplete("search", "");
106
+ input.focus();
107
+ });
108
+
109
+ if( 'event' == pagenow && EO_Ajax_Event.current_user_can.manage_venues ){
110
+ //Only add this on event edit page - i.e. not on calendar page.
111
+ $("<a style='vertical-align: top;margin: 0px -1px;padding: 0px;height: 21px;'>").attr("title", "Create New Venue").appendTo(button_wrappers).button({
112
+ icons: {
113
+ primary: "ui-icon-plus"
114
+ },
115
+ text: false
116
+ }).removeClass("ui-corner-all").addClass("ui-corner-right add-new-venue ui-combobox-button").click(function () {
117
+ $("#eventorganiser_event_detail tr.eo-add-new-venue").show();
118
+ $("tr.venue_row").show();
119
+ //Store existing venue details in case the user cancels creating a new on
120
+ eo_venue_obj={
121
+ id: $("#venue_select").val(),
122
+ label: $(".eo-venue-input input").val(),
123
+ lat: $("#eo_venue_Lat").val(),
124
+ lng: $("#eo_venue_Lng").val()
125
+ };
126
+ $("#venue_select").removeAttr("selected").val(0);
127
+ $('.eo-venue-combobox-select').hide();
128
+ $('.eo-venue-input input').val('');
129
+ eo_initialize_map(0,0);
130
+ var address = EO_Ajax_Event.location;
131
+ if( address ){
132
+ address =address.split("/");
133
+ address = address[address.length-1];
134
+ eventorganiser_code_address(address);
135
+ }else{
136
+ map.setZoom(1);
137
+ }
138
+ $(this).blur();
139
+ });
140
+ }
141
+ }
142
+ });
143
+ $("#venue_select").combobox();
144
+ });
145
+
146
+
147
+ $('.eo-add-new-venue-cancel').click(function(e){
148
+ e.preventDefault();
149
+ $('.eo-venue-combobox-select').show();
150
+ $('.eo-add-new-venue input').val('');
151
+
152
+ //Restore old venue details
153
+ eo_initialize_map(eo_venue_obj.lat,eo_venue_obj.lng);
154
+ $("#venue_select").val(eo_venue_obj.id);
155
+ $(".eo-venue-input input").val(eo_venue_obj.label);
156
+
157
+ $("#eventorganiser_event_detail tr.eo-add-new-venue").hide();
158
+ //$("#eventorganiser_event_detail tr.venue_row ").hide();
159
+ });
160
+
161
+
162
+ var locale = EO_Ajax_Event.locale;
163
+ function eo_update_event_form() {
164
+ var speed = 700;
165
+ var a = !$("#HWSEvent_rec").prop("checked");
166
+ $(".reoccurence .event-date :input").attr("disabled", a);
167
+ $(".reoccurence .event-date :input").toggleClass("ui-state-disabled", a);
168
+ a = !$("#eo_allday:checkbox").attr("checked");
169
+ $(".eo_time").attr("disabled", !a);
170
+ $(".eo_time").toggleClass("ui-state-disabled", !a);
171
+ switch ($("#HWSEventInput_Req").val()) {
172
+ case "once":
173
+ case "custom":
174
+ $("#HWSEvent_freq").val("1");
175
+ $(".reocurrence_row").hide();
176
+ $("#dayofweekrepeat").show();
177
+ $("#dayofmonthrepeat").show();
178
+ $(".reocurrence_row :input").attr("disabled", true);
179
+ break;
180
+ case "weekly":
181
+ $(".reocurrence_row :input").attr("disabled", false);
182
+ if ($("#HWSEvent_freq").val() > 1) {
183
+ $("#recpan").text(locale.weeks);
184
+ } else {
185
+ $("#recpan").text(locale.week);
186
+ }
187
+ $(".reocurrence_row").fadeIn(speed);
188
+ $("#dayofweekrepeat").fadeIn(speed);
189
+ $("#dayofweekrepeat :input").attr("disabled", false);
190
+ $("#dayofmonthrepeat").hide();
191
+ $("#dayofmonthrepeat :radio").attr("disabled", true);
192
+ break;
193
+ case "monthly":
194
+ $(".reocurrence_row :input").attr("disabled", false);
195
+ if ($("#HWSEvent_freq").val() > 1) {
196
+ $("#recpan").text(locale.months);
197
+ } else {
198
+ $("#recpan").text(locale.month);
199
+ }
200
+ $(".reocurrence_row").fadeIn(speed);
201
+ $("#dayofmonthrepeat").fadeIn(speed);
202
+ $("#dayofmonthrepeat :input").attr("disabled", false);
203
+ $("#dayofweekrepeat").hide();
204
+ $("#dayofweekrepeat :input").attr("disabled", true);
205
+ break;
206
+ case "daily":
207
+ $(".reocurrence_row :input").attr("disabled", false);
208
+ $(".reocurrence_row").fadeIn(speed);
209
+ if ($("#HWSEvent_freq").val() > 1) {
210
+ $("#recpan").text(locale.days);
211
+ } else {
212
+ $("#recpan").text(locale.day);
213
+ }
214
+ $("#dayofweekrepeat").hide();
215
+ $("#dayofweekrepeat :input").attr("disabled", true);
216
+ $("#dayofmonthrepeat").hide();
217
+ $("#dayofmonthrepeat :radio").attr("disabled", true);
218
+ break;
219
+ case "yearly":
220
+ $(".reocurrence_row :input").attr("disabled", false);
221
+ $(".reocurrence_row").fadeIn(speed);
222
+ if ($("#HWSEvent_freq").val() > 1) {
223
+ $("#recpan").text(locale.years);
224
+ } else {
225
+ $("#recpan").text(locale.year);
226
+ }
227
+ $("#dayofweekrepeat").hide();
228
+ $("#dayofweekrepeat :input").attr("disabled", true);
229
+ $("#dayofmonthrepeat").hide();
230
+ $("#dayofmonthrepeat :radio").attr("disabled", true);
231
+ break;
232
+ }
233
+ if ($("#venue_select").val() === null) {
234
+ $("tr.venue_row").hide();
235
+ }
236
+ eo_produce_summary();
237
+ }
238
+
239
+ function eo_produce_summary() {
240
+ if ($("#HWSEventInput_Req").val() == "once") {
241
+ $("#event_summary").html("This event will be a one-time event");
242
+ return
243
+ }
244
+ var a = $("#from_date").datepicker("getDate");
245
+ var b = EO_Ajax_Event.locale.weekDay;
246
+ var c = new Array("SU", "MO", "TU", "WE", "TH", "FR", "SA");
247
+ var options = {
248
+ monthNamesShort: EO_Ajax_Event.locale.monthAbbrev,
249
+ dayNamesMin: EO_Ajax_Event.locale.dayAbbrev,
250
+ monthNames: EO_Ajax_Event.locale.monthNames
251
+ };
252
+
253
+ var frequency = parseInt($("#HWSEvent_freq").val());
254
+ var summary = locale.summary + " ";
255
+ switch ($("#HWSEventInput_Req").val()) {
256
+ case "custom":
257
+ case "daily":
258
+ if (frequency > 1) {
259
+ summary += sprintf(locale.dayPlural, frequency);
260
+ } else {
261
+ summary += locale.daySingle;
262
+ }
263
+ break;
264
+ case "weekly":
265
+ if (frequency > 1) {
266
+ summary += sprintf(locale.weekPlural, frequency);
267
+ } else {
268
+ summary += locale.weekSingle;
269
+ }
270
+ var selected = $("#dayofweekrepeat :checkbox:checked");
271
+ if (selected.length == 0) {
272
+ var day = a.getDay();
273
+ $("#dayofweekrepeat :checkbox[value='" + c[day] + "']").attr("checked", true);
274
+ }
275
+ selected = $("#dayofweekrepeat :checkbox:checked");
276
+ selected.each(function(a) {
277
+ if (a == 0)
278
+ summary = summary + " " + b[c.indexOf($(this).val())];
279
+ if (a > 0)
280
+ summary = summary + ", " + b[c.indexOf($(this).val())];
281
+ });
282
+ break;
283
+ case "monthly":
284
+ if (frequency > 1) {
285
+ summary += sprintf(locale.monthPlural, frequency);
286
+ } else {
287
+ summary += locale.monthSingle;
288
+ }
289
+ if ($("#dayofmonthrepeat :radio:checked").val() == "BYMONTHDAY=") {
290
+ summary = summary + " " + a.getDate() + eo_date_suffix(a);
291
+ } else {
292
+ day = a.getDay() % 7;
293
+ var n = parseInt(Math.floor((a.getDate() - 1) / 7));
294
+ var occurrence = locale.occurrence;
295
+ summary = summary + " " + occurrence[n] + " " + b[day];
296
+ }
297
+ break;
298
+ case "yearly":
299
+ if (frequency > 1) {
300
+ summary += sprintf(locale.yearPlural, frequency);
301
+ } else {
302
+ summary += locale.yearSingle;
303
+ }
304
+ summary = summary + " " + $.datepicker.formatDate("MM d", a, options) + eo_date_suffix(a);
305
+ break;
306
+ }
307
+ var d = $("#recend").datepicker("getDate");
308
+ if (d != null) {
309
+ summary = summary + " " + locale.until + " " + $.datepicker.formatDate("MM d'" + eo_date_suffix(d) + "' yy", d, options);
310
+ }
311
+ $("#event_summary").html(summary);
312
+ }
313
+
314
+ function eo_date_suffix(date) {
315
+ var suffix = ["th", "st", "nd", "rd"];
316
+ if (3 < date.getDate() && date.getDate() < 20) {
317
+ var s = 0;
318
+ } else {
319
+ var s = Math.min(date.getDate() % 10, 4) % 4;
320
+ }
321
+ return suffix[s];
322
+ };
323
+
324
+ if ($("#eventorganiser_detail #from_date, #eventorganiser_detail #to_date").length > 0) {
325
+ var dates = $("#eventorganiser_detail #from_date, #eventorganiser_detail #to_date").datepicker({
326
+ dateFormat: EO_Ajax_Event.format,
327
+ changeMonth: true,
328
+ changeYear: true,
329
+ monthNamesShort: EO_Ajax_Event.locale.monthAbbrev,
330
+ dayNamesMin: EO_Ajax_Event.locale.dayAbbrev,
331
+ firstDay: parseInt(EO_Ajax_Event.startday),
332
+ buttonImage: 'images/ui-icon-calendar.png',
333
+ onSelect: function(selectedDate) {
334
+ var option = this.id == "from_date" ? "minDate": "maxDate",
335
+ instance = $(this).data("datepicker"),
336
+ date = $.datepicker.parseDate(instance.settings.dateFormat || $.datepicker._defaults.dateFormat, selectedDate, instance.settings);
337
+ dates.not(this).datepicker("option", option, date);
338
+ if (this.id == "from_date") {
339
+ $("#recend").datepicker("option", "minDate", date);
340
+ }
341
+ eo_update_occurrencepicker_rules();
342
+ eo_update_event_form()
343
+ }
344
+ });
345
+ $("#recend").datepicker({
346
+ dateFormat: EO_Ajax_Event.format,
347
+ monthNamesShort: EO_Ajax_Event.locale.monthAbbrev,
348
+ dayNamesMin: EO_Ajax_Event.locale.dayAbbrev,
349
+ changeMonth: true,
350
+ changeYear: true,
351
+ firstDay: parseInt(EO_Ajax_Event.startday)
352
+ });
353
+ $('#HWSEvent_time, #HWSEvent_time2').timepicker({
354
+ showPeriodLabels: false,
355
+ hourText: EO_Ajax_Event.locale.hour,
356
+ minuteText: EO_Ajax_Event.locale.minute
357
+ });
358
+ $("#HWSEvent_rec").click(function() {
359
+ eo_update_event_form();
360
+ });
361
+ $(".reoccurence .event-date :input, .onetime .event-date :input").not('.eo_time').change(function(o) {
362
+ eo_update_event_form();
363
+ if ($(this).attr('id') != 'eo_allday') {
364
+ eo_update_occurrencepicker_rules();
365
+ };
366
+ });
367
+ eo_update_event_form();
368
+ var bool = !$(this).prop("checked");
369
+ $(".reoccurence .event-date :input").attr('disabled', bool);
370
+ $(".reoccurence .event-date :input").toggleClass('ui-state-disabled', bool);
371
+ }
372
+
373
+ //When rule changes, wipe include/exclude dates clean
374
+ function eo_update_occurrencepicker_rules() {
375
+ eo_exclude_dates = new Array();
376
+ eo_include_dates = new Array();
377
+ eo_update_inc_ex_Input();
378
+ eo_generate_dates_by_rule(eo_viewing_month[0], eo_viewing_month[1], {});
379
+ dp.datepicker("refresh");
380
+ };
381
+
382
+ //Show/hide calendar
383
+ $('.eo_occurrence_toogle').click(function(e) {
384
+ e.preventDefault();
385
+ e.stopPropagation();
386
+ dp.toggle();
387
+ if( dp.is(":visible") ){
388
+ $('.eo_occurrence_toogle').val(EO_Ajax_Event.locale.hideDates);
389
+ }else{
390
+ $('.eo_occurrence_toogle').val(EO_Ajax_Event.locale.showDates);
391
+ }
392
+ });
393
+
394
+ //Fires before each date. Decides what classes to add
395
+ function eo_pre_show_date(date) {
396
+ var date_str = $.datepicker.formatDate('yy-mm-dd', date);
397
+ var isEventful = eo_is_date_eventful(date_str);
398
+ if (isEventful[0]) {
399
+ return [true, "ui-state-active", ""];
400
+ }
401
+ return [true, "ui-state-disabled", ''];
402
+ }
403
+
404
+ //Is given date an occurrence of the event?
405
+ function eo_is_date_eventful(date) {
406
+ var index = $.inArray(date, eo_occurrences_by_rule);
407
+
408
+ if (index > -1) {
409
+ //Occurs by rule - is it excluded manually?
410
+ var excluded = $.inArray(date, eo_exclude_dates);
411
+ if (excluded > -1) {
412
+ return [false, excluded];
413
+ } else {
414
+ return [true, -1];
415
+ }
416
+ } else {
417
+ //Doesn't occurs by rule - is it included manually?
418
+ var included = $.inArray(date, eo_include_dates);
419
+ if (included > -1) {
420
+ return [true, included];
421
+ } else {
422
+ return [false, -1];
423
+ }
424
+ }
425
+ }
426
+
427
+ //Generate array of eventful dates in given month by the event's current rule.
428
+ function eo_generate_dates_by_rule(year, month, inst) {
429
+ //month is 1-12.
430
+ eo_occurrences_by_rule = new Array();
431
+ eo_viewing_month = [year, month];
432
+
433
+ var schedule = $('#HWSEventInput_Req').val();
434
+ var frequency = parseInt($('#HWSEvent_freq').val());
435
+
436
+ var start = $("#from_date").datepicker("getDate");
437
+ var end = $("#recend").datepicker("getDate");
438
+
439
+ //Get month start/end dates. Date expects month 0-11.
440
+ month = month - 1;
441
+ var month_start = new Date(year, month, 1);
442
+ var nxt_mon = new Date(year, month + 1, 1);
443
+ var month_end = new Date(nxt_mon - 1);
444
+
445
+ if (end < month_start || start > month_end) {
446
+ return;
447
+ }
448
+
449
+ //If event starts in previous month - how many days from start to first occurrence in current month?
450
+ // Depends on occurrence (and 'stream' for weekly events.
451
+ switch (schedule) {
452
+ case 'once':
453
+ case 'custom':
454
+ var formateddate = $.datepicker.formatDate('yy-mm-dd', start);
455
+ eo_occurrences_by_rule.push(formateddate);
456
+ return;
457
+ break;
458
+
459
+ case 'daily':
460
+ if (start < month_start) {
461
+ var count_days = Math.abs((month_start - start) / (1000 * 60 * 60 * 24)) - 1;
462
+ count_days = count_days % frequency;
463
+ } else {
464
+ var count_days = parseInt(start.getDate());
465
+ }
466
+ var skip = frequency;
467
+ var streams = new Array();
468
+ var start_stream = new Date(month_start);
469
+ start_stream.setDate(month_start.getDate() + (count_days - 1));
470
+ streams.push(start_stream);
471
+ break;
472
+
473
+ case 'weekly':
474
+ var month_start_day = month_start.getDay();
475
+ var selected = $("#dayofweekrepeat :checkbox:checked");
476
+
477
+ var ical_weekdays = new Array("SU", "MO", "TU", "WE", "TH", "FR", "SA");
478
+ streams = new Array();
479
+ selected.each(function(index) {
480
+ index = ical_weekdays.indexOf($(this).val());
481
+ start_stream = new Date(start);
482
+ start_stream.setDate(start.getDate() + (index - start.getDay() + 7) % 7);
483
+ if (start_stream < month_start) {
484
+ count_days = Math.abs((month_start - start) / (1000 * 60 * 60 * 24));
485
+ count_days = count_days - count_days % (frequency * 7);
486
+ start_stream.setDate(start_stream.getDate() + count_days);
487
+ }
488
+ streams.push(start_stream);
489
+ });
490
+ skip = 7 * frequency;
491
+ break;
492
+
493
+ //These are easy
494
+ case 'monthly':
495
+ var month_difference = (year - start.getFullYear()) * 12 + month - (start.getMonth() + 1);
496
+ if (month_difference % frequency != 0) {
497
+ return;
498
+ }
499
+ var meta = $('input[name="eo_input[schedule_meta]"]:checked').val();
500
+ if (meta == 'BYMONTHDAY=') {
501
+ var day = start.getDate();
502
+ var daysinmonth = month_end.getDate();
503
+ if (day <= daysinmonth) {
504
+ //If valid date
505
+ var pointer = new Date(year, month, day);
506
+ }
507
+
508
+ } else {
509
+ //e.g. 3rd friday of month:
510
+ var n = Math.ceil(start.getDate() / 7);
511
+ var occurrence_day = start.getDay();
512
+ if (n >= 5) {
513
+ //Last day
514
+ var month_end_day = month_end.getDay();
515
+ var occurence_date = month_end.getDate() + (occurrence_day - month_end_day - 7) % 7;
516
+ } else {
517
+ month_start_day = month_start.getDay();
518
+ var offset = (occurrence_day - month_start_day + 7) % 7;
519
+ occurence_date = offset + (n - 1) * 7 + 1;
520
+ }
521
+ var pointer = new Date(month_start);
522
+ pointer.setDate(occurence_date);
523
+ }
524
+ if (pointer <= end) {
525
+ //If before end
526
+ formateddate = $.datepicker.formatDate('yy-mm-dd', pointer);
527
+ eo_occurrences_by_rule.push(formateddate);
528
+ }
529
+ return;
530
+ break;
531
+
532
+ case 'yearly':
533
+ var year_difference = (year - start.getFullYear());
534
+ if (year_difference % frequency != 0) {
535
+ return;
536
+ }
537
+
538
+ var dateCheck = new Date(year, start.getMonth(), start.getDate());
539
+
540
+ if (month == start.getMonth() && dateCheck.getMonth() == start.getMonth()) {
541
+ pointer = new Date(start);
542
+ pointer.setYear(year);
543
+ if (pointer <= end) {
544
+ //If before end
545
+ formateddate = $.datepicker.formatDate('yy-mm-dd', pointer);
546
+ eo_occurrences_by_rule.push(formateddate);
547
+ }
548
+ }
549
+ return;
550
+ break;
551
+
552
+ default:
553
+ return;
554
+ break;
555
+
556
+ }
557
+ //End switch
558
+ //While in current month, and event has not finished - generate occurrences.
559
+ for (var x in streams) {
560
+ pointer = new Date(streams[x]);
561
+ while (pointer <= month_end && pointer <= end) {
562
+ formateddate = $.datepicker.formatDate('yy-mm-dd', pointer);
563
+ eo_occurrences_by_rule.push(formateddate);
564
+ pointer.setDate(pointer.getDate() + skip);
565
+ }
566
+ }
567
+ }
568
+
569
+ //When a date is selected, add or remove it based on current state
570
+ function eo_addOrRemoveDate(date, inst) {
571
+
572
+ var isEventful = eo_is_date_eventful(date);
573
+ if (isEventful[0]) {
574
+ //Date is eventful. Remove date
575
+ var index = isEventful[1];
576
+ if (index > -1) {
577
+ //Date was manually included
578
+ eo_removeDateFromInclude(index);
579
+ } else {
580
+ //Date was eventful by rule
581
+ eo_addDateToExclude(date);
582
+ }
583
+ } else {
584
+ //Date is not eventful. Add date
585
+ index = isEventful[1];
586
+ if (index > -1) {
587
+ //Date was manually excluded
588
+ eo_removeDateFromExclude(index);
589
+ } else {
590
+ //Date was not an event by rule
591
+ eo_addDateToInclude(date);
592
+ }
593
+ }
594
+ //Update inclusion, exclusion textareas
595
+ eo_update_inc_ex_Input();
596
+ }
597
+
598
+ // Add/remove dates from inclusion, exclusion arrays
599
+ function eo_addDateToInclude(date) {
600
+ if ($.inArray(date, eo_include_dates) < 0)
601
+ eo_include_dates.push(date);
602
+ }
603
+ function eo_removeDateFromInclude(index) {
604
+ eo_include_dates.splice(index, 1);
605
+ }
606
+ function eo_addDateToExclude(date) {
607
+ if ($.inArray(date, eo_exclude_dates) < 0)
608
+ eo_exclude_dates.push(date);
609
+ }
610
+ function eo_removeDateFromExclude(index) {
611
+ eo_exclude_dates.splice(index, 1);
612
+ }
613
+
614
+ var now = new Date();
615
+ eo_generate_dates_by_rule(now.getFullYear(), now.getMonth() + 1, {});
616
+
617
+ var dp = $('#eo_occurrence_datepicker');
618
+ if (dp.length > 0) {
619
+ dp.datepicker({
620
+ dateFormat: "yy-mm-dd",
621
+ onSelect: eo_addOrRemoveDate,
622
+ beforeShowDay: eo_pre_show_date,
623
+ onChangeMonthYear: eo_generate_dates_by_rule,
624
+ changeMonth: true,
625
+ changeYear: true,
626
+ monthNamesShort: EO_Ajax_Event.locale.monthAbbrev,
627
+ dayNamesMin: EO_Ajax_Event.locale.dayAbbrev,
628
+ firstDay: parseInt(EO_Ajax_Event.startday)
629
+ }).hide();
630
+ }
631
+ $('html').click(function() {
632
+ dp.hide();
633
+ });
634
+ dp.find('.ui-datepicker-inline').click(function(e) {
635
+ if (!e)
636
+ e = window.event;
637
+ e.cancelBubble = true;
638
+ if (e.stopPropagation)
639
+ e.stopPropagation();
640
+ });
641
+ });
642
+
643
+ /**
644
+ sprintf() for JavaScript 0.7-beta1
645
+ http://www.diveintojavascript.com/projects/javascript-sprintf
646
+
647
+ Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>
648
+ All rights reserved.
649
+
650
+ Redistribution and use in source and binary forms, with or without
651
+ modification, are permitted provided that the following conditions are met:
652
+ * Redistributions of source code must retain the above copyright
653
+ notice, this list of conditions and the following disclaimer.
654
+ * Redistributions in binary form must reproduce the above copyright
655
+ notice, this list of conditions and the following disclaimer in the
656
+ documentation and/or other materials provided with the distribution.
657
+ * Neither the name of sprintf() for JavaScript nor the
658
+ names of its contributors may be used to endorse or promote products
659
+ derived from this software without specific prior written permission.
660
+
661
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
662
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
663
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
664
+ DISCLAIMED. IN NO EVENT SHALL Alexandru Marasteanu BE LIABLE FOR ANY
665
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
666
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
667
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
668
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
669
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
670
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
671
+
672
+
673
+ Changelog:
674
+ 2010.09.06 - 0.7-beta1
675
+ - features: vsprintf, support for named placeholders
676
+ - enhancements: format cache, reduced global namespace pollution
677
+
678
+ 2010.05.22 - 0.6:
679
+ - reverted to 0.4 and fixed the bug regarding the sign of the number 0
680
+ Note:
681
+ Thanks to Raphael Pigulla <raph (at] n3rd [dot) org> (http://www.n3rd.org/)
682
+ who warned me about a bug in 0.5, I discovered that the last update was
683
+ a regress. I appologize for that.
684
+
685
+ 2010.05.09 - 0.5:
686
+ - bug fix: 0 is now preceeded with a + sign
687
+ - bug fix: the sign was not at the right position on padded results (Kamal Abdali)
688
+ - switched from GPL to BSD license
689
+
690
+ 2007.10.21 - 0.4:
691
+ - unit test and patch (David Baird)
692
+
693
+ 2007.09.17 - 0.3:
694
+ - bug fix: no longer throws exception on empty paramenters (Hans Pufal)
695
+
696
+ 2007.09.11 - 0.2:
697
+ - feature: added argument swapping
698
+
699
+ 2007.04.03 - 0.1:
700
+ - initial release
701
+ **/
702
+
703
+ var sprintf = (function() {
704
+ function get_type(variable) {
705
+ return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
706
+ }
707
+ function str_repeat(input, multiplier) {
708
+ for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
709
+ return output.join('');
710
+ }
711
+
712
+ var str_format = function() {
713
+ if (!str_format.cache.hasOwnProperty(arguments[0])) {
714
+ str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
715
+ }
716
+ return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
717
+ };
718
+
719
+ str_format.format = function(parse_tree, argv) {
720
+ var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
721
+ for (i = 0; i < tree_length; i++) {
722
+ node_type = get_type(parse_tree[i]);
723
+ if (node_type === 'string') {
724
+ output.push(parse_tree[i]);
725
+ }
726
+ else if (node_type === 'array') {
727
+ match = parse_tree[i]; // convenience purposes only
728
+ if (match[2]) { // keyword argument
729
+ arg = argv[cursor];
730
+ for (k = 0; k < match[2].length; k++) {
731
+ if (!arg.hasOwnProperty(match[2][k])) {
732
+ throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
733
+ }
734
+ arg = arg[match[2][k]];
735
+ }
736
+ }
737
+ else if (match[1]) { // positional argument (explicit)
738
+ arg = argv[match[1]];
739
+ }
740
+ else { // positional argument (implicit)
741
+ arg = argv[cursor++];
742
+ }
743
+
744
+ if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
745
+ throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
746
+ }
747
+ switch (match[8]) {
748
+ case 'b': arg = arg.toString(2); break;
749
+ case 'c': arg = String.fromCharCode(arg); break;
750
+ case 'd': arg = parseInt(arg, 10); break;
751
+ case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
752
+ case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
753
+ case 'o': arg = arg.toString(8); break;
754
+ case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
755
+ case 'u': arg = Math.abs(arg); break;
756
+ case 'x': arg = arg.toString(16); break;
757
+ case 'X': arg = arg.toString(16).toUpperCase(); break;
758
+ }
759
+ arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
760
+ pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
761
+ pad_length = match[6] - String(arg).length;
762
+ pad = match[6] ? str_repeat(pad_character, pad_length) : '';
763
+ output.push(match[5] ? arg + pad : pad + arg);
764
+ }
765
+ }
766
+ return output.join('');
767
+ };
768
+
769
+ str_format.cache = {};
770
+
771
+ str_format.parse = function(fmt) {
772
+ var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
773
+ while (_fmt) {
774
+ if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
775
+ parse_tree.push(match[0]);
776
+ }
777
+ else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
778
+ parse_tree.push('%');
779
+ }
780
+ else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
781
+ if (match[2]) {
782
+ arg_names |= 1;
783
+ var field_list = [], replacement_field = match[2], field_match = [];
784
+ if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
785
+ field_list.push(field_match[1]);
786
+ while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
787
+ if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
788
+ field_list.push(field_match[1]);
789
+ }
790
+ else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
791
+ field_list.push(field_match[1]);
792
+ }
793
+ else {
794
+ throw('[sprintf] huh?');
795
+ }
796
+ }
797
+ }
798
+ else {
799
+ throw('[sprintf] huh?');
800
+ }
801
+ match[2] = field_list;
802
+ }
803
+ else {
804
+ arg_names |= 2;
805
+ }
806
+ if (arg_names === 3) {
807
+ throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
808
+ }
809
+ parse_tree.push(match);
810
+ }
811
+ else {
812
+ throw('[sprintf] huh?');
813
+ }
814
+ _fmt = _fmt.substring(match[0].length);
815
+ }
816
+ return parse_tree;
817
+ };
818
+
819
+ return str_format;
820
+ })();
821
+
822
+ var vsprintf = function(fmt, argv) {
823
+ argv.unshift(fmt);
824
+ return sprintf.apply(null, argv);
825
+ };
826
+
827
+ /*
828
+ * jQuery UI Timepicker 0.3.1
829
+ *
830
+ * Copyright 2010-2011, Francois Gelinas
831
+ * Dual licensed under the MIT or GPL Version 2 licenses.
832
+ * http://jquery.org/license
833
+ *
834
+ * http://fgelinas.com/code/timepicker
835
+ *
836
+ * Depends:
837
+ * jquery.ui.core.js
838
+ * jquery.ui.position.js (only if position settngs are used)
839
+ *
840
+ * Change version 0.1.0 - moved the t-rex up here
841
+ *
842
+ */
843
+
844
+ (function ($) {
845
+
846
+ $.extend($.ui, { timepicker: { version: "0.3.1"} });
847
+
848
+ var PROP_NAME = 'timepicker',
849
+ tpuuid = new Date().getTime();
850
+
851
+ /**
852
+ * Time picker manager.
853
+ * Use the singleton instance of this class, $.timepicker, to interact with the time picker.
854
+ * Settings for (groups of) time pickers are maintained in an instance object,
855
+ * allowing multiple different settings on the same page.
856
+ * @constructor
857
+ */
858
+
859
+ function Timepicker() {
860
+ this._curInst = null; // The current instance in use
861
+ this._disabledInputs = []; // List of time picker inputs that have been disabled
862
+ this._timepickerShowing = false; // True if the popup picker is showing , false if not
863
+ this._inDialog = false; // True if showing within a "dialog", false if not
864
+ this._dialogClass = 'ui-timepicker-dialog'; // The name of the dialog marker class
865
+ this._mainDivId = 'ui-timepicker-div'; // The ID of the main timepicker division
866
+ this._inlineClass = 'ui-timepicker-inline'; // The name of the inline marker class
867
+ this._currentClass = 'ui-timepicker-current'; // The name of the current hour / minutes marker class
868
+ this._dayOverClass = 'ui-timepicker-days-cell-over'; // The name of the day hover marker class
869
+
870
+ this.regional = []; // Available regional settings, indexed by language code
871
+ this.regional[''] = { // Default regional settings
872
+ hourText: 'Hour', // Display text for hours section
873
+ minuteText: 'Minute', // Display text for minutes link
874
+ amPmText: ['AM', 'PM'], // Display text for AM PM
875
+ closeButtonText: 'Done', // Text for the confirmation button (ok button)
876
+ nowButtonText: 'Now', // Text for the now button
877
+ deselectButtonText: 'Deselect' // Text for the deselect button
878
+ };
879
+ this._defaults = { // Global defaults for all the time picker instances
880
+ showOn: 'focus', // 'focus' for popup on focus,
881
+ // 'button' for trigger button, or 'both' for either (not yet implemented)
882
+ button: null, // 'button' element that will trigger the timepicker
883
+ showAnim: 'fadeIn', // Name of jQuery animation for popup
884
+ showOptions: {}, // Options for enhanced animations
885
+ appendText: '', // Display text following the input box, e.g. showing the format
886
+
887
+ beforeShow: null, // Define a callback function executed before the timepicker is shown
888
+ onSelect: null, // Define a callback function when a hour / minutes is selected
889
+ onClose: null, // Define a callback function when the timepicker is closed
890
+
891
+ timeSeparator: ':', // The character to use to separate hours and minutes.
892
+ periodSeparator: ' ', // The character to use to separate the time from the time period.
893
+ showPeriod: false, // Define whether or not to show AM/PM with selected time
894
+ showPeriodLabels: true, // Show the AM/PM labels on the left of the time picker
895
+ showLeadingZero: true, // Define whether or not to show a leading zero for hours < 10. [true/false]
896
+ showMinutesLeadingZero: true, // Define whether or not to show a leading zero for minutes < 10.
897
+ altField: '', // Selector for an alternate field to store selected time into
898
+ defaultTime: 'now', // Used as default time when input field is empty or for inline timePicker
899
+ // (set to 'now' for the current time, '' for no highlighted time)
900
+ myPosition: 'left top', // Position of the dialog relative to the input.
901
+ // see the position utility for more info : http://jqueryui.com/demos/position/
902
+ atPosition: 'left bottom', // Position of the input element to match
903
+ // Note : if the position utility is not loaded, the timepicker will attach left top to left bottom
904
+ //NEW: 2011-02-03
905
+ onHourShow: null, // callback for enabling / disabling on selectable hours ex : function(hour) { return true; }
906
+ onMinuteShow: null, // callback for enabling / disabling on time selection ex : function(hour,minute) { return true; }
907
+
908
+ hours: {
909
+ starts: 0, // first displayed hour
910
+ ends: 23 // last displayed hour
911
+ },
912
+ minutes: {
913
+ starts: 0, // first displayed minute
914
+ ends: 55, // last displayed minute
915
+ interval: 5 // interval of displayed minutes
916
+ },
917
+ rows: 4, // number of rows for the input tables, minimum 2, makes more sense if you use multiple of 2
918
+ // 2011-08-05 0.2.4
919
+ showHours: true, // display the hours section of the dialog
920
+ showMinutes: true, // display the minute section of the dialog
921
+ optionalMinutes: false, // optionally parse inputs of whole hours with minutes omitted
922
+
923
+ // buttons
924
+ showCloseButton: false, // shows an OK button to confirm the edit
925
+ showNowButton: false, // Shows the 'now' button
926
+ showDeselectButton: false // Shows the deselect time button
927
+
928
+ };
929
+ $.extend(this._defaults, this.regional['']);
930
+
931
+ this.tpDiv = $('<div id="' + this._mainDivId + '" class="ui-timepicker ui-widget ui-helper-clearfix ui-corner-all " style="display: none"></div>');
932
+ }
933
+
934
+ $.extend(Timepicker.prototype, {
935
+ /* Class name added to elements to indicate already configured with a time picker. */
936
+ markerClassName: 'hasTimepicker',
937
+
938
+ /* Debug logging (if enabled). */
939
+ log: function () {
940
+ },
941
+
942
+ _widgetTimepicker: function () {
943
+ return this.tpDiv;
944
+ },
945
+
946
+ /* Override the default settings for all instances of the time picker.
947
+ @param settings object - the new settings to use as defaults (anonymous object)
948
+ @return the manager object */
949
+ setDefaults: function (settings) {
950
+ extendRemove(this._defaults, settings || {});
951
+ return this;
952
+ },
953
+
954
+ /* Attach the time picker to a jQuery selection.
955
+ @param target element - the target input field or division or span
956
+ @param settings object - the new settings to use for this time picker instance (anonymous) */
957
+ _attachTimepicker: function (target, settings) {
958
+ // check for settings on the control itself - in namespace 'time:'
959
+ var inlineSettings = null;
960
+ for (var attrName in this._defaults) {
961
+ var attrValue = target.getAttribute('time:' + attrName);
962
+ if (attrValue) {
963
+ inlineSettings = inlineSettings || {};
964
+ try {
965
+ inlineSettings[attrName] = eval(attrValue);
966
+ } catch (err) {
967
+ inlineSettings[attrName] = attrValue;
968
+ }
969
+ }
970
+ }
971
+ var nodeName = target.nodeName.toLowerCase();
972
+ var inline = (nodeName == 'div' || nodeName == 'span');
973
+
974
+ if (!target.id) {
975
+ this.uuid += 1;
976
+ target.id = 'tp' + this.uuid;
977
+ }
978
+ var inst = this._newInst($(target), inline);
979
+ inst.settings = $.extend({}, settings || {}, inlineSettings || {});
980
+ if (nodeName == 'input') {
981
+ this._connectTimepicker(target, inst);
982
+ // init inst.hours and inst.minutes from the input value
983
+ this._setTimeFromField(inst);
984
+ } else if (inline) {
985
+ this._inlineTimepicker(target, inst);
986
+ }
987
+
988
+
989
+ },
990
+
991
+ /* Create a new instance object. */
992
+ _newInst: function (target, inline) {
993
+ var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars
994
+ return {
995
+ id: id, input: target, // associated target
996
+ inline: inline, // is timepicker inline or not :
997
+ tpDiv: (!inline ? this.tpDiv : // presentation div
998
+ $('<div class="' + this._inlineClass + ' ui-timepicker ui-widget ui-helper-clearfix"></div>'))
999
+ };
1000
+ },
1001
+
1002
+ /* Attach the time picker to an input field. */
1003
+ _connectTimepicker: function (target, inst) {
1004
+ var input = $(target);
1005
+ inst.append = $([]);
1006
+ inst.trigger = $([]);
1007
+ if (input.hasClass(this.markerClassName)) { return; }
1008
+ this._attachments(input, inst);
1009
+ input.addClass(this.markerClassName).
1010
+ keydown(this._doKeyDown).
1011
+ keyup(this._doKeyUp).
1012
+ bind("setData.timepicker", function (event, key, value) {
1013
+ inst.settings[key] = value;
1014
+ }).
1015
+ bind("getData.timepicker", function (event, key) {
1016
+ return this._get(inst, key);
1017
+ });
1018
+ $.data(target, PROP_NAME, inst);
1019
+ },
1020
+
1021
+ /* Handle keystrokes. */
1022
+ _doKeyDown: function (event) {
1023
+ var inst = $.timepicker._getInst(event.target);
1024
+ var handled = true;
1025
+ inst._keyEvent = true;
1026
+ if ($.timepicker._timepickerShowing) {
1027
+ switch (event.keyCode) {
1028
+ case 9: $.timepicker._hideTimepicker();
1029
+ handled = false;
1030
+ break; // hide on tab out
1031
+ case 13:
1032
+ $.timepicker._updateSelectedValue(inst);
1033
+ $.timepicker._hideTimepicker();
1034
+
1035
+ return false; // don't submit the form
1036
+ break; // select the value on enter
1037
+ case 27: $.timepicker._hideTimepicker();
1038
+ break; // hide on escape
1039
+ default: handled = false;
1040
+ }
1041
+ }
1042
+ else if (event.keyCode == 36 && event.ctrlKey) { // display the time picker on ctrl+home
1043
+ $.timepicker._showTimepicker(this);
1044
+ }
1045
+ else {
1046
+ handled = false;
1047
+ }
1048
+ if (handled) {
1049
+ event.preventDefault();
1050
+ event.stopPropagation();
1051
+ }
1052
+ },
1053
+
1054
+ /* Update selected time on keyUp */
1055
+ /* Added verion 0.0.5 */
1056
+ _doKeyUp: function (event) {
1057
+ var inst = $.timepicker._getInst(event.target);
1058
+ $.timepicker._setTimeFromField(inst);
1059
+ $.timepicker._updateTimepicker(inst);
1060
+ },
1061
+
1062
+ /* Make attachments based on settings. */
1063
+ _attachments: function (input, inst) {
1064
+ var appendText = this._get(inst, 'appendText');
1065
+ var isRTL = this._get(inst, 'isRTL');
1066
+ if (inst.append) { inst.append.remove(); }
1067
+ if (appendText) {
1068
+ inst.append = $('<span class="' + this._appendClass + '">' + appendText + '</span>');
1069
+ input[isRTL ? 'before' : 'after'](inst.append);
1070
+ }
1071
+ input.unbind('focus.timepicker', this._showTimepicker);
1072
+ input.unbind('click.timepicker', this._adjustZIndex);
1073
+
1074
+ if (inst.trigger) { inst.trigger.remove(); }
1075
+
1076
+ var showOn = this._get(inst, 'showOn');
1077
+ if (showOn == 'focus' || showOn == 'both') { // pop-up time picker when in the marked field
1078
+ input.bind("focus.timepicker", this._showTimepicker);
1079
+ input.bind("click.timepicker", this._adjustZIndex);
1080
+ }
1081
+ if (showOn == 'button' || showOn == 'both') { // pop-up time picker when 'button' element is clicked
1082
+ var button = this._get(inst, 'button');
1083
+ $(button).bind("click.timepicker", function () {
1084
+ if ($.timepicker._timepickerShowing && $.timepicker._lastInput == input[0]) {
1085
+ $.timepicker._hideTimepicker();
1086
+ } else if (!inst.input.is(':disabled')) {
1087
+ $.timepicker._showTimepicker(input[0]);
1088
+ }
1089
+ return false;
1090
+ });
1091
+
1092
+ }
1093
+ },
1094
+
1095
+
1096
+ /* Attach an inline time picker to a div. */
1097
+ _inlineTimepicker: function(target, inst) {
1098
+ var divSpan = $(target);
1099
+ if (divSpan.hasClass(this.markerClassName))
1100
+ return;
1101
+ divSpan.addClass(this.markerClassName).append(inst.tpDiv).
1102
+ bind("setData.timepicker", function(event, key, value){
1103
+ inst.settings[key] = value;
1104
+ }).bind("getData.timepicker", function(event, key){
1105
+ return this._get(inst, key);
1106
+ });
1107
+ $.data(target, PROP_NAME, inst);
1108
+
1109
+ this._setTimeFromField(inst);
1110
+ this._updateTimepicker(inst);
1111
+ inst.tpDiv.show();
1112
+ },
1113
+
1114
+ _adjustZIndex: function(input) {
1115
+ input = input.target || input;
1116
+ var inst = $.timepicker._getInst(input);
1117
+ inst.tpDiv.css('zIndex', $.timepicker._getZIndex(input) +1);
1118
+ },
1119
+
1120
+ /* Pop-up the time picker for a given input field.
1121
+ @param input element - the input field attached to the time picker or
1122
+ event - if triggered by focus */
1123
+ _showTimepicker: function (input) {
1124
+ input = input.target || input;
1125
+ if (input.nodeName.toLowerCase() != 'input') { input = $('input', input.parentNode)[0]; } // find from button/image trigger
1126
+
1127
+ if ($.timepicker._isDisabledTimepicker(input) || $.timepicker._lastInput == input) { return; } // already here
1128
+
1129
+ // fix v 0.0.8 - close current timepicker before showing another one
1130
+ $.timepicker._hideTimepicker();
1131
+
1132
+ var inst = $.timepicker._getInst(input);
1133
+ if ($.timepicker._curInst && $.timepicker._curInst != inst) {
1134
+ $.timepicker._curInst.tpDiv.stop(true, true);
1135
+ }
1136
+ var beforeShow = $.timepicker._get(inst, 'beforeShow');
1137
+ extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {}));
1138
+ inst.lastVal = null;
1139
+ $.timepicker._lastInput = input;
1140
+
1141
+ $.timepicker._setTimeFromField(inst);
1142
+
1143
+ // calculate default position
1144
+ if ($.timepicker._inDialog) { input.value = ''; } // hide cursor
1145
+ if (!$.timepicker._pos) { // position below input
1146
+ $.timepicker._pos = $.timepicker._findPos(input);
1147
+ $.timepicker._pos[1] += input.offsetHeight; // add the height
1148
+ }
1149
+ var isFixed = false;
1150
+ $(input).parents().each(function () {
1151
+ isFixed |= $(this).css('position') == 'fixed';
1152
+ return !isFixed;
1153
+ });
1154
+ if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled
1155
+ $.timepicker._pos[0] -= document.documentElement.scrollLeft;
1156
+ $.timepicker._pos[1] -= document.documentElement.scrollTop;
1157
+ }
1158
+
1159
+ var offset = { left: $.timepicker._pos[0], top: $.timepicker._pos[1] };
1160
+
1161
+ $.timepicker._pos = null;
1162
+ // determine sizing offscreen
1163
+ inst.tpDiv.css({ position: 'absolute', display: 'block', top: '-1000px' });
1164
+ $.timepicker._updateTimepicker(inst);
1165
+
1166
+
1167
+ // position with the ui position utility, if loaded
1168
+ if ( ( ! inst.inline ) && ( typeof $.ui.position == 'object' ) ) {
1169
+ inst.tpDiv.position({
1170
+ of: inst.input,
1171
+ my: $.timepicker._get( inst, 'myPosition' ),
1172
+ at: $.timepicker._get( inst, 'atPosition' ),
1173
+ // offset: $( "#offset" ).val(),
1174
+ // using: using,
1175
+ collision: 'flip'
1176
+ });
1177
+ offset = inst.tpDiv.offset();
1178
+ $.timepicker._pos = [offset.top, offset.left];
1179
+ }
1180
+
1181
+
1182
+ // reset clicked state
1183
+ inst._hoursClicked = false;
1184
+ inst._minutesClicked = false;
1185
+
1186
+ // fix width for dynamic number of time pickers
1187
+ // and adjust position before showing
1188
+ offset = $.timepicker._checkOffset(inst, offset, isFixed);
1189
+ inst.tpDiv.css({ position: ($.timepicker._inDialog && $.blockUI ?
1190
+ 'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
1191
+ left: offset.left + 'px', top: offset.top + 'px'
1192
+ });
1193
+ if ( ! inst.inline ) {
1194
+ var showAnim = $.timepicker._get(inst, 'showAnim');
1195
+ var duration = $.timepicker._get(inst, 'duration');
1196
+
1197
+ var postProcess = function () {
1198
+ $.timepicker._timepickerShowing = true;
1199
+ var borders = $.timepicker._getBorders(inst.tpDiv);
1200
+ inst.tpDiv.find('iframe.ui-timepicker-cover'). // IE6- only
1201
+ css({ left: -borders[0], top: -borders[1],
1202
+ width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight()
1203
+ });
1204
+ };
1205
+
1206
+ // Fixed the zIndex problem for real (I hope) - FG - v 0.2.9
1207
+ $.timepicker._adjustZIndex(input);
1208
+ //inst.tpDiv.css('zIndex', $.timepicker._getZIndex(input) +1);
1209
+
1210
+ if ($.effects && $.effects[showAnim]) {
1211
+ inst.tpDiv.show(showAnim, $.timepicker._get(inst, 'showOptions'), duration, postProcess);
1212
+ }
1213
+ else {
1214
+ inst.tpDiv[showAnim || 'show']((showAnim ? duration : null), postProcess);
1215
+ }
1216
+ if (!showAnim || !duration) { postProcess(); }
1217
+ if (inst.input.is(':visible') && !inst.input.is(':disabled')) { inst.input.focus(); }
1218
+ $.timepicker._curInst = inst;
1219
+ }
1220
+ },
1221
+
1222
+ // This is a copy of the zIndex function of UI core 1.8.??
1223
+ // Copied in the timepicker to stay backward compatible.
1224
+ _getZIndex: function (target) {
1225
+ var elem = $( target ), position, value;
1226
+ while ( elem.length && elem[ 0 ] !== document ) {
1227
+ position = elem.css( "position" );
1228
+ if ( position === "absolute" || position === "relative" || position === "fixed" ) {
1229
+ value = parseInt( elem.css( "zIndex" ), 10 );
1230
+ if ( !isNaN( value ) && value !== 0 ) {
1231
+ return value;
1232
+ }
1233
+ }
1234
+ elem = elem.parent();
1235
+ }
1236
+ },
1237
+
1238
+ /* Refresh the time picker
1239
+ @param target element - The target input field or inline container element. */
1240
+ _refreshTimepicker: function(target) {
1241
+ var inst = this._getInst(target);
1242
+ if (inst) {
1243
+ this._updateTimepicker(inst);
1244
+ }
1245
+ },
1246
+
1247
+
1248
+ /* Generate the time picker content. */
1249
+ _updateTimepicker: function (inst) {
1250
+ inst.tpDiv.empty().append(this._generateHTML(inst));
1251
+ this._rebindDialogEvents(inst);
1252
+
1253
+ },
1254
+
1255
+ _rebindDialogEvents: function (inst) {
1256
+ var borders = $.timepicker._getBorders(inst.tpDiv),
1257
+ self = this;
1258
+ inst.tpDiv
1259
+ .find('iframe.ui-timepicker-cover') // IE6- only
1260
+ .css({ left: -borders[0], top: -borders[1],
1261
+ width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight()
1262
+ })
1263
+ .end()
1264
+ // after the picker html is appended bind the click & double click events (faster in IE this way
1265
+ // then letting the browser interpret the inline events)
1266
+ // the binding for the minute cells also exists in _updateMinuteDisplay
1267
+ .find('.ui-timepicker-minute-cell')
1268
+ .unbind()
1269
+ .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectMinutes, this))
1270
+ .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectMinutes, this))
1271
+ .end()
1272
+ .find('.ui-timepicker-hour-cell')
1273
+ .unbind()
1274
+ .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectHours, this))
1275
+ .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectHours, this))
1276
+ .end()
1277
+ .find('.ui-timepicker td a')
1278
+ .unbind()
1279
+ .bind('mouseout', function () {
1280
+ $(this).removeClass('ui-state-hover');
1281
+ if (this.className.indexOf('ui-timepicker-prev') != -1) $(this).removeClass('ui-timepicker-prev-hover');
1282
+ if (this.className.indexOf('ui-timepicker-next') != -1) $(this).removeClass('ui-timepicker-next-hover');
1283
+ })
1284
+ .bind('mouseover', function () {
1285
+ if ( ! self._isDisabledTimepicker(inst.inline ? inst.tpDiv.parent()[0] : inst.input[0])) {
1286
+ $(this).parents('.ui-timepicker-calendar').find('a').removeClass('ui-state-hover');
1287
+ $(this).addClass('ui-state-hover');
1288
+ if (this.className.indexOf('ui-timepicker-prev') != -1) $(this).addClass('ui-timepicker-prev-hover');
1289
+ if (this.className.indexOf('ui-timepicker-next') != -1) $(this).addClass('ui-timepicker-next-hover');
1290
+ }
1291
+ })
1292
+ .end()
1293
+ .find('.' + this._dayOverClass + ' a')
1294
+ .trigger('mouseover')
1295
+ .end()
1296
+ .find('.ui-timepicker-now').bind("click", function(e) {
1297
+ $.timepicker.selectNow(e);
1298
+ }).end()
1299
+ .find('.ui-timepicker-deselect').bind("click",function(e) {
1300
+ $.timepicker.deselectTime(e);
1301
+ }).end()
1302
+ .find('.ui-timepicker-close').bind("click",function(e) {
1303
+ $.timepicker._hideTimepicker();
1304
+ }).end();
1305
+ },
1306
+
1307
+ /* Generate the HTML for the current state of the time picker. */
1308
+ _generateHTML: function (inst) {
1309
+
1310
+ var h, m, row, col, html, hoursHtml, minutesHtml = '',
1311
+ showPeriod = (this._get(inst, 'showPeriod') == true),
1312
+ showPeriodLabels = (this._get(inst, 'showPeriodLabels') == true),
1313
+ showLeadingZero = (this._get(inst, 'showLeadingZero') == true),
1314
+ showHours = (this._get(inst, 'showHours') == true),
1315
+ showMinutes = (this._get(inst, 'showMinutes') == true),
1316
+ amPmText = this._get(inst, 'amPmText'),
1317
+ rows = this._get(inst, 'rows'),
1318
+ amRows = 0,
1319
+ pmRows = 0,
1320
+ amItems = 0,
1321
+ pmItems = 0,
1322
+ amFirstRow = 0,
1323
+ pmFirstRow = 0,
1324
+ hours = Array(),
1325
+ hours_options = this._get(inst, 'hours'),
1326
+ hoursPerRow = null,
1327
+ hourCounter = 0,
1328
+ hourLabel = this._get(inst, 'hourText'),
1329
+ showCloseButton = this._get(inst, 'showCloseButton'),
1330
+ closeButtonText = this._get(inst, 'closeButtonText'),
1331
+ showNowButton = this._get(inst, 'showNowButton'),
1332
+ nowButtonText = this._get(inst, 'nowButtonText'),
1333
+ showDeselectButton = this._get(inst, 'showDeselectButton'),
1334
+ deselectButtonText = this._get(inst, 'deselectButtonText'),
1335
+ showButtonPanel = showCloseButton || showNowButton || showDeselectButton;
1336
+
1337
+
1338
+
1339
+ // prepare all hours and minutes, makes it easier to distribute by rows
1340
+ for (h = hours_options.starts; h <= hours_options.ends; h++) {
1341
+ hours.push (h);
1342
+ }
1343
+ hoursPerRow = Math.ceil(hours.length / rows); // always round up
1344
+
1345
+ if (showPeriodLabels) {
1346
+ for (hourCounter = 0; hourCounter < hours.length; hourCounter++) {
1347
+ if (hours[hourCounter] < 12) {
1348
+ amItems++;
1349
+ }
1350
+ else {
1351
+ pmItems++;
1352
+ }
1353
+ }
1354
+ hourCounter = 0;
1355
+
1356
+ amRows = Math.floor(amItems / hours.length * rows);
1357
+ pmRows = Math.floor(pmItems / hours.length * rows);
1358
+
1359
+ // assign the extra row to the period that is more densly populated
1360
+ if (rows != amRows + pmRows) {
1361
+ // Make sure: AM Has Items and either PM Does Not, AM has no rows yet, or AM is more dense
1362
+ if (amItems && (!pmItems || !amRows || (pmRows && amItems / amRows >= pmItems / pmRows))) {
1363
+ amRows++;
1364
+ } else {
1365
+ pmRows++;
1366
+ }
1367
+ }
1368
+ amFirstRow = Math.min(amRows, 1);
1369
+ pmFirstRow = amRows + 1;
1370
+ hoursPerRow = Math.ceil(Math.max(amItems / amRows, pmItems / pmRows));
1371
+ }
1372
+
1373
+
1374
+ html = '<table class="ui-timepicker-table ui-widget-content ui-corner-all"><tr>';
1375
+
1376
+ if (showHours) {
1377
+
1378
+ html += '<td class="ui-timepicker-hours">' +
1379
+ '<div class="ui-timepicker-title ui-widget-header ui-helper-clearfix ui-corner-all">' +
1380
+ hourLabel +
1381
+ '</div>' +
1382
+ '<table class="ui-timepicker">';
1383
+
1384
+ for (row = 1; row <= rows; row++) {
1385
+ html += '<tr>';
1386
+ // AM
1387
+ if (row == amFirstRow && showPeriodLabels) {
1388
+ html += '<th rowspan="' + amRows.toString() + '" class="periods" scope="row">' + amPmText[0] + '</th>';
1389
+ }
1390
+ // PM
1391
+ if (row == pmFirstRow && showPeriodLabels) {
1392
+ html += '<th rowspan="' + pmRows.toString() + '" class="periods" scope="row">' + amPmText[1] + '</th>';
1393
+ }
1394
+ for (col = 1; col <= hoursPerRow; col++) {
1395
+ if (showPeriodLabels && row < pmFirstRow && hours[hourCounter] >= 12) {
1396
+ html += this._generateHTMLHourCell(inst, undefined, showPeriod, showLeadingZero);
1397
+ } else {
1398
+ html += this._generateHTMLHourCell(inst, hours[hourCounter], showPeriod, showLeadingZero);
1399
+ hourCounter++;
1400
+ }
1401
+ }
1402
+ html += '</tr>';
1403
+ }
1404
+ html += '</tr></table>' + // Close the hours cells table
1405
+ '</td>'; // Close the Hour td
1406
+ }
1407
+
1408
+ if (showMinutes) {
1409
+ html += '<td class="ui-timepicker-minutes">';
1410
+ html += this._generateHTMLMinutes(inst);
1411
+ html += '</td>';
1412
+ }
1413
+
1414
+ html += '</tr>';
1415
+
1416
+
1417
+ if (showButtonPanel) {
1418
+ var buttonPanel = '<tr><td colspan="3"><div class="ui-timepicker-buttonpane ui-widget-content">';
1419
+ if (showNowButton) {
1420
+ buttonPanel += '<button type="button" class="ui-timepicker-now ui-state-default ui-corner-all" '
1421
+ + ' data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" >'
1422
+ + nowButtonText + '</button>';
1423
+ }
1424
+ if (showDeselectButton) {
1425
+ buttonPanel += '<button type="button" class="ui-timepicker-deselect ui-state-default ui-corner-all" '
1426
+ + ' data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" >'
1427
+ + deselectButtonText + '</button>';
1428
+ }
1429
+ if (showCloseButton) {
1430
+ buttonPanel += '<button type="button" class="ui-timepicker-close ui-state-default ui-corner-all" '
1431
+ + ' data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" >'
1432
+ + closeButtonText + '</button>';
1433
+ }
1434
+
1435
+ html += buttonPanel + '</div></td></tr>';
1436
+ }
1437
+ html += '</table>';
1438
+
1439
+ /* IE6 IFRAME FIX (taken from datepicker 1.5.3, fixed in 0.1.2 */
1440
+ html += ($.browser.msie && parseInt($.browser.version,10) < 7 && !inst.inline ?
1441
+ '<iframe src="javascript:false;" class="ui-timepicker-cover" frameborder="0"></iframe>' : '');
1442
+
1443
+ return html;
1444
+ },
1445
+
1446
+ /* Special function that update the minutes selection in currently visible timepicker
1447
+ * called on hour selection when onMinuteShow is defined */
1448
+ _updateMinuteDisplay: function (inst) {
1449
+ var newHtml = this._generateHTMLMinutes(inst);
1450
+ inst.tpDiv.find('td.ui-timepicker-minutes').html(newHtml);
1451
+ this._rebindDialogEvents(inst);
1452
+ // after the picker html is appended bind the click & double click events (faster in IE this way
1453
+ // then letting the browser interpret the inline events)
1454
+ // yes I know, duplicate code, sorry
1455
+ /* .find('.ui-timepicker-minute-cell')
1456
+ .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectMinutes, this))
1457
+ .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectMinutes, this));
1458
+ */
1459
+
1460
+ },
1461
+
1462
+ /*
1463
+ * Generate the minutes table
1464
+ * This is separated from the _generateHTML function because is can be called separately (when hours changes)
1465
+ */
1466
+ _generateHTMLMinutes: function (inst) {
1467
+
1468
+ var m, row, html = '',
1469
+ rows = this._get(inst, 'rows'),
1470
+ minutes = Array(),
1471
+ minutes_options = this._get(inst, 'minutes'),
1472
+ minutesPerRow = null,
1473
+ minuteCounter = 0,
1474
+ showMinutesLeadingZero = (this._get(inst, 'showMinutesLeadingZero') == true),
1475
+ onMinuteShow = this._get(inst, 'onMinuteShow'),
1476
+ minuteLabel = this._get(inst, 'minuteText');
1477
+
1478
+ if ( ! minutes_options.starts) {
1479
+ minutes_options.starts = 0;
1480
+ }
1481
+ if ( ! minutes_options.ends) {
1482
+ minutes_options.ends = 59;
1483
+ }
1484
+ for (m = minutes_options.starts; m <= minutes_options.ends; m += minutes_options.interval) {
1485
+ minutes.push(m);
1486
+ }
1487
+ minutesPerRow = Math.round(minutes.length / rows + 0.49); // always round up
1488
+
1489
+ /*
1490
+ * The minutes table
1491
+ */
1492
+ // if currently selected minute is not enabled, we have a problem and need to select a new minute.
1493
+ if (onMinuteShow &&
1494
+ (onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours , inst.minutes]) == false) ) {
1495
+ // loop minutes and select first available
1496
+ for (minuteCounter = 0; minuteCounter < minutes.length; minuteCounter += 1) {
1497
+ m = minutes[minuteCounter];
1498
+ if (onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours, m])) {
1499
+ inst.minutes = m;
1500
+ break;
1501
+ }
1502
+ }
1503
+ }
1504
+
1505
+
1506
+
1507
+ html += '<div class="ui-timepicker-title ui-widget-header ui-helper-clearfix ui-corner-all">' +
1508
+ minuteLabel +
1509
+ '</div>' +
1510
+ '<table class="ui-timepicker">';
1511
+
1512
+ minuteCounter = 0;
1513
+ for (row = 1; row <= rows; row++) {
1514
+ html += '<tr>';
1515
+ while (minuteCounter < row * minutesPerRow) {
1516
+ m = minutes[minuteCounter];
1517
+ var displayText = '';
1518
+ if (m !== undefined ) {
1519
+ displayText = (m < 10) && showMinutesLeadingZero ? "0" + m.toString() : m.toString();
1520
+ }
1521
+ html += this._generateHTMLMinuteCell(inst, m, displayText);
1522
+ minuteCounter++;
1523
+ }
1524
+ html += '</tr>';
1525
+ }
1526
+
1527
+ html += '</table>';
1528
+
1529
+ return html;
1530
+ },
1531
+
1532
+ /* Generate the content of a "Hour" cell */
1533
+ _generateHTMLHourCell: function (inst, hour, showPeriod, showLeadingZero) {
1534
+
1535
+ var displayHour = hour;
1536
+ if ((hour > 12) && showPeriod) {
1537
+ displayHour = hour - 12;
1538
+ }
1539
+ if ((displayHour == 0) && showPeriod) {
1540
+ displayHour = 12;
1541
+ }
1542
+ if ((displayHour < 10) && showLeadingZero) {
1543
+ displayHour = '0' + displayHour;
1544
+ }
1545
+
1546
+ var html = "";
1547
+ var enabled = true;
1548
+ var onHourShow = this._get(inst, 'onHourShow'); //custom callback
1549
+
1550
+ if (hour == undefined) {
1551
+ html = '<td><span class="ui-state-default ui-state-disabled">&nbsp;</span></td>';
1552
+ return html;
1553
+ }
1554
+
1555
+ if (onHourShow) {
1556
+ enabled = onHourShow.apply((inst.input ? inst.input[0] : null), [hour]);
1557
+ }
1558
+
1559
+ if (enabled) {
1560
+ html = '<td class="ui-timepicker-hour-cell" data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" data-hour="' + hour.toString() + '">' +
1561
+ '<a class="ui-state-default ' +
1562
+ (hour == inst.hours ? 'ui-state-active' : '') +
1563
+ '">' +
1564
+ displayHour.toString() +
1565
+ '</a></td>';
1566
+ }
1567
+ else {
1568
+ html =
1569
+ '<td>' +
1570
+ '<span class="ui-state-default ui-state-disabled ' +
1571
+ (hour == inst.hours ? ' ui-state-active ' : ' ') +
1572
+ '">' +
1573
+ displayHour.toString() +
1574
+ '</span>' +
1575
+ '</td>';
1576
+ }
1577
+ return html;
1578
+ },
1579
+
1580
+ /* Generate the content of a "Hour" cell */
1581
+ _generateHTMLMinuteCell: function (inst, minute, displayText) {
1582
+ var html = "";
1583
+ var enabled = true;
1584
+ var onMinuteShow = this._get(inst, 'onMinuteShow'); //custom callback
1585
+ if (onMinuteShow) {
1586
+ //NEW: 2011-02-03 we should give the hour as a parameter as well!
1587
+ enabled = onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours,minute]); //trigger callback
1588
+ }
1589
+
1590
+ if (minute == undefined) {
1591
+ html = '<td><span class="ui-state-default ui-state-disabled">&nbsp;</span></td>';
1592
+ return html;
1593
+ }
1594
+
1595
+ if (enabled) {
1596
+ html = '<td class="ui-timepicker-minute-cell" data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" data-minute="' + minute.toString() + '" >' +
1597
+ '<a class="ui-state-default ' +
1598
+ (minute == inst.minutes ? 'ui-state-active' : '') +
1599
+ '" >' +
1600
+ displayText +
1601
+ '</a></td>';
1602
+ }
1603
+ else {
1604
+
1605
+ html = '<td>' +
1606
+ '<span class="ui-state-default ui-state-disabled" >' +
1607
+ displayText +
1608
+ '</span>' +
1609
+ '</td>';
1610
+ }
1611
+ return html;
1612
+ },
1613
+
1614
+
1615
+ /* Detach a timepicker from its control.
1616
+ @param target element - the target input field or division or span */
1617
+ _destroyTimepicker: function(target) {
1618
+ var $target = $(target);
1619
+ var inst = $.data(target, PROP_NAME);
1620
+ if (!$target.hasClass(this.markerClassName)) {
1621
+ return;
1622
+ }
1623
+ var nodeName = target.nodeName.toLowerCase();
1624
+ $.removeData(target, PROP_NAME);
1625
+ if (nodeName == 'input') {
1626
+ inst.append.remove();
1627
+ inst.trigger.remove();
1628
+ $target.removeClass(this.markerClassName)
1629
+ .unbind('focus.timepicker', this._showTimepicker)
1630
+ .unbind('click.timepicker', this._adjustZIndex);
1631
+ } else if (nodeName == 'div' || nodeName == 'span')
1632
+ $target.removeClass(this.markerClassName).empty();
1633
+ },
1634
+
1635
+ /**
1636
+ Enable the date picker to a jQuery selection.
1637
+ @constructor
1638
+ @param target element - the target input field or division or span */
1639
+ _enableTimepicker: function(target) {
1640
+ var $target = $(target),
1641
+ target_id = $target.attr('id'),
1642
+ inst = $.data(target, PROP_NAME);
1643
+
1644
+ if (!$target.hasClass(this.markerClassName)) {
1645
+ return;
1646
+ }
1647
+ var nodeName = target.nodeName.toLowerCase();
1648
+ if (nodeName == 'input') {
1649
+ target.disabled = false;
1650
+ var button = this._get(inst, 'button');
1651
+ $(button).removeClass('ui-state-disabled').disabled = false;
1652
+ inst.trigger.filter('button').
1653
+ each(function() { this.disabled = false; }).end();
1654
+ }
1655
+ else if (nodeName == 'div' || nodeName == 'span') {
1656
+ var inline = $target.children('.' + this._inlineClass);
1657
+ inline.children().removeClass('ui-state-disabled');
1658
+ inline.find('button').each(
1659
+ function() { this.disabled = false }
1660
+ )
1661
+ }
1662
+ this._disabledInputs = $.map(this._disabledInputs,
1663
+ function(value) { return (value == target_id ? null : value); }); // delete entry
1664
+ },
1665
+
1666
+ /**
1667
+ Disable the time picker to a jQuery selection.
1668
+ @constructor
1669
+ @param target element - the target input field or division or span */
1670
+ _disableTimepicker: function(target) {
1671
+ var $target = $(target);
1672
+ var inst = $.data(target, PROP_NAME);
1673
+ if (!$target.hasClass(this.markerClassName)) {
1674
+ return;
1675
+ }
1676
+ var nodeName = target.nodeName.toLowerCase();
1677
+ if (nodeName == 'input') {
1678
+ var button = this._get(inst, 'button');
1679
+
1680
+ $(button).addClass('ui-state-disabled').disabled = true;
1681
+ target.disabled = true;
1682
+
1683
+ inst.trigger.filter('button').
1684
+ each(function() { this.disabled = true; }).end();
1685
+
1686
+ }
1687
+ else if (nodeName == 'div' || nodeName == 'span') {
1688
+ var inline = $target.children('.' + this._inlineClass);
1689
+ inline.children().addClass('ui-state-disabled');
1690
+ inline.find('button').each(
1691
+ function() { this.disabled = true }
1692
+ )
1693
+
1694
+ }
1695
+ this._disabledInputs = $.map(this._disabledInputs,
1696
+ function(value) { return (value == target ? null : value); }); // delete entry
1697
+ this._disabledInputs[this._disabledInputs.length] = $target.attr('id');
1698
+ },
1699
+
1700
+ /**
1701
+ Is the first field in a jQuery collection disabled as a timepicker?
1702
+ @constructor
1703
+ @param target_id element - the target input field or division or span
1704
+ @return boolean - true if disabled, false if enabled
1705
+ */
1706
+ _isDisabledTimepicker: function (target_id) {
1707
+ if ( ! target_id) { return false; }
1708
+ for (var i = 0; i < this._disabledInputs.length; i++) {
1709
+ if (this._disabledInputs[i] == target_id) { return true; }
1710
+ }
1711
+ return false;
1712
+ },
1713
+
1714
+ /* Check positioning to remain on screen. */
1715
+ _checkOffset: function (inst, offset, isFixed) {
1716
+ var tpWidth = inst.tpDiv.outerWidth();
1717
+ var tpHeight = inst.tpDiv.outerHeight();
1718
+ var inputWidth = inst.input ? inst.input.outerWidth() : 0;
1719
+ var inputHeight = inst.input ? inst.input.outerHeight() : 0;
1720
+ var viewWidth = document.documentElement.clientWidth + $(document).scrollLeft();
1721
+ var viewHeight = document.documentElement.clientHeight + $(document).scrollTop();
1722
+
1723
+ offset.left -= (this._get(inst, 'isRTL') ? (tpWidth - inputWidth) : 0);
1724
+ offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0;
1725
+ offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
1726
+
1727
+ // now check if datepicker is showing outside window viewport - move to a better place if so.
1728
+ offset.left -= Math.min(offset.left, (offset.left + tpWidth > viewWidth && viewWidth > tpWidth) ?
1729
+ Math.abs(offset.left + tpWidth - viewWidth) : 0);
1730
+ offset.top -= Math.min(offset.top, (offset.top + tpHeight > viewHeight && viewHeight > tpHeight) ?
1731
+ Math.abs(tpHeight + inputHeight) : 0);
1732
+
1733
+ return offset;
1734
+ },
1735
+
1736
+ /* Find an object's position on the screen. */
1737
+ _findPos: function (obj) {
1738
+ var inst = this._getInst(obj);
1739
+ var isRTL = this._get(inst, 'isRTL');
1740
+ while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) {
1741
+ obj = obj[isRTL ? 'previousSibling' : 'nextSibling'];
1742
+ }
1743
+ var position = $(obj).offset();
1744
+ return [position.left, position.top];
1745
+ },
1746
+
1747
+ /* Retrieve the size of left and top borders for an element.
1748
+ @param elem (jQuery object) the element of interest
1749
+ @return (number[2]) the left and top borders */
1750
+ _getBorders: function (elem) {
1751
+ var convert = function (value) {
1752
+ return { thin: 1, medium: 2, thick: 3}[value] || value;
1753
+ };
1754
+ return [parseFloat(convert(elem.css('border-left-width'))),
1755
+ parseFloat(convert(elem.css('border-top-width')))];
1756
+ },
1757
+
1758
+
1759
+ /* Close time picker if clicked elsewhere. */
1760
+ _checkExternalClick: function (event) {
1761
+ if (!$.timepicker._curInst) { return; }
1762
+ var $target = $(event.target);
1763
+ if ($target[0].id != $.timepicker._mainDivId &&
1764
+ $target.parents('#' + $.timepicker._mainDivId).length == 0 &&
1765
+ !$target.hasClass($.timepicker.markerClassName) &&
1766
+ !$target.hasClass($.timepicker._triggerClass) &&
1767
+ $.timepicker._timepickerShowing && !($.timepicker._inDialog && $.blockUI))
1768
+ $.timepicker._hideTimepicker();
1769
+ },
1770
+
1771
+ /**
1772
+ Hide the time picker from view.
1773
+ @param input element - the input field attached to the time picker
1774
+ * @constructor
1775
+ */
1776
+ _hideTimepicker: function (input) {
1777
+ var inst = this._curInst;
1778
+ if (!inst || (input && inst != $.data(input, PROP_NAME))) { return; }
1779
+ if (this._timepickerShowing) {
1780
+ var showAnim = this._get(inst, 'showAnim');
1781
+ var duration = this._get(inst, 'duration');
1782
+ var postProcess = function () {
1783
+ $.timepicker._tidyDialog(inst);
1784
+ this._curInst = null;
1785
+ };
1786
+ if ($.effects && $.effects[showAnim]) {
1787
+ inst.tpDiv.hide(showAnim, $.timepicker._get(inst, 'showOptions'), duration, postProcess);
1788
+ }
1789
+ else {
1790
+ inst.tpDiv[(showAnim == 'slideDown' ? 'slideUp' :
1791
+ (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess);
1792
+ }
1793
+ if (!showAnim) { postProcess(); }
1794
+
1795
+ this._timepickerShowing = false;
1796
+
1797
+ this._lastInput = null;
1798
+ if (this._inDialog) {
1799
+ this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
1800
+ if ($.blockUI) {
1801
+ $.unblockUI();
1802
+ $('body').append(this.tpDiv);
1803
+ }
1804
+ }
1805
+ this._inDialog = false;
1806
+
1807
+ var onClose = this._get(inst, 'onClose');
1808
+ if (onClose) {
1809
+ onClose.apply(
1810
+ (inst.input ? inst.input[0] : null),
1811
+ [(inst.input ? inst.input.val() : ''), inst]); // trigger custom callback
1812
+ }
1813
+
1814
+ }
1815
+ },
1816
+
1817
+
1818
+
1819
+ /* Tidy up after a dialog display. */
1820
+ _tidyDialog: function (inst) {
1821
+ inst.tpDiv.removeClass(this._dialogClass).unbind('.ui-timepicker');
1822
+ },
1823
+
1824
+ /* Retrieve the instance data for the target control.
1825
+ @param target element - the target input field or division or span
1826
+ @return object - the associated instance data
1827
+ @throws error if a jQuery problem getting data */
1828
+ _getInst: function (target) {
1829
+ try {
1830
+ return $.data(target, PROP_NAME);
1831
+ }
1832
+ catch (err) {
1833
+ throw 'Missing instance data for this timepicker';
1834
+ }
1835
+ },
1836
+
1837
+ /* Get a setting value, defaulting if necessary. */
1838
+ _get: function (inst, name) {
1839
+ return inst.settings[name] !== undefined ?
1840
+ inst.settings[name] : this._defaults[name];
1841
+ },
1842
+
1843
+ /* Parse existing time and initialise time picker. */
1844
+ _setTimeFromField: function (inst) {
1845
+ if (inst.input.val() == inst.lastVal) { return; }
1846
+ var defaultTime = this._get(inst, 'defaultTime');
1847
+
1848
+ var timeToParse = defaultTime == 'now' ? this._getCurrentTimeRounded(inst) : defaultTime;
1849
+ if ((inst.inline == false) && (inst.input.val() != '')) { timeToParse = inst.input.val() }
1850
+
1851
+ if (timeToParse instanceof Date) {
1852
+ inst.hours = timeToParse.getHours();
1853
+ inst.minutes = timeToParse.getMinutes();
1854
+ } else {
1855
+ var timeVal = inst.lastVal = timeToParse;
1856
+ if (timeToParse == '') {
1857
+ inst.hours = -1;
1858
+ inst.minutes = -1;
1859
+ } else {
1860
+ var time = this.parseTime(inst, timeVal);
1861
+ inst.hours = time.hours;
1862
+ inst.minutes = time.minutes;
1863
+ }
1864
+ }
1865
+
1866
+
1867
+ $.timepicker._updateTimepicker(inst);
1868
+ },
1869
+
1870
+ /* Update or retrieve the settings for an existing time picker.
1871
+ @param target element - the target input field or division or span
1872
+ @param name object - the new settings to update or
1873
+ string - the name of the setting to change or retrieve,
1874
+ when retrieving also 'all' for all instance settings or
1875
+ 'defaults' for all global defaults
1876
+ @param value any - the new value for the setting
1877
+ (omit if above is an object or to retrieve a value) */
1878
+ _optionTimepicker: function(target, name, value) {
1879
+ var inst = this._getInst(target);
1880
+ if (arguments.length == 2 && typeof name == 'string') {
1881
+ return (name == 'defaults' ? $.extend({}, $.timepicker._defaults) :
1882
+ (inst ? (name == 'all' ? $.extend({}, inst.settings) :
1883
+ this._get(inst, name)) : null));
1884
+ }
1885
+ var settings = name || {};
1886
+ if (typeof name == 'string') {
1887
+ settings = {};
1888
+ settings[name] = value;
1889
+ }
1890
+ if (inst) {
1891
+ if (this._curInst == inst) {
1892
+ this._hideTimepicker();
1893
+ }
1894
+ extendRemove(inst.settings, settings);
1895
+ this._updateTimepicker(inst);
1896
+ }
1897
+ },
1898
+
1899
+
1900
+ /* Set the time for a jQuery selection.
1901
+ @param target element - the target input field or division or span
1902
+ @param time String - the new time */
1903
+ _setTimeTimepicker: function(target, time) {
1904
+ var inst = this._getInst(target);
1905
+ if (inst) {
1906
+ this._setTime(inst, time);
1907
+ this._updateTimepicker(inst);
1908
+ this._updateAlternate(inst, time);
1909
+ }
1910
+ },
1911
+
1912
+ /* Set the time directly. */
1913
+ _setTime: function(inst, time, noChange) {
1914
+ var origHours = inst.hours;
1915
+ var origMinutes = inst.minutes;
1916
+ time = this.parseTime(inst, time);
1917
+ inst.hours = time.hours;
1918
+ inst.minutes = time.minutes;
1919
+
1920
+ if ((origHours != inst.hours || origMinutes != inst.minuts) && !noChange) {
1921
+ inst.input.trigger('change');
1922
+ }
1923
+ this._updateTimepicker(inst);
1924
+ this._updateSelectedValue(inst);
1925
+ },
1926
+
1927
+ /* Return the current time, ready to be parsed, rounded to the closest 5 minute */
1928
+ _getCurrentTimeRounded: function (inst) {
1929
+ var currentTime = new Date(),
1930
+ currentMinutes = currentTime.getMinutes(),
1931
+ // round to closest 5
1932
+ adjustedMinutes = Math.round( currentMinutes / 5 ) * 5;
1933
+ currentTime.setMinutes(adjustedMinutes);
1934
+ return currentTime;
1935
+ },
1936
+
1937
+ /*
1938
+ * Parse a time string into hours and minutes
1939
+ */
1940
+ parseTime: function (inst, timeVal) {
1941
+ var retVal = new Object();
1942
+ retVal.hours = -1;
1943
+ retVal.minutes = -1;
1944
+
1945
+ var timeSeparator = this._get(inst, 'timeSeparator'),
1946
+ amPmText = this._get(inst, 'amPmText'),
1947
+ showHours = this._get(inst, 'showHours'),
1948
+ showMinutes = this._get(inst, 'showMinutes'),
1949
+ optionalMinutes = this._get(inst, 'optionalMinutes'),
1950
+ showPeriod = (this._get(inst, 'showPeriod') == true),
1951
+ p = timeVal.indexOf(timeSeparator);
1952
+
1953
+ // check if time separator found
1954
+ if (p != -1) {
1955
+ retVal.hours = parseInt(timeVal.substr(0, p), 10);
1956
+ retVal.minutes = parseInt(timeVal.substr(p + 1), 10);
1957
+ }
1958
+ // check for hours only
1959
+ else if ( (showHours) && ( !showMinutes || optionalMinutes ) ) {
1960
+ retVal.hours = parseInt(timeVal, 10);
1961
+ }
1962
+ // check for minutes only
1963
+ else if ( ( ! showHours) && (showMinutes) ) {
1964
+ retVal.minutes = parseInt(timeVal, 10);
1965
+ }
1966
+
1967
+ if (showHours) {
1968
+ var timeValUpper = timeVal.toUpperCase();
1969
+ if ((retVal.hours < 12) && (showPeriod) && (timeValUpper.indexOf(amPmText[1].toUpperCase()) != -1)) {
1970
+ retVal.hours += 12;
1971
+ }
1972
+ // fix for 12 AM
1973
+ if ((retVal.hours == 12) && (showPeriod) && (timeValUpper.indexOf(amPmText[0].toUpperCase()) != -1)) {
1974
+ retVal.hours = 0;
1975
+ }
1976
+ }
1977
+
1978
+ return retVal;
1979
+ },
1980
+
1981
+ selectNow: function(event) {
1982
+ var id = $(event.target).attr("data-timepicker-instance-id"),
1983
+ $target = $(id),
1984
+ inst = this._getInst($target[0]);
1985
+ //if (!inst || (input && inst != $.data(input, PROP_NAME))) { return; }
1986
+ var currentTime = new Date();
1987
+ inst.hours = currentTime.getHours();
1988
+ inst.minutes = currentTime.getMinutes();
1989
+ this._updateSelectedValue(inst);
1990
+ this._updateTimepicker(inst);
1991
+ this._hideTimepicker();
1992
+ },
1993
+
1994
+ deselectTime: function(event) {
1995
+ var id = $(event.target).attr("data-timepicker-instance-id"),
1996
+ $target = $(id),
1997
+ inst = this._getInst($target[0]);
1998
+ inst.hours = -1;
1999
+ inst.minutes = -1;
2000
+ this._updateSelectedValue(inst);
2001
+ this._hideTimepicker();
2002
+ },
2003
+
2004
+
2005
+ selectHours: function (event) {
2006
+ var $td = $(event.currentTarget),
2007
+ id = $td.attr("data-timepicker-instance-id"),
2008
+ newHours = parseInt($td.attr("data-hour")),
2009
+ fromDoubleClick = event.data.fromDoubleClick,
2010
+ $target = $(id),
2011
+ inst = this._getInst($target[0]),
2012
+ showMinutes = (this._get(inst, 'showMinutes') == true);
2013
+
2014
+ // don't select if disabled
2015
+ if ( $.timepicker._isDisabledTimepicker($target.attr('id')) ) { return false }
2016
+
2017
+ $td.parents('.ui-timepicker-hours:first').find('a').removeClass('ui-state-active');
2018
+ $td.children('a').addClass('ui-state-active');
2019
+ inst.hours = newHours;
2020
+
2021
+ // added for onMinuteShow callback
2022
+ var onMinuteShow = this._get(inst, 'onMinuteShow');
2023
+ if (onMinuteShow) {
2024
+ // this will trigger a callback on selected hour to make sure selected minute is allowed.
2025
+ this._updateMinuteDisplay(inst);
2026
+ }
2027
+
2028
+ this._updateSelectedValue(inst);
2029
+
2030
+ inst._hoursClicked = true;
2031
+ if ((inst._minutesClicked) || (fromDoubleClick) || (showMinutes == false)) {
2032
+ $.timepicker._hideTimepicker();
2033
+ }
2034
+ // return false because if used inline, prevent the url to change to a hashtag
2035
+ return false;
2036
+ },
2037
+
2038
+ selectMinutes: function (event) {
2039
+ var $td = $(event.currentTarget),
2040
+ id = $td.attr("data-timepicker-instance-id"),
2041
+ newMinutes = parseInt($td.attr("data-minute")),
2042
+ fromDoubleClick = event.data.fromDoubleClick,
2043
+ $target = $(id),
2044
+ inst = this._getInst($target[0]),
2045
+ showHours = (this._get(inst, 'showHours') == true);
2046
+
2047
+ // don't select if disabled
2048
+ if ( $.timepicker._isDisabledTimepicker($target.attr('id')) ) { return false }
2049
+
2050
+ $td.parents('.ui-timepicker-minutes:first').find('a').removeClass('ui-state-active');
2051
+ $td.children('a').addClass('ui-state-active');
2052
+
2053
+ inst.minutes = newMinutes;
2054
+ this._updateSelectedValue(inst);
2055
+
2056
+ inst._minutesClicked = true;
2057
+ if ((inst._hoursClicked) || (fromDoubleClick) || (showHours == false)) {
2058
+ $.timepicker._hideTimepicker();
2059
+ // return false because if used inline, prevent the url to change to a hashtag
2060
+ return false;
2061
+ }
2062
+
2063
+ // return false because if used inline, prevent the url to change to a hashtag
2064
+ return false;
2065
+ },
2066
+
2067
+ _updateSelectedValue: function (inst) {
2068
+ var newTime = this._getParsedTime(inst);
2069
+ if (inst.input) {
2070
+ inst.input.val(newTime);
2071
+ inst.input.trigger('change');
2072
+ }
2073
+ var onSelect = this._get(inst, 'onSelect');
2074
+ if (onSelect) { onSelect.apply((inst.input ? inst.input[0] : null), [newTime, inst]); } // trigger custom callback
2075
+ this._updateAlternate(inst, newTime);
2076
+ return newTime;
2077
+ },
2078
+
2079
+ /* this function process selected time and return it parsed according to instance options */
2080
+ _getParsedTime: function(inst) {
2081
+
2082
+ if (inst.hours == -1 && inst.minutes == -1) {
2083
+ return '';
2084
+ }
2085
+
2086
+ // default to 0 AM if hours is not valid
2087
+ if ((inst.hours < inst.hours.starts) || (inst.hours > inst.hours.ends )) { inst.hours = 0; }
2088
+ // default to 0 minutes if minute is not valid
2089
+ if ((inst.minutes < inst.minutes.starts) || (inst.minutes > inst.minutes.ends)) { inst.minutes = 0; }
2090
+
2091
+ var period = "",
2092
+ showPeriod = (this._get(inst, 'showPeriod') == true),
2093
+ showLeadingZero = (this._get(inst, 'showLeadingZero') == true),
2094
+ showHours = (this._get(inst, 'showHours') == true),
2095
+ showMinutes = (this._get(inst, 'showMinutes') == true),
2096
+ optionalMinutes = (this._get(inst, 'optionalMinutes') == true),
2097
+ amPmText = this._get(inst, 'amPmText'),
2098
+ selectedHours = inst.hours ? inst.hours : 0,
2099
+ selectedMinutes = inst.minutes ? inst.minutes : 0,
2100
+ displayHours = selectedHours ? selectedHours : 0,
2101
+ parsedTime = '';
2102
+
2103
+ if (showPeriod) {
2104
+ if (inst.hours == 0) {
2105
+ displayHours = 12;
2106
+ }
2107
+ if (inst.hours < 12) {
2108
+ period = amPmText[0];
2109
+ }
2110
+ else {
2111
+ period = amPmText[1];
2112
+ if (displayHours > 12) {
2113
+ displayHours -= 12;
2114
+ }
2115
+ }
2116
+ }
2117
+
2118
+ var h = displayHours.toString();
2119
+ if (showLeadingZero && (displayHours < 10)) { h = '0' + h; }
2120
+
2121
+ var m = selectedMinutes.toString();
2122
+ if (selectedMinutes < 10) { m = '0' + m; }
2123
+
2124
+ if (showHours) {
2125
+ parsedTime += h;
2126
+ }
2127
+ if (showHours && showMinutes && (!optionalMinutes || m != 0)) {
2128
+ parsedTime += this._get(inst, 'timeSeparator');
2129
+ }
2130
+ if (showMinutes && (!optionalMinutes || m != 0)) {
2131
+ parsedTime += m;
2132
+ }
2133
+ if (showHours) {
2134
+ if (period.length > 0) { parsedTime += this._get(inst, 'periodSeparator') + period; }
2135
+ }
2136
+
2137
+ return parsedTime;
2138
+ },
2139
+
2140
+ /* Update any alternate field to synchronise with the main field. */
2141
+ _updateAlternate: function(inst, newTime) {
2142
+ var altField = this._get(inst, 'altField');
2143
+ if (altField) { // update alternate field too
2144
+ $(altField).each(function(i,e) {
2145
+ $(e).val(newTime);
2146
+ });
2147
+ }
2148
+ },
2149
+
2150
+ /* This might look unused but it's called by the $.fn.timepicker function with param getTime */
2151
+ /* added v 0.2.3 - gitHub issue #5 - Thanks edanuff */
2152
+ _getTimeTimepicker : function(input) {
2153
+ var inst = this._getInst(input);
2154
+ return this._getParsedTime(inst);
2155
+ },
2156
+ _getHourTimepicker: function(input) {
2157
+ var inst = this._getInst(input);
2158
+ if ( inst == undefined) { return -1; }
2159
+ return inst.hours;
2160
+ },
2161
+ _getMinuteTimepicker: function(input) {
2162
+ var inst= this._getInst(input);
2163
+ if ( inst == undefined) { return -1; }
2164
+ return inst.minutes;
2165
+ }
2166
+
2167
+ });
2168
+
2169
+
2170
+
2171
+ /* Invoke the timepicker functionality.
2172
+ @param options string - a command, optionally followed by additional parameters or
2173
+ Object - settings for attaching new timepicker functionality
2174
+ @return jQuery object */
2175
+ $.fn.timepicker = function (options) {
2176
+
2177
+ /* Initialise the time picker. */
2178
+ if (!$.timepicker.initialized) {
2179
+ $(document).mousedown($.timepicker._checkExternalClick).
2180
+ find('body').append($.timepicker.tpDiv);
2181
+ $.timepicker.initialized = true;
2182
+ }
2183
+
2184
+
2185
+
2186
+ var otherArgs = Array.prototype.slice.call(arguments, 1);
2187
+ if (typeof options == 'string' && (options == 'getTime' || options == 'getHour' || options == 'getMinute' ))
2188
+ return $.timepicker['_' + options + 'Timepicker'].
2189
+ apply($.timepicker, [this[0]].concat(otherArgs));
2190
+ if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string')
2191
+ return $.timepicker['_' + options + 'Timepicker'].
2192
+ apply($.timepicker, [this[0]].concat(otherArgs));
2193
+ return this.each(function () {
2194
+ typeof options == 'string' ?
2195
+ $.timepicker['_' + options + 'Timepicker'].
2196
+ apply($.timepicker, [this].concat(otherArgs)) :
2197
+ $.timepicker._attachTimepicker(this, options);
2198
+ });
2199
+ };
2200
+
2201
+ /* jQuery extend now ignores nulls! */
2202
+ function extendRemove(target, props) {
2203
+ $.extend(target, props);
2204
+ for (var name in props)
2205
+ if (props[name] == null || props[name] == undefined)
2206
+ target[name] = props[name];
2207
+ return target;
2208
+ };
2209
+
2210
+ $.timepicker = new Timepicker(); // singleton instance
2211
+ $.timepicker.initialized = false;
2212
+ $.timepicker.uuid = new Date().getTime();
2213
+ $.timepicker.version = "0.3.1";
2214
+
2215
+ // Workaround for #4055
2216
+ // Add another global to avoid noConflict issues with inline event handlers
2217
+ window['TP_jQuery_' + tpuuid] = $;
2218
+
2219
+ })(jQuery);
js/event.min.js ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var eo_occurrences_by_rule=[];
2
+ jQuery(document).ready(function(b){function z(){b("#eo_occurrence_includes").val(eo_include_dates.join(",\r\n"));b("#eo_occurrence_excludes").val(eo_exclude_dates.join(",\r\n"))}function v(){var g=!b("#HWSEvent_rec").prop("checked");b(".reoccurence .event-date :input").attr("disabled",g);b(".reoccurence .event-date :input").toggleClass("ui-state-disabled",g);g=!b("#eo_allday:checkbox").attr("checked");b(".eo_time").attr("disabled",!g);b(".eo_time").toggleClass("ui-state-disabled",!g);switch(b("#HWSEventInput_Req").val()){case "once":case "custom":b("#HWSEvent_freq").val("1");
3
+ b(".reocurrence_row").hide();b("#dayofweekrepeat").show();b("#dayofmonthrepeat").show();b(".reocurrence_row :input").attr("disabled",true);break;case "weekly":b(".reocurrence_row :input").attr("disabled",false);b("#HWSEvent_freq").val()>1?b("#recpan").text(o.weeks):b("#recpan").text(o.week);b(".reocurrence_row").fadeIn(700);b("#dayofweekrepeat").fadeIn(700);b("#dayofweekrepeat :input").attr("disabled",false);b("#dayofmonthrepeat").hide();b("#dayofmonthrepeat :radio").attr("disabled",true);break;case "monthly":b(".reocurrence_row :input").attr("disabled",
4
+ false);b("#HWSEvent_freq").val()>1?b("#recpan").text(o.months):b("#recpan").text(o.month);b(".reocurrence_row").fadeIn(700);b("#dayofmonthrepeat").fadeIn(700);b("#dayofmonthrepeat :input").attr("disabled",false);b("#dayofweekrepeat").hide();b("#dayofweekrepeat :input").attr("disabled",true);break;case "daily":b(".reocurrence_row :input").attr("disabled",false);b(".reocurrence_row").fadeIn(700);b("#HWSEvent_freq").val()>1?b("#recpan").text(o.days):b("#recpan").text(o.day);b("#dayofweekrepeat").hide();
5
+ b("#dayofweekrepeat :input").attr("disabled",true);b("#dayofmonthrepeat").hide();b("#dayofmonthrepeat :radio").attr("disabled",true);break;case "yearly":b(".reocurrence_row :input").attr("disabled",false);b(".reocurrence_row").fadeIn(700);b("#HWSEvent_freq").val()>1?b("#recpan").text(o.years):b("#recpan").text(o.year);b("#dayofweekrepeat").hide();b("#dayofweekrepeat :input").attr("disabled",true);b("#dayofmonthrepeat").hide();b("#dayofmonthrepeat :radio").attr("disabled",true);break}b("#venue_select").val()===
6
+ null&&b("tr.venue_row").hide();q()}function q(){if(b("#HWSEventInput_Req").val()=="once")b("#event_summary").html("This event will be a one-time event");else{var g=b("#from_date").datepicker("getDate"),i=EO_Ajax_Event.locale.weekDay,t=new Array("SU","MO","TU","WE","TH","FR","SA"),m={monthNamesShort:EO_Ajax_Event.locale.monthAbbrev,dayNamesMin:EO_Ajax_Event.locale.dayAbbrev,monthNames:EO_Ajax_Event.locale.monthNames},h=parseInt(b("#HWSEvent_freq").val()),n=o.summary+" ";switch(b("#HWSEventInput_Req").val()){case "custom":case "daily":n+=
7
+ h>1?sprintf(o.dayPlural,h):o.daySingle;break;case "weekly":n+=h>1?sprintf(o.weekPlural,h):o.weekSingle;h=b("#dayofweekrepeat :checkbox:checked");if(h.length==0){h=g.getDay();b("#dayofweekrepeat :checkbox[value='"+t[h]+"']").attr("checked",true)}h=b("#dayofweekrepeat :checkbox:checked");h.each(function(p){if(p==0)n=n+" "+i[t.indexOf(b(this).val())];if(p>0)n=n+", "+i[t.indexOf(b(this).val())]});break;case "monthly":n+=h>1?sprintf(o.monthPlural,h):o.monthSingle;if(b("#dayofmonthrepeat :radio:checked").val()==
8
+ "BYMONTHDAY=")n=n+" "+g.getDate()+a(g);else{h=g.getDay()%7;g=parseInt(Math.floor((g.getDate()-1)/7));n=n+" "+o.occurrence[g]+" "+i[h]}break;case "yearly":n+=h>1?sprintf(o.yearPlural,h):o.yearSingle;n=n+" "+b.datepicker.formatDate("MM d",g,m)+a(g);break}g=b("#recend").datepicker("getDate");if(g!=null)n=n+" "+o.until+" "+b.datepicker.formatDate("MM d'"+a(g)+"' yy",g,m);b("#event_summary").html(n)}}function a(g){return["th","st","nd","rd"][3<g.getDate()&&g.getDate()<20?0:Math.min(g.getDate()%10,4)%4]}
9
+ function c(){eo_exclude_dates=[];eo_include_dates=[];z();f(eo_viewing_month[0],eo_viewing_month[1],{});A.datepicker("refresh")}function e(g){g=b.datepicker.formatDate("yy-mm-dd",g);if(d(g)[0])return[true,"ui-state-active",""];return[true,"ui-state-disabled",""]}function d(g){if(b.inArray(g,eo_occurrences_by_rule)>-1){g=b.inArray(g,eo_exclude_dates);return g>-1?[false,g]:[true,-1]}else{g=b.inArray(g,eo_include_dates);return g>-1?[true,g]:[false,-1]}}function f(g,i){eo_occurrences_by_rule=[];eo_viewing_month=
10
+ [g,i];var t=b("#HWSEventInput_Req").val(),m=parseInt(b("#HWSEvent_freq").val()),h=b("#from_date").datepicker("getDate"),n=b("#recend").datepicker("getDate");i-=1;var p=new Date(g,i,1),w=new Date(new Date(g,i+1,1)-1);if(!(n<p||h>w)){switch(t){case "once":case "custom":g=b.datepicker.formatDate("yy-mm-dd",h);eo_occurrences_by_rule.push(g);return;case "daily":if(h<p){var B=Math.abs((p-h)/864E5)-1;B%=m}else B=parseInt(h.getDate());i=m;var E=[],C=new Date(p);C.setDate(p.getDate()+(B-1));E.push(C);break;
11
+ case "weekly":var r=p.getDay();r=b("#dayofweekrepeat :checkbox:checked");var H=new Array("SU","MO","TU","WE","TH","FR","SA");E=[];r.each(function(G){G=H.indexOf(b(this).val());C=new Date(h);C.setDate(h.getDate()+(G-h.getDay()+7)%7);if(C<p){B=Math.abs((p-h)/864E5);B-=B%(m*7);C.setDate(C.getDate()+B)}E.push(C)});i=7*m;break;case "monthly":if(((g-h.getFullYear())*12+i-(h.getMonth()+1))%m!=0)return;if(b('input[name="eo_input[schedule_meta]"]:checked').val()=="BYMONTHDAY="){var F=h.getDate();w=w.getDate();
12
+ if(F<=w)r=new Date(g,i,F)}else{g=Math.ceil(h.getDate()/7);F=h.getDay();if(g>=5){r=w.getDay();w=w.getDate()+(F-r-7)%7}else{r=p.getDay();w=(F-r+7)%7+(g-1)*7+1}r=new Date(p);r.setDate(w)}if(r<=n){g=b.datepicker.formatDate("yy-mm-dd",r);eo_occurrences_by_rule.push(g)}return;case "yearly":if((g-h.getFullYear())%m!=0)return;w=new Date(g,h.getMonth(),h.getDate());if(i==h.getMonth()&&w.getMonth()==h.getMonth()){r=new Date(h);r.setYear(g);if(r<=n){g=b.datepicker.formatDate("yy-mm-dd",r);eo_occurrences_by_rule.push(g)}}return;
13
+ default:return}for(F in E)for(r=new Date(E[F]);r<=w&&r<=n;){g=b.datepicker.formatDate("yy-mm-dd",r);eo_occurrences_by_rule.push(g);r.setDate(r.getDate()+i)}}}function j(g){var i=d(g);if(i[0]){i=i[1];i>-1?k(i):s(g)}else{i=i[1];i>-1?x(i):l(g)}z()}function l(g){b.inArray(g,eo_include_dates)<0&&eo_include_dates.push(g)}function k(g){eo_include_dates.splice(g,1)}function s(g){b.inArray(g,eo_exclude_dates)<0&&eo_exclude_dates.push(g)}function x(g){eo_exclude_dates.splice(g,1)}if(!Array.prototype.indexOf)Array.prototype.indexOf=
14
+ function(g,i){var t=this.length;i=Number(i)||0;i=i<0?Math.ceil(i):Math.floor(i);if(i<0)i+=t;for(;i<t;i++)if(i in this&&this[i]===g)return i;return-1};var u;if(b("#eo_occurrence_includes").length>0){eo_include_dates=b("#eo_occurrence_includes").val().split(",");eo_exclude_dates=b("#eo_occurrence_excludes").val().split(",")}b(document).ready(function(){b.widget("ui.combobox",{_create:function(){var g,i=this.element.hide(),t=i.children(":selected");t=t.val()?t.text():"";i=b("<span>").addClass("ui-combobox eo-venue-input").insertAfter(i);
15
+ g=b("<input>").appendTo(i).val(t).addClass("ui-combobox-input").autocomplete({delay:0,minLength:0,source:function(m,h){b.getJSON(EO_Ajax_Event.ajaxurl+"?callback=?&action=eo-search-venue",m,function(n){n=b.map(n,function(p){p.label=p.name;return p});h(n)})},select:function(m,h){if(b("tr.venue_row").length>0){h.item.term_id==0?b("tr.venue_row").hide():b("tr.venue_row").show();b("#eventorganiser_event_detail tr.eo-add-new-venue").hide();eo_initialize_map(h.item.venue_lat,h.item.venue_lng);b("#eo_venue_Lat").val(h.item.venue_lat);
16
+ b("#eo_venue_Lng").val(h.item.venue_lng)}b("#venue_select").removeAttr("selected");b("#venue_select").val(h.item.term_id)}}).addClass("ui-widget-content ui-corner-left");g.data("autocomplete")._renderItem=function(m,h){if(h.term_id==0)return b("<li></li>").data("item.autocomplete",h).append("<a>"+h.label+"</a>").appendTo(m);var n=[h.venue_address,h.venue_city,h.venue_state,h.venue_postcode,h.venue_country];n=b.grep(n,function(p){return p});return b("<li></li>").data("item.autocomplete",h).append("<a>"+
17
+ h.label+"</br> <span style='font-size: 0.8em'><em>"+n.join(", ")+"</span></em></a>").appendTo(m)};i=b("<span>").addClass("eo-venue-combobox-buttons").appendTo(i);b("<a style='vertical-align: top;margin: 0px -1px;padding: 0px;height: 21px;'>").attr("title","Show All Items").appendTo(i).button({icons:{primary:"ui-icon-triangle-1-s"},text:false}).removeClass("ui-corner-all").addClass("ui-corner-right ui-combobox-toggle ui-combobox-button").click(function(){if(g.autocomplete("widget").is(":visible"))g.autocomplete("close");
18
+ else{b(this).blur();g.autocomplete("search","");g.focus()}});"event"==pagenow&&EO_Ajax_Event.current_user_can.manage_venues&&b("<a style='vertical-align: top;margin: 0px -1px;padding: 0px;height: 21px;'>").attr("title","Create New Venue").appendTo(i).button({icons:{primary:"ui-icon-plus"},text:false}).removeClass("ui-corner-all").addClass("ui-corner-right add-new-venue ui-combobox-button").click(function(){b("#eventorganiser_event_detail tr.eo-add-new-venue").show();b("tr.venue_row").show();u={id:b("#venue_select").val(),
19
+ label:b(".eo-venue-input input").val(),lat:b("#eo_venue_Lat").val(),lng:b("#eo_venue_Lng").val()};b("#venue_select").removeAttr("selected").val(0);b(".eo-venue-combobox-select").hide();b(".eo-venue-input input").val("");eo_initialize_map(0,0);var m=EO_Ajax_Event.location;if(m){m=m.split("/");m=m[m.length-1];eventorganiser_code_address(m)}else map.setZoom(1);b(this).blur()})}});b("#venue_select").combobox()});b(".eo-add-new-venue-cancel").click(function(g){g.preventDefault();b(".eo-venue-combobox-select").show();
20
+ b(".eo-add-new-venue input").val("");eo_initialize_map(u.lat,u.lng);b("#venue_select").val(u.id);b(".eo-venue-input input").val(u.label);b("#eventorganiser_event_detail tr.eo-add-new-venue").hide()});var o=EO_Ajax_Event.locale;if(b("#eventorganiser_detail #from_date, #eventorganiser_detail #to_date").length>0){var D=b("#eventorganiser_detail #from_date, #eventorganiser_detail #to_date").datepicker({dateFormat:EO_Ajax_Event.format,changeMonth:true,changeYear:true,monthNamesShort:EO_Ajax_Event.locale.monthAbbrev,
21
+ dayNamesMin:EO_Ajax_Event.locale.dayAbbrev,firstDay:parseInt(EO_Ajax_Event.startday),buttonImage:"images/ui-icon-calendar.png",onSelect:function(g){var i=this.id=="from_date"?"minDate":"maxDate",t=b(this).data("datepicker");g=b.datepicker.parseDate(t.settings.dateFormat||b.datepicker._defaults.dateFormat,g,t.settings);D.not(this).datepicker("option",i,g);this.id=="from_date"&&b("#recend").datepicker("option","minDate",g);c();v()}});b("#recend").datepicker({dateFormat:EO_Ajax_Event.format,monthNamesShort:EO_Ajax_Event.locale.monthAbbrev,
22
+ dayNamesMin:EO_Ajax_Event.locale.dayAbbrev,changeMonth:true,changeYear:true,firstDay:parseInt(EO_Ajax_Event.startday)});b("#HWSEvent_time, #HWSEvent_time2").timepicker({showPeriodLabels:false,hourText:EO_Ajax_Event.locale.hour,minuteText:EO_Ajax_Event.locale.minute});b("#HWSEvent_rec").click(function(){v()});b(".reoccurence .event-date :input, .onetime .event-date :input").not(".eo_time").change(function(){v();b(this).attr("id")!="eo_allday"&&c()});v();var y=!b(this).prop("checked");b(".reoccurence .event-date :input").attr("disabled",
23
+ y);b(".reoccurence .event-date :input").toggleClass("ui-state-disabled",y)}b(".eo_occurrence_toogle").click(function(g){g.preventDefault();g.stopPropagation();A.toggle();A.is(":visible")?b(".eo_occurrence_toogle").val(EO_Ajax_Event.locale.hideDates):b(".eo_occurrence_toogle").val(EO_Ajax_Event.locale.showDates)});y=new Date;f(y.getFullYear(),y.getMonth()+1,{});var A=b("#eo_occurrence_datepicker");A.length>0&&A.datepicker({dateFormat:"yy-mm-dd",onSelect:j,beforeShowDay:e,onChangeMonthYear:f,changeMonth:true,
24
+ changeYear:true,monthNamesShort:EO_Ajax_Event.locale.monthAbbrev,dayNamesMin:EO_Ajax_Event.locale.dayAbbrev,firstDay:parseInt(EO_Ajax_Event.startday)}).hide();b("html").click(function(){A.hide()});A.find(".ui-datepicker-inline").click(function(g){if(!g)g=window.event;g.cancelBubble=true;g.stopPropagation&&g.stopPropagation()})});
25
+ var sprintf=function(){function b(q){return Object.prototype.toString.call(q).slice(8,-1).toLowerCase()}function z(q,a){for(var c=[];a>0;c[--a]=q);return c.join("")}var v=function(){v.cache.hasOwnProperty(arguments[0])||(v.cache[arguments[0]]=v.parse(arguments[0]));return v.format.call(null,v.cache[arguments[0]],arguments)};v.format=function(q,a){var c=1,e=q.length,d="",f=[],j,l,k,s;for(j=0;j<e;j++){d=b(q[j]);if(d==="string")f.push(q[j]);else if(d==="array"){k=q[j];if(k[2]){d=a[c];for(l=0;l<k[2].length;l++){if(!d.hasOwnProperty(k[2][l]))throw sprintf('[sprintf] property "%s" does not exist',
26
+ k[2][l]);d=d[k[2][l]]}}else d=k[1]?a[k[1]]:a[c++];if(/[^s]/.test(k[8])&&b(d)!="number")throw sprintf("[sprintf] expecting number but found %s",b(d));switch(k[8]){case "b":d=d.toString(2);break;case "c":d=String.fromCharCode(d);break;case "d":d=parseInt(d,10);break;case "e":d=k[7]?d.toExponential(k[7]):d.toExponential();break;case "f":d=k[7]?parseFloat(d).toFixed(k[7]):parseFloat(d);break;case "o":d=d.toString(8);break;case "s":d=(d=String(d))&&k[7]?d.substring(0,k[7]):d;break;case "u":d=Math.abs(d);
27
+ break;case "x":d=d.toString(16);break;case "X":d=d.toString(16).toUpperCase();break}d=/[def]/.test(k[8])&&k[3]&&d>=0?"+"+d:d;l=k[4]?k[4]=="0"?"0":k[4].charAt(1):" ";s=k[6]-String(d).length;l=k[6]?z(l,s):"";f.push(k[5]?d+l:l+d)}}return f.join("")};v.cache={};v.parse=function(q){q=q;for(var a=[],c=[],e=0;q;){if((a=/^[^\x25]+/.exec(q))!==null)c.push(a[0]);else if((a=/^\x25{2}/.exec(q))!==null)c.push("%");else if((a=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(q))!==
28
+ null){if(a[2]){e|=1;var d=[],f=a[2],j=[];if((j=/^([a-z_][a-z_\d]*)/i.exec(f))!==null)for(d.push(j[1]);(f=f.substring(j[0].length))!=="";)if((j=/^\.([a-z_][a-z_\d]*)/i.exec(f))!==null)d.push(j[1]);else if((j=/^\[(\d+)\]/.exec(f))!==null)d.push(j[1]);else throw"[sprintf] huh?";else throw"[sprintf] huh?";a[2]=d}else e|=2;if(e===3)throw"[sprintf] mixing positional and named placeholders is not (yet) supported";c.push(a)}else throw"[sprintf] huh?";q=q.substring(a[0].length)}return c};return v}(),vsprintf=
29
+ function(b,z){z.unshift(b);return sprintf.apply(null,z)};
30
+ (function(b){function z(){this._curInst=null;this._disabledInputs=[];this._inDialog=this._timepickerShowing=false;this._dialogClass="ui-timepicker-dialog";this._mainDivId="ui-timepicker-div";this._inlineClass="ui-timepicker-inline";this._currentClass="ui-timepicker-current";this._dayOverClass="ui-timepicker-days-cell-over";this.regional=[];this.regional[""]={hourText:"Hour",minuteText:"Minute",amPmText:["AM","PM"],closeButtonText:"Done",nowButtonText:"Now",deselectButtonText:"Deselect"};this._defaults=
31
+ {showOn:"focus",button:null,showAnim:"fadeIn",showOptions:{},appendText:"",beforeShow:null,onSelect:null,onClose:null,timeSeparator:":",periodSeparator:" ",showPeriod:false,showPeriodLabels:true,showLeadingZero:true,showMinutesLeadingZero:true,altField:"",defaultTime:"now",myPosition:"left top",atPosition:"left bottom",onHourShow:null,onMinuteShow:null,hours:{starts:0,ends:23},minutes:{starts:0,ends:55,interval:5},rows:4,showHours:true,showMinutes:true,optionalMinutes:false,showCloseButton:false,
32
+ showNowButton:false,showDeselectButton:false};b.extend(this._defaults,this.regional[""]);this.tpDiv=b('<div id="'+this._mainDivId+'" class="ui-timepicker ui-widget ui-helper-clearfix ui-corner-all " style="display: none"></div>')}function v(a,c){b.extend(a,c);for(var e in c)if(c[e]==null||c[e]==undefined)a[e]=c[e];return a}b.extend(b.ui,{timepicker:{version:"0.3.1"}});var q=(new Date).getTime();b.extend(z.prototype,{markerClassName:"hasTimepicker",log:function(){},_widgetTimepicker:function(){return this.tpDiv},
33
+ setDefaults:function(a){v(this._defaults,a||{});return this},_attachTimepicker:function(a,c){var e=null;for(var d in this._defaults){var f=a.getAttribute("time:"+d);if(f){e=e||{};try{e[d]=eval(f)}catch(j){e[d]=f}}}d=a.nodeName.toLowerCase();f=d=="div"||d=="span";if(!a.id){this.uuid+=1;a.id="tp"+this.uuid}var l=this._newInst(b(a),f);l.settings=b.extend({},c||{},e||{});if(d=="input"){this._connectTimepicker(a,l);this._setTimeFromField(l)}else f&&this._inlineTimepicker(a,l)},_newInst:function(a,c){return{id:a[0].id.replace(/([^A-Za-z0-9_-])/g,
34
+ "\\\\$1"),input:a,inline:c,tpDiv:!c?this.tpDiv:b('<div class="'+this._inlineClass+' ui-timepicker ui-widget ui-helper-clearfix"></div>')}},_connectTimepicker:function(a,c){var e=b(a);c.append=b([]);c.trigger=b([]);if(!e.hasClass(this.markerClassName)){this._attachments(e,c);e.addClass(this.markerClassName).keydown(this._doKeyDown).keyup(this._doKeyUp).bind("setData.timepicker",function(d,f,j){c.settings[f]=j}).bind("getData.timepicker",function(d,f){return this._get(c,f)});b.data(a,"timepicker",
35
+ c)}},_doKeyDown:function(a){var c=b.timepicker._getInst(a.target),e=true;c._keyEvent=true;if(b.timepicker._timepickerShowing)switch(a.keyCode){case 9:b.timepicker._hideTimepicker();e=false;break;case 13:b.timepicker._updateSelectedValue(c);b.timepicker._hideTimepicker();return false;case 27:b.timepicker._hideTimepicker();break;default:e=false}else if(a.keyCode==36&&a.ctrlKey)b.timepicker._showTimepicker(this);else e=false;if(e){a.preventDefault();a.stopPropagation()}},_doKeyUp:function(a){a=b.timepicker._getInst(a.target);
36
+ b.timepicker._setTimeFromField(a);b.timepicker._updateTimepicker(a)},_attachments:function(a,c){var e=this._get(c,"appendText"),d=this._get(c,"isRTL");c.append&&c.append.remove();if(e){c.append=b('<span class="'+this._appendClass+'">'+e+"</span>");a[d?"before":"after"](c.append)}a.unbind("focus.timepicker",this._showTimepicker);a.unbind("click.timepicker",this._adjustZIndex);c.trigger&&c.trigger.remove();e=this._get(c,"showOn");if(e=="focus"||e=="both"){a.bind("focus.timepicker",this._showTimepicker);
37
+ a.bind("click.timepicker",this._adjustZIndex)}if(e=="button"||e=="both"){e=this._get(c,"button");b(e).bind("click.timepicker",function(){if(b.timepicker._timepickerShowing&&b.timepicker._lastInput==a[0])b.timepicker._hideTimepicker();else c.input.is(":disabled")||b.timepicker._showTimepicker(a[0]);return false})}},_inlineTimepicker:function(a,c){var e=b(a);if(!e.hasClass(this.markerClassName)){e.addClass(this.markerClassName).append(c.tpDiv).bind("setData.timepicker",function(d,f,j){c.settings[f]=
38
+ j}).bind("getData.timepicker",function(d,f){return this._get(c,f)});b.data(a,"timepicker",c);this._setTimeFromField(c);this._updateTimepicker(c);c.tpDiv.show()}},_adjustZIndex:function(a){a=a.target||a;b.timepicker._getInst(a).tpDiv.css("zIndex",b.timepicker._getZIndex(a)+1)},_showTimepicker:function(a){a=a.target||a;if(a.nodeName.toLowerCase()!="input")a=b("input",a.parentNode)[0];if(!(b.timepicker._isDisabledTimepicker(a)||b.timepicker._lastInput==a)){b.timepicker._hideTimepicker();var c=b.timepicker._getInst(a);
39
+ b.timepicker._curInst&&b.timepicker._curInst!=c&&b.timepicker._curInst.tpDiv.stop(true,true);var e=b.timepicker._get(c,"beforeShow");v(c.settings,e?e.apply(a,[a,c]):{});c.lastVal=null;b.timepicker._lastInput=a;b.timepicker._setTimeFromField(c);if(b.timepicker._inDialog)a.value="";if(!b.timepicker._pos){b.timepicker._pos=b.timepicker._findPos(a);b.timepicker._pos[1]+=a.offsetHeight}var d=false;b(a).parents().each(function(){d|=b(this).css("position")=="fixed";return!d});if(d&&b.browser.opera){b.timepicker._pos[0]-=
40
+ document.documentElement.scrollLeft;b.timepicker._pos[1]-=document.documentElement.scrollTop}e={left:b.timepicker._pos[0],top:b.timepicker._pos[1]};b.timepicker._pos=null;c.tpDiv.css({position:"absolute",display:"block",top:"-1000px"});b.timepicker._updateTimepicker(c);if(!c.inline&&typeof b.ui.position=="object"){c.tpDiv.position({of:c.input,my:b.timepicker._get(c,"myPosition"),at:b.timepicker._get(c,"atPosition"),collision:"flip"});e=c.tpDiv.offset();b.timepicker._pos=[e.top,e.left]}c._hoursClicked=
41
+ false;c._minutesClicked=false;e=b.timepicker._checkOffset(c,e,d);c.tpDiv.css({position:b.timepicker._inDialog&&b.blockUI?"static":d?"fixed":"absolute",display:"none",left:e.left+"px",top:e.top+"px"});if(!c.inline){e=b.timepicker._get(c,"showAnim");var f=b.timepicker._get(c,"duration"),j=function(){b.timepicker._timepickerShowing=true;var l=b.timepicker._getBorders(c.tpDiv);c.tpDiv.find("iframe.ui-timepicker-cover").css({left:-l[0],top:-l[1],width:c.tpDiv.outerWidth(),height:c.tpDiv.outerHeight()})};
42
+ b.timepicker._adjustZIndex(a);b.effects&&b.effects[e]?c.tpDiv.show(e,b.timepicker._get(c,"showOptions"),f,j):c.tpDiv[e||"show"](e?f:null,j);if(!e||!f)j();c.input.is(":visible")&&!c.input.is(":disabled")&&c.input.focus();b.timepicker._curInst=c}}},_getZIndex:function(a){a=b(a);for(var c;a.length&&a[0]!==document;){c=a.css("position");if(c==="absolute"||c==="relative"||c==="fixed"){c=parseInt(a.css("zIndex"),10);if(!isNaN(c)&&c!==0)return c}a=a.parent()}},_refreshTimepicker:function(a){(a=this._getInst(a))&&
43
+ this._updateTimepicker(a)},_updateTimepicker:function(a){a.tpDiv.empty().append(this._generateHTML(a));this._rebindDialogEvents(a)},_rebindDialogEvents:function(a){var c=b.timepicker._getBorders(a.tpDiv),e=this;a.tpDiv.find("iframe.ui-timepicker-cover").css({left:-c[0],top:-c[1],width:a.tpDiv.outerWidth(),height:a.tpDiv.outerHeight()}).end().find(".ui-timepicker-minute-cell").unbind().bind("click",{fromDoubleClick:false},b.proxy(b.timepicker.selectMinutes,this)).bind("dblclick",{fromDoubleClick:true},
44
+ b.proxy(b.timepicker.selectMinutes,this)).end().find(".ui-timepicker-hour-cell").unbind().bind("click",{fromDoubleClick:false},b.proxy(b.timepicker.selectHours,this)).bind("dblclick",{fromDoubleClick:true},b.proxy(b.timepicker.selectHours,this)).end().find(".ui-timepicker td a").unbind().bind("mouseout",function(){b(this).removeClass("ui-state-hover");this.className.indexOf("ui-timepicker-prev")!=-1&&b(this).removeClass("ui-timepicker-prev-hover");this.className.indexOf("ui-timepicker-next")!=-1&&
45
+ b(this).removeClass("ui-timepicker-next-hover")}).bind("mouseover",function(){if(!e._isDisabledTimepicker(a.inline?a.tpDiv.parent()[0]:a.input[0])){b(this).parents(".ui-timepicker-calendar").find("a").removeClass("ui-state-hover");b(this).addClass("ui-state-hover");this.className.indexOf("ui-timepicker-prev")!=-1&&b(this).addClass("ui-timepicker-prev-hover");this.className.indexOf("ui-timepicker-next")!=-1&&b(this).addClass("ui-timepicker-next-hover")}}).end().find("."+this._dayOverClass+" a").trigger("mouseover").end().find(".ui-timepicker-now").bind("click",
46
+ function(d){b.timepicker.selectNow(d)}).end().find(".ui-timepicker-deselect").bind("click",function(d){b.timepicker.deselectTime(d)}).end().find(".ui-timepicker-close").bind("click",function(){b.timepicker._hideTimepicker()}).end()},_generateHTML:function(a){var c,e,d,f,j=this._get(a,"showPeriod")==true,l=this._get(a,"showPeriodLabels")==true,k=this._get(a,"showLeadingZero")==true;e=this._get(a,"showHours")==true;var s=this._get(a,"showMinutes")==true,x=this._get(a,"amPmText"),u=this._get(a,"rows"),
47
+ o=0,D=0,y=f=0,A=0,g=0,i=Array(),t=this._get(a,"hours");c=null;var m=0;d=this._get(a,"hourText");var h=this._get(a,"showCloseButton"),n=this._get(a,"closeButtonText"),p=this._get(a,"showNowButton"),w=this._get(a,"nowButtonText"),B=this._get(a,"showDeselectButton"),E=this._get(a,"deselectButtonText"),C=h||p||B;for(c=t.starts;c<=t.ends;c++)i.push(c);c=Math.ceil(i.length/u);if(l){for(m=0;m<i.length;m++)if(i[m]<12)f++;else y++;m=0;o=Math.floor(f/i.length*u);D=Math.floor(y/i.length*u);if(u!=o+D)if(f&&(!y||
48
+ !o||D&&f/o>=y/D))o++;else D++;A=Math.min(o,1);g=o+1;c=Math.ceil(Math.max(f/o,y/D))}f='<table class="ui-timepicker-table ui-widget-content ui-corner-all"><tr>';if(e){f+='<td class="ui-timepicker-hours"><div class="ui-timepicker-title ui-widget-header ui-helper-clearfix ui-corner-all">'+d+'</div><table class="ui-timepicker">';for(e=1;e<=u;e++){f+="<tr>";if(e==A&&l)f+='<th rowspan="'+o.toString()+'" class="periods" scope="row">'+x[0]+"</th>";if(e==g&&l)f+='<th rowspan="'+D.toString()+'" class="periods" scope="row">'+
49
+ x[1]+"</th>";for(d=1;d<=c;d++)if(l&&e<g&&i[m]>=12)f+=this._generateHTMLHourCell(a,undefined,j,k);else{f+=this._generateHTMLHourCell(a,i[m],j,k);m++}f+="</tr>"}f+="</tr></table></td>"}if(s){f+='<td class="ui-timepicker-minutes">';f+=this._generateHTMLMinutes(a);f+="</td>"}f+="</tr>";if(C){j='<tr><td colspan="3"><div class="ui-timepicker-buttonpane ui-widget-content">';if(p)j+='<button type="button" class="ui-timepicker-now ui-state-default ui-corner-all" data-timepicker-instance-id="#'+a.id.replace(/\\\\/g,
50
+ "\\")+'" >'+w+"</button>";if(B)j+='<button type="button" class="ui-timepicker-deselect ui-state-default ui-corner-all" data-timepicker-instance-id="#'+a.id.replace(/\\\\/g,"\\")+'" >'+E+"</button>";if(h)j+='<button type="button" class="ui-timepicker-close ui-state-default ui-corner-all" data-timepicker-instance-id="#'+a.id.replace(/\\\\/g,"\\")+'" >'+n+"</button>";f+=j+"</div></td></tr>"}f+="</table>";f+=b.browser.msie&&parseInt(b.browser.version,10)<7&&!a.inline?'<iframe src="javascript:false;" class="ui-timepicker-cover" frameborder="0"></iframe>':
51
+ "";return f},_updateMinuteDisplay:function(a){var c=this._generateHTMLMinutes(a);a.tpDiv.find("td.ui-timepicker-minutes").html(c);this._rebindDialogEvents(a)},_generateHTMLMinutes:function(a){var c,e,d="",f=this._get(a,"rows"),j=Array();e=this._get(a,"minutes");var l=null,k=0,s=this._get(a,"showMinutesLeadingZero")==true,x=this._get(a,"onMinuteShow"),u=this._get(a,"minuteText");if(!e.starts)e.starts=0;if(!e.ends)e.ends=59;for(c=e.starts;c<=e.ends;c+=e.interval)j.push(c);l=Math.round(j.length/f+0.49);
52
+ if(x&&x.apply(a.input?a.input[0]:null,[a.hours,a.minutes])==false)for(k=0;k<j.length;k+=1){c=j[k];if(x.apply(a.input?a.input[0]:null,[a.hours,c])){a.minutes=c;break}}d+='<div class="ui-timepicker-title ui-widget-header ui-helper-clearfix ui-corner-all">'+u+'</div><table class="ui-timepicker">';k=0;for(e=1;e<=f;e++){for(d+="<tr>";k<e*l;){c=j[k];x="";if(c!==undefined)x=c<10&&s?"0"+c.toString():c.toString();d+=this._generateHTMLMinuteCell(a,c,x);k++}d+="</tr>"}d+="</table>";return d},_generateHTMLHourCell:function(a,
53
+ c,e,d){var f=c;if(c>12&&e)f=c-12;if(f==0&&e)f=12;if(f<10&&d)f="0"+f;e="";e=true;d=this._get(a,"onHourShow");if(c==undefined)return e='<td><span class="ui-state-default ui-state-disabled">&nbsp;</span></td>';if(d)e=d.apply(a.input?a.input[0]:null,[c]);return e=e?'<td class="ui-timepicker-hour-cell" data-timepicker-instance-id="#'+a.id.replace(/\\\\/g,"\\")+'" data-hour="'+c.toString()+'"><a class="ui-state-default '+(c==a.hours?"ui-state-active":"")+'">'+f.toString()+"</a></td>":'<td><span class="ui-state-default ui-state-disabled '+
54
+ (c==a.hours?" ui-state-active ":" ")+'">'+f.toString()+"</span></td>"},_generateHTMLMinuteCell:function(a,c,e){var d="";d=true;var f=this._get(a,"onMinuteShow");if(f)d=f.apply(a.input?a.input[0]:null,[a.hours,c]);if(c==undefined)return d='<td><span class="ui-state-default ui-state-disabled">&nbsp;</span></td>';return d=d?'<td class="ui-timepicker-minute-cell" data-timepicker-instance-id="#'+a.id.replace(/\\\\/g,"\\")+'" data-minute="'+c.toString()+'" ><a class="ui-state-default '+(c==a.minutes?"ui-state-active":
55
+ "")+'" >'+e+"</a></td>":'<td><span class="ui-state-default ui-state-disabled" >'+e+"</span></td>"},_destroyTimepicker:function(a){var c=b(a),e=b.data(a,"timepicker");if(c.hasClass(this.markerClassName)){var d=a.nodeName.toLowerCase();b.removeData(a,"timepicker");if(d=="input"){e.append.remove();e.trigger.remove();c.removeClass(this.markerClassName).unbind("focus.timepicker",this._showTimepicker).unbind("click.timepicker",this._adjustZIndex)}else if(d=="div"||d=="span")c.removeClass(this.markerClassName).empty()}},
56
+ _enableTimepicker:function(a){var c=b(a),e=c.attr("id"),d=b.data(a,"timepicker");if(c.hasClass(this.markerClassName)){var f=a.nodeName.toLowerCase();if(f=="input"){a.disabled=false;a=this._get(d,"button");b(a).removeClass("ui-state-disabled").disabled=false;d.trigger.filter("button").each(function(){this.disabled=false}).end()}else if(f=="div"||f=="span"){d=c.children("."+this._inlineClass);d.children().removeClass("ui-state-disabled");d.find("button").each(function(){this.disabled=false})}this._disabledInputs=
57
+ b.map(this._disabledInputs,function(j){return j==e?null:j})}},_disableTimepicker:function(a){var c=b(a),e=b.data(a,"timepicker");if(c.hasClass(this.markerClassName)){var d=a.nodeName.toLowerCase();if(d=="input"){d=this._get(e,"button");b(d).addClass("ui-state-disabled").disabled=true;a.disabled=true;e.trigger.filter("button").each(function(){this.disabled=true}).end()}else if(d=="div"||d=="span"){e=c.children("."+this._inlineClass);e.children().addClass("ui-state-disabled");e.find("button").each(function(){this.disabled=
58
+ true})}this._disabledInputs=b.map(this._disabledInputs,function(f){return f==a?null:f});this._disabledInputs[this._disabledInputs.length]=c.attr("id")}},_isDisabledTimepicker:function(a){if(!a)return false;for(var c=0;c<this._disabledInputs.length;c++)if(this._disabledInputs[c]==a)return true;return false},_checkOffset:function(a,c,e){var d=a.tpDiv.outerWidth(),f=a.tpDiv.outerHeight(),j=a.input?a.input.outerWidth():0,l=a.input?a.input.outerHeight():0,k=document.documentElement.clientWidth+b(document).scrollLeft(),
59
+ s=document.documentElement.clientHeight+b(document).scrollTop();c.left-=this._get(a,"isRTL")?d-j:0;c.left-=e&&c.left==a.input.offset().left?b(document).scrollLeft():0;c.top-=e&&c.top==a.input.offset().top+l?b(document).scrollTop():0;c.left-=Math.min(c.left,c.left+d>k&&k>d?Math.abs(c.left+d-k):0);c.top-=Math.min(c.top,c.top+f>s&&s>f?Math.abs(f+l):0);return c},_findPos:function(a){for(var c=this._get(this._getInst(a),"isRTL");a&&(a.type=="hidden"||a.nodeType!=1);)a=a[c?"previousSibling":"nextSibling"];
60
+ a=b(a).offset();return[a.left,a.top]},_getBorders:function(a){var c=function(e){return{thin:1,medium:2,thick:3}[e]||e};return[parseFloat(c(a.css("border-left-width"))),parseFloat(c(a.css("border-top-width")))]},_checkExternalClick:function(a){if(b.timepicker._curInst){a=b(a.target);a[0].id!=b.timepicker._mainDivId&&a.parents("#"+b.timepicker._mainDivId).length==0&&!a.hasClass(b.timepicker.markerClassName)&&!a.hasClass(b.timepicker._triggerClass)&&b.timepicker._timepickerShowing&&!(b.timepicker._inDialog&&
61
+ b.blockUI)&&b.timepicker._hideTimepicker()}},_hideTimepicker:function(a){var c=this._curInst;if(!(!c||a&&c!=b.data(a,"timepicker")))if(this._timepickerShowing){a=this._get(c,"showAnim");var e=this._get(c,"duration"),d=function(){b.timepicker._tidyDialog(c);this._curInst=null};b.effects&&b.effects[a]?c.tpDiv.hide(a,b.timepicker._get(c,"showOptions"),e,d):c.tpDiv[a=="slideDown"?"slideUp":a=="fadeIn"?"fadeOut":"hide"](a?e:null,d);a||d();this._timepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",
62
+ left:"0",top:"-100px"});if(b.blockUI){b.unblockUI();b("body").append(this.tpDiv)}}this._inDialog=false;if(a=this._get(c,"onClose"))a.apply(c.input?c.input[0]:null,[c.input?c.input.val():"",c])}},_tidyDialog:function(a){a.tpDiv.removeClass(this._dialogClass).unbind(".ui-timepicker")},_getInst:function(a){try{return b.data(a,"timepicker")}catch(c){throw"Missing instance data for this timepicker";}},_get:function(a,c){return a.settings[c]!==undefined?a.settings[c]:this._defaults[c]},_setTimeFromField:function(a){if(a.input.val()!=
63
+ a.lastVal){var c=this._get(a,"defaultTime");c=c=="now"?this._getCurrentTimeRounded(a):c;if(a.inline==false&&a.input.val()!="")c=a.input.val();if(c instanceof Date){a.hours=c.getHours();a.minutes=c.getMinutes()}else{var e=a.lastVal=c;if(c==""){a.hours=-1;a.minutes=-1}else{c=this.parseTime(a,e);a.hours=c.hours;a.minutes=c.minutes}}b.timepicker._updateTimepicker(a)}},_optionTimepicker:function(a,c,e){var d=this._getInst(a);if(arguments.length==2&&typeof c=="string")return c=="defaults"?b.extend({},b.timepicker._defaults):
64
+ d?c=="all"?b.extend({},d.settings):this._get(d,c):null;var f=c||{};if(typeof c=="string"){f={};f[c]=e}if(d){this._curInst==d&&this._hideTimepicker();v(d.settings,f);this._updateTimepicker(d)}},_setTimeTimepicker:function(a,c){if(a=this._getInst(a)){this._setTime(a,c);this._updateTimepicker(a);this._updateAlternate(a,c)}},_setTime:function(a,c,e){var d=a.hours,f=a.minutes;c=this.parseTime(a,c);a.hours=c.hours;a.minutes=c.minutes;if((d!=a.hours||f!=a.minuts)&&!e)a.input.trigger("change");this._updateTimepicker(a);
65
+ this._updateSelectedValue(a)},_getCurrentTimeRounded:function(){var a=new Date,c=a.getMinutes();c=Math.round(c/5)*5;a.setMinutes(c);return a},parseTime:function(a,c){var e={};e.hours=-1;e.minutes=-1;var d=this._get(a,"timeSeparator"),f=this._get(a,"amPmText"),j=this._get(a,"showHours"),l=this._get(a,"showMinutes"),k=this._get(a,"optionalMinutes");a=this._get(a,"showPeriod")==true;d=c.indexOf(d);if(d!=-1){e.hours=parseInt(c.substr(0,d),10);e.minutes=parseInt(c.substr(d+1),10)}else if(j&&(!l||k))e.hours=
66
+ parseInt(c,10);else if(!j&&l)e.minutes=parseInt(c,10);if(j){c=c.toUpperCase();if(e.hours<12&&a&&c.indexOf(f[1].toUpperCase())!=-1)e.hours+=12;if(e.hours==12&&a&&c.indexOf(f[0].toUpperCase())!=-1)e.hours=0}return e},selectNow:function(a){a=b(a.target).attr("data-timepicker-instance-id");a=this._getInst(b(a)[0]);var c=new Date;a.hours=c.getHours();a.minutes=c.getMinutes();this._updateSelectedValue(a);this._updateTimepicker(a);this._hideTimepicker()},deselectTime:function(a){a=b(a.target).attr("data-timepicker-instance-id");
67
+ a=this._getInst(b(a)[0]);a.hours=-1;a.minutes=-1;this._updateSelectedValue(a);this._hideTimepicker()},selectHours:function(a){var c=b(a.currentTarget),e=c.attr("data-timepicker-instance-id"),d=parseInt(c.attr("data-hour"));a=a.data.fromDoubleClick;e=b(e);var f=this._getInst(e[0]),j=this._get(f,"showMinutes")==true;if(b.timepicker._isDisabledTimepicker(e.attr("id")))return false;c.parents(".ui-timepicker-hours:first").find("a").removeClass("ui-state-active");c.children("a").addClass("ui-state-active");
68
+ f.hours=d;this._get(f,"onMinuteShow")&&this._updateMinuteDisplay(f);this._updateSelectedValue(f);f._hoursClicked=true;if(f._minutesClicked||a||j==false)b.timepicker._hideTimepicker();return false},selectMinutes:function(a){var c=b(a.currentTarget),e=c.attr("data-timepicker-instance-id"),d=parseInt(c.attr("data-minute"));a=a.data.fromDoubleClick;e=b(e);var f=this._getInst(e[0]),j=this._get(f,"showHours")==true;if(b.timepicker._isDisabledTimepicker(e.attr("id")))return false;c.parents(".ui-timepicker-minutes:first").find("a").removeClass("ui-state-active");
69
+ c.children("a").addClass("ui-state-active");f.minutes=d;this._updateSelectedValue(f);f._minutesClicked=true;if(f._hoursClicked||a||j==false){b.timepicker._hideTimepicker();return false}return false},_updateSelectedValue:function(a){var c=this._getParsedTime(a);if(a.input){a.input.val(c);a.input.trigger("change")}var e=this._get(a,"onSelect");if(e)e.apply(a.input?a.input[0]:null,[c,a]);this._updateAlternate(a,c);return c},_getParsedTime:function(a){if(a.hours==-1&&a.minutes==-1)return"";if(a.hours<
70
+ a.hours.starts||a.hours>a.hours.ends)a.hours=0;if(a.minutes<a.minutes.starts||a.minutes>a.minutes.ends)a.minutes=0;var c="",e=this._get(a,"showPeriod")==true,d=this._get(a,"showLeadingZero")==true,f=this._get(a,"showHours")==true,j=this._get(a,"showMinutes")==true,l=this._get(a,"optionalMinutes")==true,k=this._get(a,"amPmText"),s=a.hours?a.hours:0,x=a.minutes?a.minutes:0,u=s?s:0;s="";if(e){if(a.hours==0)u=12;if(a.hours<12)c=k[0];else{c=k[1];if(u>12)u-=12}}e=u.toString();if(d&&u<10)e="0"+e;d=x.toString();
71
+ if(x<10)d="0"+d;if(f)s+=e;if(f&&j&&(!l||d!=0))s+=this._get(a,"timeSeparator");if(j&&(!l||d!=0))s+=d;if(f)if(c.length>0)s+=this._get(a,"periodSeparator")+c;return s},_updateAlternate:function(a,c){(a=this._get(a,"altField"))&&b(a).each(function(e,d){b(d).val(c)})},_getTimeTimepicker:function(a){return this._getParsedTime(this._getInst(a))},_getHourTimepicker:function(a){a=this._getInst(a);if(a==undefined)return-1;return a.hours},_getMinuteTimepicker:function(a){a=this._getInst(a);if(a==undefined)return-1;
72
+ return a.minutes}});b.fn.timepicker=function(a){if(!b.timepicker.initialized){b(document).mousedown(b.timepicker._checkExternalClick).find("body").append(b.timepicker.tpDiv);b.timepicker.initialized=true}var c=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="getTime"||a=="getHour"||a=="getMinute"))return b.timepicker["_"+a+"Timepicker"].apply(b.timepicker,[this[0]].concat(c));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return b.timepicker["_"+a+"Timepicker"].apply(b.timepicker,
73
+ [this[0]].concat(c));return this.each(function(){typeof a=="string"?b.timepicker["_"+a+"Timepicker"].apply(b.timepicker,[this].concat(c)):b.timepicker._attachTimepicker(this,a)})};b.timepicker=new z;b.timepicker.initialized=false;b.timepicker.uuid=(new Date).getTime();b.timepicker.version="0.3.1";window["TP_jQuery_"+q]=b})(jQuery);
js/eventorganiser-pointer.js ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready( function($) {
2
+ eventorganiser_open_pointer(0);
3
+ function eventorganiser_open_pointer(i){
4
+ pointer = eventorganiserPointer.pointers[i];
5
+ options = jQuery.extend( pointer.options, {
6
+ close: function() {
7
+ jQuery.post( ajaxurl, {
8
+ pointer: pointer.pointer_id,
9
+ action: 'dismiss-wp-pointer'
10
+ });
11
+ },
12
+ });
13
+
14
+ jQuery(pointer.target).pointer( options ).pointer('open');
15
+ }
16
+ });
js/frontend.js ADDED
@@ -0,0 +1,467 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function ($) {
2
+ jQuery(document).ready(function () {
3
+
4
+ function eventorganiser_cat_dropdown(options){
5
+
6
+ var terms = options.categories;
7
+
8
+ var html="<select class='eo-cal-filter' id='eo-event-cat'>";
9
+ html+="<option value=''>"+options.buttonText.cat+"</option>";
10
+ for (var i=0; i<terms.length; i++){
11
+ html+= "<option class='cat-colour-"+terms[i].colour+" cat' value='"+terms[i].slug+"'>"+terms[i].name+"</option>";
12
+ }
13
+ html+="</select>";
14
+
15
+ var element = $("<span class='fc-header-dropdown filter-category'></span>");
16
+ element.append(html);
17
+ return element;
18
+ }
19
+
20
+ function eventorganiser_mini_calendar(){
21
+ var element = $("<span class='fc-header-goto'><input type='hidden' class='eo-mini-calendar'/></span>");
22
+ return element;
23
+ }
24
+
25
+ function eventorganiser_venue_dropdown(options){
26
+
27
+ var venues = options.venues;
28
+
29
+ var html="<select class='eo-cal-filter' id='eo-event-venue'>";
30
+ html+="<option value=''>"+options.buttonText.venue+"</option>";
31
+
32
+ for (var i=0; i<venues.length; i++){
33
+ html+= "<option value='"+venues[i].term_id+"'>"+venues[i].name+"</option>";
34
+ }
35
+ html+="</select>";
36
+ var element = $("<span class='fc-header-dropdown filter-venue'></span>");
37
+ element.append(html);
38
+ return element;
39
+ }
40
+
41
+
42
+ if( $('#eo-upcoming-dates').length>0 && $('#eo-upcoming-dates').find('li:gt(4)').length > 0 ){
43
+ var eobloc = 5;
44
+ var locale = { more : EOAjaxFront.locale.ShowMore, less : EOAjaxFront.locale.ShowLess};
45
+ $('#eo-upcoming-dates').find('li:gt('+(eobloc-1)+')').hide().end().after(
46
+ $('<a href="#" id="eo-upcoming-dates-less">'+locale.less+'</a> <span id="eo-upcoming-dates-pipe">|</span> <a href="#" id="eo-upcoming-dates-more">'+locale.more+'</a>')
47
+ );
48
+ $('#eo-upcoming-dates-pipe').hide();
49
+ $('#eo-upcoming-dates-less').hide().click(function(e){
50
+ e.preventDefault();
51
+ var index = Math.floor( ($('#eo-upcoming-dates li:visible').length -1) / eobloc)*eobloc -1;
52
+ $('#eo-upcoming-dates li:gt('+index+')').hide();
53
+ $('#eo-upcoming-dates-more,#eo-upcoming-dates-pipe').show();
54
+ if( $('#eo-upcoming-dates li:visible').length <= eobloc ){
55
+ $('#eo-upcoming-dates-less,#eo-upcoming-dates-pipe').hide();
56
+ }
57
+ });
58
+ $('#eo-upcoming-dates-more').click(function(e){
59
+ e.preventDefault();
60
+ $('#eo-upcoming-dates-less,#eo-upcoming-dates-pipe, #eo-upcoming-dates li:hidden:lt('+eobloc+')').show();
61
+ var offset = $('#eo-upcoming-dates-pipe').offset();
62
+ $('html, body').animate({
63
+ scrollTop: Math.max( offset.top + 40 - $(window).height(),$(window).scrollTop())
64
+ });
65
+ if( $('#eo-upcoming-dates li:hidden').length == 0 ){
66
+ $('#eo-upcoming-dates-more,#eo-upcoming-dates-pipe').hide();
67
+ }
68
+ });
69
+ }
70
+
71
+ if ($(".eo-fullcalendar").length > 0) {
72
+ var calendars = EOAjax.calendars;
73
+ var loadingTimeOut;
74
+ for (var i = 0; i < calendars.length; i++) {
75
+ var calendar = "#eo_fullcalendar_" + (i + 1);
76
+
77
+ if (typeof calendars[i].category === "undefined") {
78
+ calendars[i].category ='';
79
+ }
80
+ if (typeof calendars[i].venue === "undefined") {
81
+ calendars[i].venue ='';
82
+ }
83
+ $(calendar).fullCalendar({
84
+ id: calendar,
85
+ year: calendars[i].year ? calendars[i].year : undefined,
86
+ month: calendars[i].month ? calendars[i].month : undefined,
87
+ date: calendars[i].date ? calendars[i].date : undefined,
88
+ category: calendars[i].event_category,
89
+ venue: calendars[i].event_venue,
90
+ customButtons:{
91
+ category: eventorganiser_cat_dropdown,
92
+ venue: eventorganiser_venue_dropdown,
93
+ 'goto': eventorganiser_mini_calendar
94
+ },
95
+ theme: true,
96
+ categories: EOAjax.fullcal.categories,
97
+ venues: EOAjax.fullcal.venues,
98
+ timeFormatphp: calendars[i].timeformatphp,
99
+ timeFormat: calendars[i].timeformat,
100
+ editable: false,
101
+ tooltip: calendars[i].tooltip,
102
+ firstDay: parseInt(EOAjax.fullcal.firstDay),
103
+ weekends: calendars[i].weekends,
104
+ allDaySlot: calendars[i].alldayslot,
105
+ allDayText: calendars[i].alldaytext,
106
+ axisFormat: calendars[i].axisformat,
107
+ minTime: calendars[i].mintime,
108
+ maxTime:calendars[i].maxtime,
109
+ columnFormat: {
110
+ month: calendars[i].columnformatmonth,
111
+ week: calendars[i].columnformatweek,
112
+ day: calendars[i].columnformatday
113
+ },
114
+ titleFormat: {
115
+ month: calendars[i].titleformatmonth,
116
+ week: calendars[i].titleformatweek,
117
+ day: calendars[i].titleformatday
118
+ },
119
+ header: {
120
+ left: calendars[i].headerleft,
121
+ center: calendars[i].headercenter,
122
+ right: calendars[i].headerright
123
+ },
124
+ eventRender: function (a, b, v) {
125
+ var c = $(v.calendar.options.id).find(".filter-category .eo-cal-filter").val();
126
+ var d = $(v.calendar.options.id).find(".filter-venue .eo-cal-filter").val();
127
+
128
+ if (typeof c !== "undefined" && c != "" && $.inArray(c, a.category) < 0) {
129
+ return "<div></div>";
130
+ }
131
+ if (typeof d !== "undefined" && d != "" && d != a.venue) {
132
+ return "<div></div>";
133
+ }
134
+ if (! v.calendar.options.tooltip ) {
135
+ return
136
+ }
137
+
138
+ $(b).qtip({
139
+ content: {
140
+ text: a.description,
141
+ button: "x",
142
+ title: a.title
143
+ },
144
+ position: {
145
+ my: "top center",
146
+ at: "bottom center"
147
+ },
148
+ hide: {
149
+ fixed: true,
150
+ delay: 500,
151
+ effect: function (a) {
152
+ $(this).fadeOut("50")
153
+ }
154
+ },
155
+ border: {
156
+ radius: 4,
157
+ width: 3
158
+ },
159
+ style: {
160
+ classes: "ui-tooltip-shadow",
161
+ widget: true,
162
+ tip: "topMiddle"
163
+ }
164
+ })
165
+ },
166
+ buttonText: {
167
+ today: EOAjaxFront.locale.today,
168
+ month: EOAjaxFront.locale.month,
169
+ week: EOAjaxFront.locale.week,
170
+ day: EOAjaxFront.locale.day,
171
+ cat: EOAjaxFront.locale.cat,
172
+ venue: EOAjaxFront.locale.venue
173
+ },
174
+ monthNames: EOAjaxFront.locale.monthNames,
175
+ monthNamesShort: EOAjaxFront.locale.monthAbbrev,
176
+ dayNames: EOAjaxFront.locale.dayNames,
177
+ dayNamesShort: EOAjaxFront.locale.dayAbbrev,
178
+ eventColor: "#21759B",
179
+ defaultView: calendars[i].defaultview,
180
+ lazyFetching: "true",
181
+ events: function (a, b, c, d) {
182
+ var request = {
183
+ start: jQuery.fullCalendar.formatDate(a, "yyyy-MM-dd"),
184
+ end: jQuery.fullCalendar.formatDate(b, "yyyy-MM-dd"),
185
+ timeformat:d.timeFormatphp
186
+ };
187
+ if (typeof d.category !== "undefined" &&d.category != "") {
188
+ request.category = d.category
189
+ }
190
+ if (typeof d.venue !== "undefined" &&d.venue != "") {
191
+ request.venue = d.venue
192
+ }
193
+ jQuery.ajax({
194
+ url: EOAjax.ajaxurl + "?action=eventorganiser-fullcal",
195
+ dataType: "JSON",
196
+ data: request,
197
+ success: c
198
+ })
199
+ },
200
+ selectable: false,
201
+ weekMode: "variable",
202
+ aspectRatio: 1.5,
203
+ loading: function (a) {
204
+ var loading = $("#" + $(this).attr("id") + "_loading");
205
+ if (a) {
206
+ window.clearTimeout(loadingTimeOut);
207
+ loadingTimeOut = window.setTimeout(function () {
208
+ loading.show()
209
+ }, 1e3)
210
+ } else {
211
+ window.clearTimeout(loadingTimeOut);
212
+ loading.hide()
213
+ }
214
+ }
215
+ })
216
+ }
217
+
218
+ $(".eo-cal-filter").change(function () {
219
+ $(".eo-fullcalendar").fullCalendar("rerenderEvents")
220
+ })
221
+
222
+ $('.eo-mini-calendar').datepicker({
223
+ dateFormat: 'DD, d MM, yy',
224
+ changeMonth: true,
225
+ changeYear: true,
226
+ dateFormat: 'DD, d MM, yy',
227
+ firstDay: parseInt(EOAjax.fullcal.firstDay),
228
+ buttonText: EOAjaxFront.locale.gotodate,
229
+ monthNamesShort: EOAjaxFront.locale.monthAbbrev,
230
+ dayNamesMin: EOAjaxFront.locale.dayAbbrev,
231
+ showOn: 'button',
232
+ onSelect: function (dateText, dp) {
233
+ var cal_id = $(this).parents('div.eo-fullcalendar').attr('id');
234
+ $('#'+cal_id).fullCalendar('gotoDate', new Date(Date.parse(dateText)));
235
+ }
236
+ });
237
+ }
238
+
239
+ if ($(".eo_widget_calendar").length > 0 ) {
240
+
241
+ $(".eo_widget_calendar tfoot").unbind("click");
242
+ $(".eo_widget_calendar").off("click").on("click", 'tfoot a', function (a) {
243
+ a.preventDefault();
244
+ var b = $(this).closest(".eo_widget_calendar").attr("id");
245
+
246
+ //Defaults
247
+ var cal = {showpastevents: 1};
248
+
249
+ //Shortcode widget calendar
250
+ if( typeof EOAjax !== "undefined" && typeof EOAjax.widget_calendars !== "undefined" ){
251
+ cal = EOAjax.widget_calendars[b];
252
+ }
253
+ //Widget calendar
254
+ if (typeof eo_widget_cal !== "undefined") {
255
+ cal = eo_widget_cal[b];
256
+ }
257
+
258
+ //Set month
259
+ cal.eo_month = eveorg_getParameterByName("eo_month", $(this).attr("href"));
260
+
261
+ $.getJSON(EOAjaxFront.adminajax + "?action=eo_widget_cal", cal,function (a) {$("#" + b + "_content").html(a)})
262
+ })
263
+ }
264
+
265
+ if ($('.eo-agenda-widget').length > 0) {
266
+ function getEvents(a, b) {
267
+ $.ajax({
268
+ url: EOAjaxFront.adminajax,
269
+ dataType: "JSON",
270
+ data: {
271
+ action: "eo_widget_agenda",
272
+ instance_number: b["number"],
273
+ direction: a,
274
+ start: b.StartDate,
275
+ end: b.EndDate
276
+ },
277
+ success: function (a) {
278
+ if (!jQuery.isArray(a) || !a[0]) {
279
+ return false
280
+ } else {
281
+ b["StartDate"] = a[0].StartDate;
282
+ b["EndDate"] = a[a.length - 1].StartDate;
283
+ populateAgenda(a, b)
284
+ }
285
+ }
286
+ })
287
+ }
288
+ function populateAgenda(a, b) {
289
+ var agendaWidget = $("#" + b.id + "_container");
290
+ var dateList = agendaWidget.find("ul.dates");
291
+ var dates = dateList.find("li");
292
+ $(dates).remove();
293
+ var current = "";
294
+ for (i = 0; i < a.length; i++) {
295
+ var d = new Date(a[i].StartDate);
296
+ if (current == "" || current != a[i].StartDate && b
297
+ .mode == "day") {
298
+ current = a[i].StartDate;
299
+ var currentList = $('<li class="date" >' + a[i].display + '<ul class="a-date"></ul></li>');
300
+ dateList.append(currentList)
301
+ }
302
+ if( b.add_to_google ){
303
+ var c = $('<li class="event"></li>').append('<span class="cat"></span><span><strong>' + a[i].time + ": </strong></span>" + a[i]
304
+ .post_title)
305
+ .append('<div class="meta" style="display:none;"><span>' + a[i].link + "</span><span>   </span><span>" + a[i]
306
+ .Glink + "</span></div>");
307
+ }else{
308
+ var c = $('<li class="event"></li>').append("<a class='eo-agenda-event-permalink' href='"+a[i].event_url+"'><span class='cat'></span><span><strong>" + a[i].time + ": </strong></span>" + a[i]
309
+ .post_title+"</a>")
310
+ }
311
+ c.find("span.cat")
312
+ .css({
313
+ background: a[i].color
314
+ });
315
+ currentList.append(c)
316
+ }
317
+ dates = dateList.find("li");
318
+ var events_el = agendaWidget.find("ul li.event");
319
+ events_el.on("click", function () {
320
+ $(this).find(".meta")
321
+ .toggle("400")
322
+ })
323
+ }
324
+ for (var agenda in eo_widget_agenda) {
325
+ agenda = eo_widget_agenda[agenda];
326
+ var d = new Date;
327
+ agenda.StartDate = $.fullCalendar.formatDate(d, "yyyy-MM-dd");
328
+ agenda.EndDate = agenda.StartDate;
329
+ getEvents(1, agenda)
330
+ }
331
+ $(".eo-agenda-widget .agenda-nav span.button").click(function (a) {
332
+ var id = $(this).parents(".eo-agenda-widget").attr("id");
333
+ agenda = eo_widget_agenda[id];
334
+ a.preventDefault();
335
+ if ($(this).hasClass("next")) {
336
+ var dir = "+1"
337
+ } else if ($(this).hasClass("prev")) {
338
+ var dir = "-1"
339
+ } else {
340
+ var par = $(this).parent();
341
+ if (par.hasClass("prev")) {
342
+ var dir = "-1"
343
+ } else {
344
+ var dir = "+1"
345
+ }
346
+ }
347
+ getEvents(dir, agenda)
348
+ })
349
+ }
350
+ });
351
+ })(jQuery);
352
+
353
+ function eveorg_getParameterByName(a, b) {
354
+ a = a.replace(/[\[]/, "\\[")
355
+ .replace(/[\]]/, "\\]");
356
+ var c = "[\\?&]" + a + "=([^&#]*)";
357
+ var d = new RegExp(c);
358
+ var e = d.exec(b);
359
+ if (e == null) return "";
360
+ else return decodeURIComponent(e[1].replace(/\+/g, " "))
361
+ }
362
+
363
+ function eo_load_map() {
364
+ var maps = EOAjax.map;
365
+ for (var i = 0; i < maps.length; i++) {
366
+
367
+ if ( null === document.getElementById( "eo_venue_map-" + (i + 1) ) )
368
+ continue;
369
+
370
+ var locations = maps[i].locations;
371
+ var b = {
372
+ zoom: maps[i].zoom,
373
+ scrollwheel: maps[i].scrollwheel,
374
+ zoomControl: maps[i].zoomcontrol,
375
+ rotateControl: maps[i].rotatecontrol,
376
+ panControl: maps[i].pancontrol,
377
+ overviewMapControl: maps[i].overviewmapcontrol,
378
+ streetViewControl: maps[i].streetviewcontrol,
379
+ draggable: maps[i].draggable,
380
+ mapTypeControl: maps[i].maptypecontrol,
381
+ mapTypeId: google.maps.MapTypeId[maps[i].maptypeid]
382
+ };
383
+ var map = new google.maps.Map(document.getElementById("eo_venue_map-" + (i + 1)), b);
384
+
385
+ // Create a new viewpoint bound
386
+ var bounds = new google.maps.LatLngBounds();
387
+
388
+ var LatLngList = new Array();
389
+ for( var j=0; j<locations.length; j++){
390
+ var lat = locations[j].lat;
391
+ var lng = locations[j].lng;
392
+ if (lat !== undefined && lng != undefined) {
393
+ LatLngList.push(new google.maps.LatLng(lat, lng));
394
+ bounds.extend (LatLngList[j]);
395
+ var c = new google.maps.Marker({
396
+ position: LatLngList[j],
397
+ map: map,
398
+ content:locations[j].tooltipContent
399
+ });
400
+ if( 'false' != maps[i].tooltip ){
401
+ google.maps.event.addListener(c, 'click',eventorganiser_venue_tooltip);
402
+ }
403
+ }
404
+ }
405
+
406
+ if( locations.length > 1 ){
407
+ // Fit these bounds to the map
408
+ map.fitBounds (bounds);
409
+ //google.maps.event.addListenerOnce(map, 'zoom_changed', function() {map.setZoom(zoom);});
410
+ }else{
411
+ map.setCenter ( LatLngList[0]);
412
+ }
413
+
414
+ }//Foreach map
415
+ }
416
+ /**
417
+ * @constructor
418
+ */
419
+ function eventorganiser_venue_tooltip() {
420
+
421
+ // Grab marker position: convert world point into pixel point
422
+ var map = this.getMap();
423
+ var pixel = this.getMap().getProjection().fromLatLngToPoint(this.position);
424
+ var topRight=map.getProjection().fromLatLngToPoint(map.getBounds().getNorthEast());
425
+ var bottomLeft=map.getProjection().fromLatLngToPoint(map.getBounds().getSouthWest());
426
+ var scale=Math.pow(2,map.getZoom());
427
+ pixel= new google.maps.Point((pixel.x- bottomLeft.x)*scale,(pixel.y-topRight.y)*scale);
428
+
429
+ //var pixel = LatLngToPixel.fromLatLngToContainerPixel(this.position);
430
+ var pos = [ pixel.x, pixel.y ];
431
+
432
+ if(this.tooltip){
433
+ this.tooltip.qtip('api').set('position.target', pos);
434
+ this.tooltip.qtip('show');
435
+ return;
436
+ }
437
+ jQuery(this.getMap().getDiv()).css({overflow: 'visible'});
438
+
439
+ // Create the tooltip on a dummy div and store it on the marker
440
+ this.tooltip =jQuery('<div />').qtip({
441
+ content: {
442
+ text: this.content
443
+ },
444
+ border: {
445
+ radius: 4,
446
+ width: 3
447
+ },
448
+ style: {
449
+ classes: "ui-tooltip-shadow",
450
+ widget: true
451
+ },
452
+ position: {
453
+ at: "right center",
454
+ my: "top center",
455
+ target: pos,
456
+ container: jQuery(this.getMap().getDiv())
457
+ },
458
+ show: {
459
+ ready: true,
460
+ event: false,
461
+ solo: true
462
+ },
463
+ hide: {
464
+ event: 'mouseleave unfocus'
465
+ }
466
+ });
467
+ }
js/frontend.min.js ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function(a){jQuery(document).ready(function(){function f(b){var d=b.categories,g="<select class='eo-cal-filter' id='eo-event-cat'>";g+="<option value=''>"+b.buttonText.cat+"</option>";for(b=0;b<d.length;b++)g+="<option class='cat-colour-"+d[b].colour+" cat' value='"+d[b].slug+"'>"+d[b].name+"</option>";g+="</select>";d=a("<span class='fc-header-dropdown filter-category'></span>");d.append(g);return d}function j(){return a("<span class='fc-header-goto'><input type='hidden' class='eo-mini-calendar'/></span>")}
2
+ function k(b){var d=b.venues,g="<select class='eo-cal-filter' id='eo-event-venue'>";g+="<option value=''>"+b.buttonText.venue+"</option>";for(b=0;b<d.length;b++)g+="<option value='"+d[b].term_id+"'>"+d[b].name+"</option>";g+="</select>";d=a("<span class='fc-header-dropdown filter-venue'></span>");d.append(g);return d}if(a("#eo-upcoming-dates").length>0&&a("#eo-upcoming-dates").find("li:gt(4)").length>0){var e={more:EOAjaxFront.locale.ShowMore,less:EOAjaxFront.locale.ShowLess};a("#eo-upcoming-dates").find("li:gt(4)").hide().end().after(a('<a href="#" id="eo-upcoming-dates-less">'+
3
+ e.less+'</a> <span id="eo-upcoming-dates-pipe">|</span> <a href="#" id="eo-upcoming-dates-more">'+e.more+"</a>"));a("#eo-upcoming-dates-pipe").hide();a("#eo-upcoming-dates-less").hide().click(function(b){b.preventDefault();b=Math.floor((a("#eo-upcoming-dates li:visible").length-1)/5)*5-1;a("#eo-upcoming-dates li:gt("+b+")").hide();a("#eo-upcoming-dates-more,#eo-upcoming-dates-pipe").show();a("#eo-upcoming-dates li:visible").length<=5&&a("#eo-upcoming-dates-less,#eo-upcoming-dates-pipe").hide()});
4
+ a("#eo-upcoming-dates-more").click(function(b){b.preventDefault();a("#eo-upcoming-dates-less,#eo-upcoming-dates-pipe, #eo-upcoming-dates li:hidden:lt(5)").show();b=a("#eo-upcoming-dates-pipe").offset();a("html, body").animate({scrollTop:Math.max(b.top+40-a(window).height(),a(window).scrollTop())});a("#eo-upcoming-dates li:hidden").length==0&&a("#eo-upcoming-dates-more,#eo-upcoming-dates-pipe").hide()})}if(a(".eo-fullcalendar").length>0){e=EOAjax.calendars;for(var l,c=0;c<e.length;c++){var n="#eo_fullcalendar_"+
5
+ (c+1);if(typeof e[c].category==="undefined")e[c].category="";if(typeof e[c].venue==="undefined")e[c].venue="";a(n).fullCalendar({id:n,year:e[c].year?e[c].year:undefined,month:e[c].month?e[c].month:undefined,date:e[c].date?e[c].date:undefined,category:e[c].event_category,venue:e[c].event_venue,customButtons:{category:f,venue:k,"goto":j},theme:true,categories:EOAjax.fullcal.categories,venues:EOAjax.fullcal.venues,timeFormatphp:e[c].timeformatphp,timeFormat:e[c].timeformat,editable:false,tooltip:e[c].tooltip,
6
+ firstDay:parseInt(EOAjax.fullcal.firstDay),weekends:e[c].weekends,allDaySlot:e[c].alldayslot,allDayText:e[c].alldaytext,axisFormat:e[c].axisformat,minTime:e[c].mintime,maxTime:e[c].maxtime,columnFormat:{month:e[c].columnformatmonth,week:e[c].columnformatweek,day:e[c].columnformatday},titleFormat:{month:e[c].titleformatmonth,week:e[c].titleformatweek,day:e[c].titleformatday},header:{left:e[c].headerleft,center:e[c].headercenter,right:e[c].headerright},eventRender:function(b,d,g){var h=a(g.calendar.options.id).find(".filter-category .eo-cal-filter").val(),
7
+ i=a(g.calendar.options.id).find(".filter-venue .eo-cal-filter").val();if(typeof h!=="undefined"&&h!=""&&a.inArray(h,b.category)<0)return"<div></div>";if(typeof i!=="undefined"&&i!=""&&i!=b.venue)return"<div></div>";g.calendar.options.tooltip&&a(d).qtip({content:{text:b.description,button:"x",title:b.title},position:{my:"top center",at:"bottom center"},hide:{fixed:true,delay:500,effect:function(){a(this).fadeOut("50")}},border:{radius:4,width:3},style:{classes:"ui-tooltip-shadow",widget:true,tip:"topMiddle"}})},
8
+ buttonText:{today:EOAjaxFront.locale.today,month:EOAjaxFront.locale.month,week:EOAjaxFront.locale.week,day:EOAjaxFront.locale.day,cat:EOAjaxFront.locale.cat,venue:EOAjaxFront.locale.venue},monthNames:EOAjaxFront.locale.monthNames,monthNamesShort:EOAjaxFront.locale.monthAbbrev,dayNames:EOAjaxFront.locale.dayNames,dayNamesShort:EOAjaxFront.locale.dayAbbrev,eventColor:"#21759B",defaultView:e[c].defaultview,lazyFetching:"true",events:function(b,d,g,h){b={start:jQuery.fullCalendar.formatDate(b,"yyyy-MM-dd"),
9
+ end:jQuery.fullCalendar.formatDate(d,"yyyy-MM-dd"),timeformat:h.timeFormatphp};if(typeof h.category!=="undefined"&&h.category!="")b.category=h.category;if(typeof h.venue!=="undefined"&&h.venue!="")b.venue=h.venue;jQuery.ajax({url:EOAjax.ajaxurl+"?action=eventorganiser-fullcal",dataType:"JSON",data:b,success:g})},selectable:false,weekMode:"variable",aspectRatio:1.5,loading:function(b){var d=a("#"+a(this).attr("id")+"_loading");if(b){window.clearTimeout(l);l=window.setTimeout(function(){d.show()},1E3)}else{window.clearTimeout(l);
10
+ d.hide()}}})}a(".eo-cal-filter").change(function(){a(".eo-fullcalendar").fullCalendar("rerenderEvents")});a(".eo-mini-calendar").datepicker({dateFormat:"DD, d MM, yy",changeMonth:true,changeYear:true,dateFormat:"DD, d MM, yy",firstDay:parseInt(EOAjax.fullcal.firstDay),buttonText:EOAjaxFront.locale.gotodate,monthNamesShort:EOAjaxFront.locale.monthAbbrev,dayNamesMin:EOAjaxFront.locale.dayAbbrev,showOn:"button",onSelect:function(b){var d=a(this).parents("div.eo-fullcalendar").attr("id");a("#"+d).fullCalendar("gotoDate",
11
+ new Date(Date.parse(b)))}})}if(a(".eo_widget_calendar").length>0){a(".eo_widget_calendar tfoot").unbind("click");a(".eo_widget_calendar").off("click").on("click","tfoot a",function(b){b.preventDefault();var d=a(this).closest(".eo_widget_calendar").attr("id");b={showpastevents:1};if(typeof EOAjax!=="undefined"&&typeof EOAjax.widget_calendars!=="undefined")b=EOAjax.widget_calendars[d];if(typeof eo_widget_cal!=="undefined")b=eo_widget_cal[d];b.eo_month=eveorg_getParameterByName("eo_month",a(this).attr("href"));
12
+ a.getJSON(EOAjaxFront.adminajax+"?action=eo_widget_cal",b,function(g){a("#"+d+"_content").html(g)})})}if(a(".eo-agenda-widget").length>0){function o(b,d){a.ajax({url:EOAjaxFront.adminajax,dataType:"JSON",data:{action:"eo_widget_agenda",instance_number:d.number,direction:b,start:d.StartDate,end:d.EndDate},success:function(g){if(!jQuery.isArray(g)||!g[0])return false;else{d.StartDate=g[0].StartDate;d.EndDate=g[g.length-1].StartDate;r(g,d)}}})}function r(b,d){var g=a("#"+d.id+"_container"),h=g.find("ul.dates"),
13
+ i=h.find("li");a(i).remove();i="";for(c=0;c<b.length;c++){if(i==""||i!=b[c].StartDate&&d.mode=="day"){i=b[c].StartDate;var p=a('<li class="date" >'+b[c].display+'<ul class="a-date"></ul></li>');h.append(p)}var q=d.add_to_google?a('<li class="event"></li>').append('<span class="cat"></span><span><strong>'+b[c].time+": </strong></span>"+b[c].post_title).append('<div class="meta" style="display:none;"><span>'+b[c].link+"</span><span> \u00a0 </span><span>"+b[c].Glink+"</span></div>"):a('<li class="event"></li>').append("<a class='eo-agenda-event-permalink' href='"+
14
+ b[c].event_url+"'><span class='cat'></span><span><strong>"+b[c].time+": </strong></span>"+b[c].post_title+"</a>");q.find("span.cat").css({background:b[c].color});p.append(q)}i=h.find("li");g.find("ul li.event").on("click",function(){a(this).find(".meta").toggle("400")})}for(var m in eo_widget_agenda){m=eo_widget_agenda[m];m.StartDate=a.fullCalendar.formatDate(new Date,"yyyy-MM-dd");m.EndDate=m.StartDate;o(1,m)}a(".eo-agenda-widget .agenda-nav span.button").click(function(b){var d=a(this).parents(".eo-agenda-widget").attr("id");
15
+ m=eo_widget_agenda[d];b.preventDefault();b=a(this).hasClass("next")?"+1":a(this).hasClass("prev")?"-1":a(this).parent().hasClass("prev")?"-1":"+1";o(b,m)})}})})(jQuery);function eveorg_getParameterByName(a,f){a=a.replace(/[\[]/,"\\[").replace(/[\]]/,"\\]");a=(new RegExp("[\\?&]"+a+"=([^&#]*)")).exec(f);return a==null?"":decodeURIComponent(a[1].replace(/\+/g," "))}
16
+ function eo_load_map(){for(var a=EOAjax.map,f=0;f<a.length;f++)if(null!==document.getElementById("eo_venue_map-"+(f+1))){var j=a[f].locations,k={zoom:a[f].zoom,scrollwheel:a[f].scrollwheel,zoomControl:a[f].zoomcontrol,rotateControl:a[f].rotatecontrol,panControl:a[f].pancontrol,overviewMapControl:a[f].overviewmapcontrol,streetViewControl:a[f].streetviewcontrol,draggable:a[f].draggable,mapTypeControl:a[f].maptypecontrol,mapTypeId:google.maps.MapTypeId[a[f].maptypeid]};k=new google.maps.Map(document.getElementById("eo_venue_map-"+
17
+ (f+1)),k);for(var e=new google.maps.LatLngBounds,l=[],c=0;c<j.length;c++){var n=j[c].lat,o=j[c].lng;if(n!==undefined&&o!=undefined){l.push(new google.maps.LatLng(n,o));e.extend(l[c]);n=new google.maps.Marker({position:l[c],map:k,content:j[c].tooltipContent});"false"!=a[f].tooltip&&google.maps.event.addListener(n,"click",eventorganiser_venue_tooltip)}}j.length>1?k.fitBounds(e):k.setCenter(l[0])}}
18
+ function eventorganiser_venue_tooltip(){var a=this.getMap(),f=this.getMap().getProjection().fromLatLngToPoint(this.position),j=a.getProjection().fromLatLngToPoint(a.getBounds().getNorthEast()),k=a.getProjection().fromLatLngToPoint(a.getBounds().getSouthWest());a=Math.pow(2,a.getZoom());f=new google.maps.Point((f.x-k.x)*a,(f.y-j.y)*a);f=[f.x,f.y];if(this.tooltip){this.tooltip.qtip("api").set("position.target",f);this.tooltip.qtip("show")}else{jQuery(this.getMap().getDiv()).css({overflow:"visible"});
19
+ this.tooltip=jQuery("<div />").qtip({content:{text:this.content},border:{radius:4,width:3},style:{classes:"ui-tooltip-shadow",widget:true},position:{at:"right center",my:"top center",target:f,container:jQuery(this.getMap().getDiv())},show:{ready:true,event:false,solo:true},hide:{event:"mouseleave unfocus"}})}};
js/fullcalendar.js ADDED
@@ -0,0 +1,5236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ - * NOTE: This is an edited version of the FullCalendar v1.5.4 (eventorganiser branch)
3
+ -*/
4
+ /**
5
+ * @preserve
6
+ * FullCalendar v1.5.4
7
+ * http://arshaw.com/fullcalendar/
8
+ *
9
+ * Use fullcalendar.css for basic styling.
10
+ * For event drag & drop, requires jQuery UI draggable.
11
+ * For event resizing, requires jQuery UI resizable.
12
+ *
13
+ * Copyright (c) 2011 Adam Shaw
14
+ * Dual licensed under the MIT and GPL licenses, located in
15
+ * MIT-LICENSE.txt and GPL-LICENSE.txt respectively.
16
+ *
17
+ * Date: Sat Nov 3 20:29:34 2012 +0000
18
+ *
19
+ */
20
+
21
+ (function($, undefined) {
22
+
23
+
24
+ var defaults = {
25
+
26
+ // display
27
+ defaultView: 'month',
28
+ aspectRatio: 1.35,
29
+ header: {
30
+ left: 'title',
31
+ center: '',
32
+ right: 'today prev,next'
33
+ },
34
+ weekends: true,
35
+
36
+ // editing
37
+ //editable: false,
38
+ //disableDragging: false,
39
+ //disableResizing: false,
40
+
41
+ allDayDefault: true,
42
+ ignoreTimezone: true,
43
+
44
+ // event ajax
45
+ lazyFetching: true,
46
+ startParam: 'start',
47
+ endParam: 'end',
48
+
49
+ // time formats
50
+ titleFormat: {
51
+ month: 'MMMM yyyy',
52
+ week: "MMM d[ yyyy]{ '&#8212;'[ MMM] d yyyy}",
53
+ day: 'dddd, MMM d, yyyy'
54
+ },
55
+ columnFormat: {
56
+ month: 'ddd',
57
+ week: 'ddd M/d',
58
+ day: 'dddd M/d'
59
+ },
60
+ timeFormat: { // for event elements
61
+ '': 'h(:mm)t' // default
62
+ },
63
+
64
+ // locale
65
+ isRTL: false,
66
+ firstDay: 0,
67
+ monthNames: ['January','February','March','April','May','June','July','August','September','October','November','December'],
68
+ monthNamesShort: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
69
+ dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
70
+ dayNamesShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'],
71
+ buttonText: {
72
+ prev: '&nbsp;&#9668;&nbsp;',
73
+ next: '&nbsp;&#9658;&nbsp;',
74
+ prevYear: '&nbsp;&lt;&lt;&nbsp;',
75
+ nextYear: '&nbsp;&gt;&gt;&nbsp;',
76
+ today: 'today',
77
+ month: 'month',
78
+ week: 'week',
79
+ day: 'day'
80
+ },
81
+
82
+ // jquery-ui theming
83
+ theme: false,
84
+ buttonIcons: {
85
+ prev: 'circle-triangle-w',
86
+ next: 'circle-triangle-e'
87
+ },
88
+
89
+ //selectable: false,
90
+ unselectAuto: true,
91
+
92
+ dropAccept: '*'
93
+
94
+ };
95
+
96
+ // right-to-left defaults
97
+ var rtlDefaults = {
98
+ header: {
99
+ left: 'next,prev today',
100
+ center: '',
101
+ right: 'title'
102
+ },
103
+ buttonText: {
104
+ prev: '&nbsp;&#9658;&nbsp;',
105
+ next: '&nbsp;&#9668;&nbsp;',
106
+ prevYear: '&nbsp;&gt;&gt;&nbsp;',
107
+ nextYear: '&nbsp;&lt;&lt;&nbsp;'
108
+ },
109
+ buttonIcons: {
110
+ prev: 'circle-triangle-e',
111
+ next: 'circle-triangle-w'
112
+ }
113
+ };
114
+
115
+
116
+
117
+ var fc = $.fullCalendar = { version: "1.5.4" };
118
+ var fcViews = fc.views = {};
119
+
120
+
121
+ $.fn.fullCalendar = function(options) {
122
+
123
+
124
+ // method calling
125
+ if (typeof options == 'string') {
126
+ var args = Array.prototype.slice.call(arguments, 1);
127
+ var res;
128
+ this.each(function() {
129
+ var calendar = $.data(this, 'fullCalendar');
130
+ if (calendar && $.isFunction(calendar[options])) {
131
+ var r = calendar[options].apply(calendar, args);
132
+ if (res === undefined) {
133
+ res = r;
134
+ }
135
+ if (options == 'destroy') {
136
+ $.removeData(this, 'fullCalendar');
137
+ }
138
+ }
139
+ });
140
+ if (res !== undefined) {
141
+ return res;
142
+ }
143
+ return this;
144
+ }
145
+
146
+
147
+ // would like to have this logic in EventManager, but needs to happen before options are recursively extended
148
+ var eventSources = options.eventSources || [];
149
+ delete options.eventSources;
150
+ if (options.events) {
151
+ eventSources.push(options.events);
152
+ delete options.events;
153
+ }
154
+
155
+
156
+ options = $.extend(true, {},
157
+ defaults,
158
+ (options.isRTL || options.isRTL===undefined && defaults.isRTL) ? rtlDefaults : {},
159
+ options
160
+ );
161
+
162
+
163
+ this.each(function(i, _element) {
164
+ var element = $(_element);
165
+ var calendar = new Calendar(element, options, eventSources);
166
+ element.data('fullCalendar', calendar); // TODO: look into memory leak implications
167
+ calendar.render();
168
+ });
169
+
170
+
171
+ return this;
172
+
173
+ };
174
+
175
+
176
+ // function for adding/overriding defaults
177
+ function setDefaults(d) {
178
+ $.extend(true, defaults, d);
179
+ }
180
+
181
+
182
+
183
+
184
+ function Calendar(element, options, eventSources) {
185
+ var t = this;
186
+
187
+
188
+ // exports
189
+ t.options = options;
190
+ t.render = render;
191
+ t.destroy = destroy;
192
+ t.refetchEvents = refetchEvents;
193
+ t.reportEvents = reportEvents;
194
+ t.reportEventChange = reportEventChange;
195
+ t.rerenderEvents = rerenderEvents;
196
+ t.changeView = changeView;
197
+ t.select = select;
198
+ t.unselect = unselect;
199
+ t.prev = prev;
200
+ t.next = next;
201
+ t.prevYear = prevYear;
202
+ t.nextYear = nextYear;
203
+ t.today = today;
204
+ t.gotoDate = gotoDate;
205
+ t.incrementDate = incrementDate;
206
+ t.formatDate = function(format, date) { return formatDate(format, date, options) };
207
+ t.formatDates = function(format, date1, date2) { return formatDates(format, date1, date2, options) };
208
+ t.getDate = getDate;
209
+ t.getView = getView;
210
+ t.option = option;
211
+ t.trigger = trigger;
212
+
213
+
214
+ // imports
215
+ EventManager.call(t, options, eventSources);
216
+ var isFetchNeeded = t.isFetchNeeded;
217
+ var fetchEvents = t.fetchEvents;
218
+
219
+
220
+ // locals
221
+ var _element = element[0];
222
+ var header;
223
+ var headerElement;
224
+ var content;
225
+ var tm; // for making theme classes
226
+ var currentView;
227
+ var viewInstances = {};
228
+ var elementOuterWidth;
229
+ var suggestedViewHeight;
230
+ var absoluteViewElement;
231
+ var resizeUID = 0;
232
+ var ignoreWindowResize = 0;
233
+ var date = new Date();
234
+ var events = [];
235
+ var _dragElement;
236
+
237
+
238
+
239
+ /* Main Rendering
240
+ -----------------------------------------------------------------------------*/
241
+
242
+
243
+ setYMD(date, options.year, options.month, options.date);
244
+
245
+
246
+ function render(inc) {
247
+ if (!content) {
248
+ initialRender();
249
+ }else{
250
+ calcSize();
251
+ markSizesDirty();
252
+ markEventsDirty();
253
+ renderView(inc);
254
+ }
255
+ }
256
+
257
+
258
+ function initialRender() {
259
+ tm = options.theme ? 'ui' : 'fc';
260
+ element.addClass('fc');
261
+ if (options.isRTL) {
262
+ element.addClass('fc-rtl');
263
+ }
264
+ if (options.theme) {
265
+ element.addClass('ui-widget');
266
+ }
267
+ content = $("<div class='fc-content' style='position:relative'/>")
268
+ .prependTo(element);
269
+ header = new Header(t, options);
270
+ headerElement = header.render();
271
+ if (headerElement) {
272
+ element.prepend(headerElement);
273
+ }
274
+ changeView(options.defaultView);
275
+ $(window).resize(windowResize);
276
+ // needed for IE in a 0x0 iframe, b/c when it is resized, never triggers a windowResize
277
+ if (!bodyVisible()) {
278
+ lateRender();
279
+ }
280
+ }
281
+
282
+
283
+ // called when we know the calendar couldn't be rendered when it was initialized,
284
+ // but we think it's ready now
285
+ function lateRender() {
286
+ setTimeout(function() { // IE7 needs this so dimensions are calculated correctly
287
+ if (!currentView.start && bodyVisible()) { // !currentView.start makes sure this never happens more than once
288
+ renderView();
289
+ }
290
+ },0);
291
+ }
292
+
293
+
294
+ function destroy() {
295
+ $(window).unbind('resize', windowResize);
296
+ header.destroy();
297
+ content.remove();
298
+ element.removeClass('fc fc-rtl ui-widget');
299
+ }
300
+
301
+
302
+
303
+ function elementVisible() {
304
+ return _element.offsetWidth !== 0;
305
+ }
306
+
307
+
308
+ function bodyVisible() {
309
+ return $('body')[0].offsetWidth !== 0;
310
+ }
311
+
312
+
313
+
314
+ /* View Rendering
315
+ -----------------------------------------------------------------------------*/
316
+
317
+ // TODO: improve view switching (still weird transition in IE, and FF has whiteout problem)
318
+
319
+ function changeView(newViewName) {
320
+ if (!currentView || newViewName != currentView.name) {
321
+ ignoreWindowResize++; // because setMinHeight might change the height before render (and subsequently setSize) is reached
322
+
323
+ unselect();
324
+
325
+ var oldView = currentView;
326
+ var newViewElement;
327
+
328
+ if (oldView) {
329
+ (oldView.beforeHide || noop)(); // called before changing min-height. if called after, scroll state is reset (in Opera)
330
+ setMinHeight(content, content.height());
331
+ oldView.element.hide();
332
+ }else{
333
+ setMinHeight(content, 1); // needs to be 1 (not 0) for IE7, or else view dimensions miscalculated
334
+ }
335
+ content.css('overflow', 'hidden');
336
+
337
+ currentView = viewInstances[newViewName];
338
+ if (currentView) {
339
+ currentView.element.show();
340
+ }else{
341
+ currentView = viewInstances[newViewName] = new fcViews[newViewName](
342
+ newViewElement = absoluteViewElement =
343
+ $("<div class='fc-view fc-view-" + newViewName + "' style='position:absolute'/>")
344
+ .appendTo(content),
345
+ t // the calendar object
346
+ );
347
+ }
348
+
349
+ if (oldView) {
350
+ header.deactivateButton(oldView.name);
351
+ }
352
+ header.activateButton(newViewName);
353
+
354
+ renderView(); // after height has been set, will make absoluteViewElement's position=relative, then set to null
355
+
356
+ content.css('overflow', '');
357
+ if (oldView) {
358
+ setMinHeight(content, 1);
359
+ }
360
+
361
+ if (!newViewElement) {
362
+ (currentView.afterShow || noop)(); // called after setting min-height/overflow, so in final scroll state (for Opera)
363
+ }
364
+
365
+ ignoreWindowResize--;
366
+ }
367
+ }
368
+
369
+
370
+
371
+ function renderView(inc) {
372
+ if (elementVisible()) {
373
+ ignoreWindowResize++; // because renderEvents might temporarily change the height before setSize is reached
374
+
375
+ unselect();
376
+
377
+ if (suggestedViewHeight === undefined) {
378
+ calcSize();
379
+ }
380
+
381
+ var forceEventRender = false;
382
+ if (!currentView.start || inc || date < currentView.start || date >= currentView.end) {
383
+ // view must render an entire new date range (and refetch/render events)
384
+ currentView.render(date, inc || 0); // responsible for clearing events
385
+ setSize(true);
386
+ forceEventRender = true;
387
+ }
388
+ else if (currentView.sizeDirty) {
389
+ // view must resize (and rerender events)
390
+ currentView.clearEvents();
391
+ setSize();
392
+ forceEventRender = true;
393
+ }
394
+ else if (currentView.eventsDirty) {
395
+ currentView.clearEvents();
396
+ forceEventRender = true;
397
+ }
398
+ currentView.sizeDirty = false;
399
+ currentView.eventsDirty = false;
400
+ updateEvents(forceEventRender);
401
+
402
+ elementOuterWidth = element.outerWidth();
403
+
404
+ header.updateTitle(currentView.title);
405
+ var today = new Date();
406
+ if (today >= currentView.start && today < currentView.end) {
407
+ header.disableButton('today');
408
+ }else{
409
+ header.enableButton('today');
410
+ }
411
+
412
+ ignoreWindowResize--;
413
+ currentView.trigger('viewDisplay', _element);
414
+ }
415
+ }
416
+
417
+
418
+
419
+ /* Resizing
420
+ -----------------------------------------------------------------------------*/
421
+
422
+
423
+ function updateSize() {
424
+ markSizesDirty();
425
+ if (elementVisible()) {
426
+ calcSize();
427
+ setSize();
428
+ unselect();
429
+ currentView.clearEvents();
430
+ currentView.renderEvents(events);
431
+ currentView.sizeDirty = false;
432
+ }
433
+ }
434
+
435
+
436
+ function markSizesDirty() {
437
+ $.each(viewInstances, function(i, inst) {
438
+ inst.sizeDirty = true;
439
+ });
440
+ }
441
+
442
+
443
+ function calcSize() {
444
+ if (options.contentHeight) {
445
+ suggestedViewHeight = options.contentHeight;
446
+ }
447
+ else if (options.height) {
448
+ suggestedViewHeight = options.height - (headerElement ? headerElement.height() : 0) - vsides(content);
449
+ }
450
+ else {
451
+ suggestedViewHeight = Math.round(content.width() / Math.max(options.aspectRatio, .5));
452
+ }
453
+ }
454
+
455
+
456
+ function setSize(dateChanged) { // todo: dateChanged?
457
+ ignoreWindowResize++;
458
+ currentView.setHeight(suggestedViewHeight, dateChanged);
459
+ if (absoluteViewElement) {
460
+ absoluteViewElement.css('position', 'relative');
461
+ absoluteViewElement = null;
462
+ }
463
+ currentView.setWidth(content.width(), dateChanged);
464
+ ignoreWindowResize--;
465
+ }
466
+
467
+
468
+ function windowResize() {
469
+ if (!ignoreWindowResize) {
470
+ if (currentView.start) { // view has already been rendered
471
+ var uid = ++resizeUID;
472
+ setTimeout(function() { // add a delay
473
+ if (uid == resizeUID && !ignoreWindowResize && elementVisible()) {
474
+ if (elementOuterWidth != (elementOuterWidth = element.outerWidth())) {
475
+ ignoreWindowResize++; // in case the windowResize callback changes the height
476
+ updateSize();
477
+ currentView.trigger('windowResize', _element);
478
+ ignoreWindowResize--;
479
+ }
480
+ }
481
+ }, 200);
482
+ }else{
483
+ // calendar must have been initialized in a 0x0 iframe that has just been resized
484
+ lateRender();
485
+ }
486
+ }
487
+ }
488
+
489
+
490
+
491
+ /* Event Fetching/Rendering
492
+ -----------------------------------------------------------------------------*/
493
+
494
+
495
+ // fetches events if necessary, rerenders events if necessary (or if forced)
496
+ function updateEvents(forceRender) {
497
+ if (!options.lazyFetching || isFetchNeeded(currentView.visStart, currentView.visEnd)) {
498
+ refetchEvents();
499
+ }
500
+ else if (forceRender) {
501
+ rerenderEvents();
502
+ }
503
+ }
504
+
505
+
506
+ function refetchEvents() {
507
+ fetchEvents(currentView.visStart, currentView.visEnd, options); // will call reportEvents
508
+ }
509
+
510
+
511
+ // called when event data arrives
512
+ function reportEvents(_events) {
513
+ events = _events;
514
+ rerenderEvents();
515
+ }
516
+
517
+
518
+ // called when a single event's data has been changed
519
+ function reportEventChange(eventID) {
520
+ rerenderEvents(eventID);
521
+ }
522
+
523
+
524
+ // attempts to rerenderEvents
525
+ function rerenderEvents(modifiedEventID) {
526
+ markEventsDirty();
527
+ if (elementVisible()) {
528
+ currentView.clearEvents();
529
+ currentView.renderEvents(events, modifiedEventID);
530
+ currentView.eventsDirty = false;
531
+ }
532
+ }
533
+
534
+
535
+ function markEventsDirty() {
536
+ $.each(viewInstances, function(i, inst) {
537
+ inst.eventsDirty = true;
538
+ });
539
+ }
540
+
541
+
542
+
543
+ /* Selection
544
+ -----------------------------------------------------------------------------*/
545
+
546
+
547
+ function select(start, end, allDay) {
548
+ currentView.select(start, end, allDay===undefined ? true : allDay);
549
+ }
550
+
551
+
552
+ function unselect() { // safe to be called before renderView
553
+ if (currentView) {
554
+ currentView.unselect();
555
+ }
556
+ }
557
+
558
+
559
+
560
+ /* Date
561
+ -----------------------------------------------------------------------------*/
562
+
563
+
564
+ function prev() {
565
+ renderView(-1);
566
+ }
567
+
568
+
569
+ function next() {
570
+ renderView(1);
571
+ }
572
+
573
+
574
+ function prevYear() {
575
+ addYears(date, -1);
576
+ renderView();
577
+ }
578
+
579
+
580
+ function nextYear() {
581
+ addYears(date, 1);
582
+ renderView();
583
+ }
584
+
585
+
586
+ function today() {
587
+ date = new Date();
588
+ renderView();
589
+ }
590
+
591
+
592
+ function gotoDate(year, month, dateOfMonth) {
593
+ if (year instanceof Date) {
594
+ date = cloneDate(year); // provided 1 argument, a Date
595
+ }else{
596
+ setYMD(date, year, month, dateOfMonth);
597
+ }
598
+ renderView();
599
+ }
600
+
601
+
602
+ function incrementDate(years, months, days) {
603
+ if (years !== undefined) {
604
+ addYears(date, years);
605
+ }
606
+ if (months !== undefined) {
607
+ addMonths(date, months);
608
+ }
609
+ if (days !== undefined) {
610
+ addDays(date, days);
611
+ }
612
+ renderView();
613
+ }
614
+
615
+
616
+ function getDate() {
617
+ return cloneDate(date);
618
+ }
619
+
620
+
621
+
622
+ /* Misc
623
+ -----------------------------------------------------------------------------*/
624
+
625
+
626
+ function getView() {
627
+ return currentView;
628
+ }
629
+
630
+
631
+ function option(name, value) {
632
+ if (value === undefined) {
633
+ return options[name];
634
+ }
635
+ if (name == 'height' || name == 'contentHeight' || name == 'aspectRatio') {
636
+ options[name] = value;
637
+ updateSize();
638
+ }else{
639
+ options[name] = value;
640
+ rerenderEvents();
641
+ }
642
+ }
643
+
644
+
645
+ function trigger(name, thisObj) {
646
+ if (options[name]) {
647
+ return options[name].apply(
648
+ thisObj || _element,
649
+ Array.prototype.slice.call(arguments, 2)
650
+ );
651
+ }
652
+ }
653
+
654
+
655
+
656
+ /* External Dragging
657
+ ------------------------------------------------------------------------*/
658
+
659
+ if (options.droppable) {
660
+ $(document)
661
+ .bind('dragstart', function(ev, ui) {
662
+ var _e = ev.target;
663
+ var e = $(_e);
664
+ if (!e.parents('.fc').length) { // not already inside a calendar
665
+ var accept = options.dropAccept;
666
+ if ($.isFunction(accept) ? accept.call(_e, e) : e.is(accept)) {
667
+ _dragElement = _e;
668
+ currentView.dragStart(_dragElement, ev, ui);
669
+ }
670
+ }
671
+ })
672
+ .bind('dragstop', function(ev, ui) {
673
+ if (_dragElement) {
674
+ currentView.dragStop(_dragElement, ev, ui);
675
+ _dragElement = null;
676
+ }
677
+ });
678
+ }
679
+
680
+
681
+ }
682
+
683
+ function Header(calendar, options) {
684
+ var t = this;
685
+
686
+
687
+ // exports
688
+ t.render = render;
689
+ t.destroy = destroy;
690
+ t.updateTitle = updateTitle;
691
+ t.activateButton = activateButton;
692
+ t.deactivateButton = deactivateButton;
693
+ t.disableButton = disableButton;
694
+ t.enableButton = enableButton;
695
+
696
+
697
+ // locals
698
+ var element = $([]);
699
+ var tm;
700
+
701
+
702
+
703
+ function render() {
704
+ tm = options.theme ? 'ui' : 'fc';
705
+ var sections = options.header;
706
+ if (sections) {
707
+ element = $("<table class='fc-header' style='width:100%'/>")
708
+ .append(
709
+ $("<tr/>")
710
+ .append(renderSection('left'))
711
+ .append(renderSection('center'))
712
+ .append(renderSection('right'))
713
+ );
714
+ return element;
715
+ }
716
+ }
717
+
718
+
719
+ function destroy() {
720
+ element.remove();
721
+ }
722
+
723
+
724
+ function renderSection(position) {
725
+ var e = $("<td class='fc-header-" + position + "'/>");
726
+ var buttonStr = options.header[position];
727
+ if (buttonStr) {
728
+ $.each(buttonStr.split(' '), function(i) {
729
+ if (i > 0) {
730
+ e.append("<span class='fc-header-space'/>");
731
+ }
732
+ var prevButton;
733
+ $.each(this.split(','), function(j, buttonName) {
734
+ if (buttonName == 'title') {
735
+ e.append("<span class='fc-header-title'><h2>&nbsp;</h2></span>");
736
+ if (prevButton) {
737
+ prevButton.addClass(tm + '-corner-right');
738
+ }
739
+ prevButton = null;
740
+
741
+ }else if( $.isFunction(options.customButtons[buttonName]) ){
742
+ var element = options.customButtons[buttonName](options);
743
+ e.append(element);
744
+ }else{
745
+ var buttonClick;
746
+ if (calendar[buttonName]) {
747
+ buttonClick = calendar[buttonName]; // calendar method
748
+ }
749
+ else if (fcViews[buttonName]) {
750
+ buttonClick = function() {
751
+ button.removeClass(tm + '-state-hover'); // forget why
752
+ calendar.changeView(buttonName);
753
+ };
754
+ }
755
+ if (buttonClick) {
756
+ var icon = options.theme ? smartProperty(options.buttonIcons, buttonName) : null; // why are we using smartProperty here?
757
+ var text = smartProperty(options.buttonText, buttonName); // why are we using smartProperty here?
758
+
759
+ if ( options.buttonui ){
760
+ var button = $("<span class='fc-button fc-button-" + buttonName + " " + tm + "-state-default'>" +text+"</span>");
761
+ button.button();
762
+ }else{
763
+ var button = $(
764
+ "<span class='fc-button fc-button-" + buttonName + " " + tm + "-state-default'>" +
765
+ "<span class='fc-button-inner'>" +
766
+ "<span class='fc-button-content'>" +
767
+ (icon ?
768
+ "<span class='fc-icon-wrap'>" +
769
+ "<span class='ui-icon ui-icon-" + icon + "'/>" +
770
+ "</span>" :
771
+ text
772
+ ) +
773
+ "</span>" +
774
+ "<span class='fc-button-effect'><span></span></span>" +
775
+ "</span>" +
776
+ "</span>"
777
+ );
778
+ }
779
+ if (button) {
780
+ button
781
+ .click(function() {
782
+ if (!button.hasClass(tm + '-state-disabled')) {
783
+ buttonClick();
784
+ }
785
+ })
786
+ .mousedown(function() {
787
+ button
788
+ .not('.' + tm + '-state-active')
789
+ .not('.' + tm + '-state-disabled')
790
+ .addClass(tm + '-state-down');
791
+ })
792
+ .mouseup(function() {
793
+ button.removeClass(tm + '-state-down');
794
+ })
795
+ .hover(
796
+ function() {
797
+ button
798
+ .not('.' + tm + '-state-active')
799
+ .not('.' + tm + '-state-disabled')
800
+ .addClass(tm + '-state-hover');
801
+ },
802
+ function() {
803
+ button
804
+ .removeClass(tm + '-state-hover')
805
+ .removeClass(tm + '-state-down');
806
+ }
807
+ )
808
+ .appendTo(e);
809
+ if (!prevButton) {
810
+ button.addClass(tm + '-corner-left');
811
+ }
812
+ prevButton = button;
813
+ }
814
+ }
815
+ }
816
+ });
817
+ if (prevButton) {
818
+ prevButton.addClass(tm + '-corner-right');
819
+ }
820
+ });
821
+ }
822
+ return e;
823
+ }
824
+
825
+
826
+ function updateTitle(html) {
827
+ element.find('h2')
828
+ .html(html);
829
+ }
830
+
831
+
832
+ function activateButton(buttonName) {
833
+ element.find('span.fc-button-' + buttonName)
834
+ .addClass(tm + '-state-active');
835
+ }
836
+
837
+
838
+ function deactivateButton(buttonName) {
839
+ element.find('span.fc-button-' + buttonName)
840
+ .removeClass(tm + '-state-active');
841
+ }
842
+
843
+
844
+ function disableButton(buttonName) {
845
+ element.find('span.fc-button-' + buttonName)
846
+ .addClass(tm + '-state-disabled');
847
+ }
848
+
849
+
850
+ function enableButton(buttonName) {
851
+ element.find('span.fc-button-' + buttonName)
852
+ .removeClass(tm + '-state-disabled');
853
+ }
854
+
855
+
856
+ }
857
+
858
+ fc.sourceNormalizers = [];
859
+ fc.sourceFetchers = [];
860
+
861
+ var ajaxDefaults = {
862
+ dataType: 'json',
863
+ cache: false
864
+ };
865
+
866
+ var eventGUID = 1;
867
+
868
+
869
+ function EventManager(options, _sources) {
870
+ var t = this;
871
+
872
+
873
+ // exports
874
+ t.isFetchNeeded = isFetchNeeded;
875
+ t.fetchEvents = fetchEvents;
876
+ t.addEventSource = addEventSource;
877
+ t.removeEventSource = removeEventSource;
878
+ t.updateEvent = updateEvent;
879
+ t.renderEvent = renderEvent;
880
+ t.removeEvents = removeEvents;
881
+ t.clientEvents = clientEvents;
882
+ t.normalizeEvent = normalizeEvent;
883
+
884
+
885
+ // imports
886
+ var trigger = t.trigger;
887
+ var getView = t.getView;
888
+ var reportEvents = t.reportEvents;
889
+
890
+
891
+ // locals
892
+ var stickySource = { events: [] };
893
+ var sources = [ stickySource ];
894
+ var rangeStart, rangeEnd;
895
+ var currentFetchID = 0;
896
+ var pendingSourceCnt = 0;
897
+ var loadingLevel = 0;
898
+ var cache = [];
899
+
900
+
901
+ for (var i=0; i<_sources.length; i++) {
902
+ _addEventSource(_sources[i]);
903
+ }
904
+
905
+
906
+
907
+ /* Fetching
908
+ -----------------------------------------------------------------------------*/
909
+
910
+
911
+ function isFetchNeeded(start, end) {
912
+ return !rangeStart || start < rangeStart || end > rangeEnd;
913
+ }
914
+
915
+
916
+ function fetchEvents(start, end, options) {
917
+ rangeStart = start;
918
+ rangeEnd = end;
919
+ cache = [];
920
+ var fetchID = ++currentFetchID;
921
+ var len = sources.length;
922
+ pendingSourceCnt = len;
923
+ for (var i=0; i<len; i++) {
924
+ fetchEventSource(sources[i], fetchID, options);
925
+ }
926
+ }
927
+
928
+
929
+ function fetchEventSource(source, fetchID, options) {
930
+ _fetchEventSource(source, function(events) {
931
+ if (fetchID == currentFetchID) {
932
+ if (events) {
933
+ for (var i=0; i<events.length; i++) {
934
+ events[i].source = source;
935
+ normalizeEvent(events[i]);
936
+ }
937
+ cache = cache.concat(events);
938
+ }
939
+ pendingSourceCnt--;
940
+ if (!pendingSourceCnt) {
941
+ reportEvents(cache);
942
+ }
943
+ }
944
+ }, options);
945
+ }
946
+
947
+
948
+ function _fetchEventSource(source, callback) {
949
+ var i;
950
+ var fetchers = fc.sourceFetchers;
951
+ var res;
952
+ for (i=0; i<fetchers.length; i++) {
953
+ res = fetchers[i](source, rangeStart, rangeEnd, callback);
954
+ if (res === true) {
955
+ // the fetcher is in charge. made its own async request
956
+ return;
957
+ }
958
+ else if (typeof res == 'object') {
959
+ // the fetcher returned a new source. process it
960
+ _fetchEventSource(res, callback, options);
961
+ return;
962
+ }
963
+ }
964
+ var events = source.events;
965
+ if (events) {
966
+ if ($.isFunction(events)) {
967
+ pushLoading();
968
+ events(cloneDate(rangeStart), cloneDate(rangeEnd), function(events) {
969
+ callback(events);
970
+ popLoading();
971
+ }, options);
972
+ }
973
+ else if ($.isArray(events)) {
974
+ callback(events);
975
+ }
976
+ else {
977
+ callback();
978
+ }
979
+ }else{
980
+ var url = source.url;
981
+ if (url) {
982
+ var success = source.success;
983
+ var error = source.error;
984
+ var complete = source.complete;
985
+ var data = $.extend({}, source.data || {});
986
+ var startParam = firstDefined(source.startParam, options.startParam);
987
+ var endParam = firstDefined(source.endParam, options.endParam);
988
+ if (startParam) {
989
+ data[startParam] = Math.round(+rangeStart / 1000);
990
+ }
991
+ if (endParam) {
992
+ data[endParam] = Math.round(+rangeEnd / 1000);
993
+ }
994
+ pushLoading();
995
+ $.ajax($.extend({}, ajaxDefaults, source, {
996
+ data: data,
997
+ success: function(events) {
998
+ events = events || [];
999
+ var res = applyAll(success, this, arguments);
1000
+ if ($.isArray(res)) {
1001
+ events = res;
1002
+ }
1003
+ callback(events);
1004
+ },
1005
+ error: function() {
1006
+ applyAll(error, this, arguments);
1007
+ callback();
1008
+ },
1009
+ complete: function() {
1010
+ applyAll(complete, this, arguments);
1011
+ popLoading();
1012
+ }
1013
+ }));
1014
+ }else{
1015
+ callback();
1016
+ }
1017
+ }
1018
+ }
1019
+
1020
+
1021
+
1022
+ /* Sources
1023
+ -----------------------------------------------------------------------------*/
1024
+
1025
+
1026
+ function addEventSource(source) {
1027
+ source = _addEventSource(source);
1028
+ if (source) {
1029
+ pendingSourceCnt++;
1030
+ fetchEventSource(source, currentFetchID); // will eventually call reportEvents
1031
+ }
1032
+ }
1033
+
1034
+
1035
+ function _addEventSource(source) {
1036
+ if ($.isFunction(source) || $.isArray(source)) {
1037
+ source = { events: source };
1038
+ }
1039
+ else if (typeof source == 'string') {
1040
+ source = { url: source };
1041
+ }
1042
+ if (typeof source == 'object') {
1043
+ normalizeSource(source);
1044
+ sources.push(source);
1045
+ return source;
1046
+ }
1047
+ }
1048
+
1049
+
1050
+ function removeEventSource(source) {
1051
+ sources = $.grep(sources, function(src) {
1052
+ return !isSourcesEqual(src, source);
1053
+ });
1054
+ // remove all client events from that source
1055
+ cache = $.grep(cache, function(e) {
1056
+ return !isSourcesEqual(e.source, source);
1057
+ });
1058
+ reportEvents(cache);
1059
+ }
1060
+
1061
+
1062
+
1063
+ /* Manipulation
1064
+ -----------------------------------------------------------------------------*/
1065
+
1066
+
1067
+ function updateEvent(event) { // update an existing event
1068
+ var i, len = cache.length, e,
1069
+ defaultEventEnd = getView().defaultEventEnd, // getView???
1070
+ startDelta = event.start - event._start,
1071
+ endDelta = event.end ?
1072
+ (event.end - (event._end || defaultEventEnd(event))) // event._end would be null if event.end
1073
+ : 0; // was null and event was just resized
1074
+ for (i=0; i<len; i++) {
1075
+ e = cache[i];
1076
+ if (e._id == event._id && e != event) {
1077
+ e.start = new Date(+e.start + startDelta);
1078
+ if (event.end) {
1079
+ if (e.end) {
1080
+ e.end = new Date(+e.end + endDelta);
1081
+ }else{
1082
+ e.end = new Date(+defaultEventEnd(e) + endDelta);
1083
+ }
1084
+ }else{
1085
+ e.end = null;
1086
+ }
1087
+ e.title = event.title;
1088
+ e.url = event.url;
1089
+ e.allDay = event.allDay;
1090
+ e.className = event.className;
1091
+ e.editable = event.editable;
1092
+ e.color = event.color;
1093
+ e.backgroudColor = event.backgroudColor;
1094
+ e.borderColor = event.borderColor;
1095
+ e.textColor = event.textColor;
1096
+ normalizeEvent(e);
1097
+ }
1098
+ }
1099
+ normalizeEvent(event);
1100
+ reportEvents(cache);
1101
+ }
1102
+
1103
+
1104
+ function renderEvent(event, stick) {
1105
+ normalizeEvent(event);
1106
+ if (!event.source) {
1107
+ if (stick) {
1108
+ stickySource.events.push(event);
1109
+ event.source = stickySource;
1110
+ }
1111
+ cache.push(event);
1112
+ }
1113
+ reportEvents(cache);
1114
+ }
1115
+
1116
+
1117
+ function removeEvents(filter) {
1118
+ if (!filter) { // remove all
1119
+ cache = [];
1120
+ // clear all array sources
1121
+ for (var i=0; i<sources.length; i++) {
1122
+ if ($.isArray(sources[i].events)) {
1123
+ sources[i].events = [];
1124
+ }
1125
+ }
1126
+ }else{
1127
+ if (!$.isFunction(filter)) { // an event ID
1128
+ var id = filter + '';
1129
+ filter = function(e) {
1130
+ return e._id == id;
1131
+ };
1132
+ }
1133
+ cache = $.grep(cache, filter, true);
1134
+ // remove events from array sources
1135
+ for (var i=0; i<sources.length; i++) {
1136
+ if ($.isArray(sources[i].events)) {
1137
+ sources[i].events = $.grep(sources[i].events, filter, true);
1138
+ }
1139
+ }
1140
+ }
1141
+ reportEvents(cache);
1142
+ }
1143
+
1144
+
1145
+ function clientEvents(filter) {
1146
+ if ($.isFunction(filter)) {
1147
+ return $.grep(cache, filter);
1148
+ }
1149
+ else if (filter) { // an event ID
1150
+ filter += '';
1151
+ return $.grep(cache, function(e) {
1152
+ return e._id == filter;
1153
+ });
1154
+ }
1155
+ return cache; // else, return all
1156
+ }
1157
+
1158
+
1159
+
1160
+ /* Loading State
1161
+ -----------------------------------------------------------------------------*/
1162
+
1163
+
1164
+ function pushLoading() {
1165
+ if (!loadingLevel++) {
1166
+ trigger('loading', null, true);
1167
+ }
1168
+ }
1169
+
1170
+
1171
+ function popLoading() {
1172
+ if (!--loadingLevel) {
1173
+ trigger('loading', null, false);
1174
+ }
1175
+ }
1176
+
1177
+
1178
+
1179
+ /* Event Normalization
1180
+ -----------------------------------------------------------------------------*/
1181
+
1182
+
1183
+ function normalizeEvent(event) {
1184
+ var source = event.source || {};
1185
+ var ignoreTimezone = firstDefined(source.ignoreTimezone, options.ignoreTimezone);
1186
+ event._id = event._id || (event.id === undefined ? '_fc' + eventGUID++ : event.id + '');
1187
+ if (event.date) {
1188
+ if (!event.start) {
1189
+ event.start = event.date;
1190
+ }
1191
+ delete event.date;
1192
+ }
1193
+ event._start = cloneDate(event.start = parseDate(event.start, ignoreTimezone));
1194
+ event.end = parseDate(event.end, ignoreTimezone);
1195
+ if (event.end && event.end <= event.start) {
1196
+ event.end = null;
1197
+ }
1198
+ event._end = event.end ? cloneDate(event.end) : null;
1199
+ if (event.allDay === undefined) {
1200
+ event.allDay = firstDefined(source.allDayDefault, options.allDayDefault);
1201
+ }
1202
+ if (event.className) {
1203
+ if (typeof event.className == 'string') {
1204
+ event.className = event.className.split(/\s+/);
1205
+ }
1206
+ }else{
1207
+ event.className = [];
1208
+ }
1209
+ // TODO: if there is no start date, return false to indicate an invalid event
1210
+ }
1211
+
1212
+
1213
+
1214
+ /* Utils
1215
+ ------------------------------------------------------------------------------*/
1216
+
1217
+
1218
+ function normalizeSource(source) {
1219
+ if (source.className) {
1220
+ // TODO: repeat code, same code for event classNames
1221
+ if (typeof source.className == 'string') {
1222
+ source.className = source.className.split(/\s+/);
1223
+ }
1224
+ }else{
1225
+ source.className = [];
1226
+ }
1227
+ var normalizers = fc.sourceNormalizers;
1228
+ for (var i=0; i<normalizers.length; i++) {
1229
+ normalizers[i](source);
1230
+ }
1231
+ }
1232
+
1233
+
1234
+ function isSourcesEqual(source1, source2) {
1235
+ return source1 && source2 && getSourcePrimitive(source1) == getSourcePrimitive(source2);
1236
+ }
1237
+
1238
+
1239
+ function getSourcePrimitive(source) {
1240
+ return ((typeof source == 'object') ? (source.events || source.url) : '') || source;
1241
+ }
1242
+
1243
+
1244
+ }
1245
+
1246
+
1247
+ fc.addDays = addDays;
1248
+ fc.cloneDate = cloneDate;
1249
+ fc.parseDate = parseDate;
1250
+ fc.parseISO8601 = parseISO8601;
1251
+ fc.parseTime = parseTime;
1252
+ fc.formatDate = formatDate;
1253
+ fc.formatDates = formatDates;
1254
+
1255
+
1256
+
1257
+ /* Date Math
1258
+ -----------------------------------------------------------------------------*/
1259
+
1260
+ var dayIDs = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'],
1261
+ DAY_MS = 86400000,
1262
+ HOUR_MS = 3600000,
1263
+ MINUTE_MS = 60000;
1264
+
1265
+
1266
+ function addYears(d, n, keepTime) {
1267
+ d.setFullYear(d.getFullYear() + n);
1268
+ if (!keepTime) {
1269
+ clearTime(d);
1270
+ }
1271
+ return d;
1272
+ }
1273
+
1274
+
1275
+ function addMonths(d, n, keepTime) { // prevents day overflow/underflow
1276
+ if (+d) { // prevent infinite looping on invalid dates
1277
+ var m = d.getMonth() + n,
1278
+ check = cloneDate(d);
1279
+ check.setDate(1);
1280
+ check.setMonth(m);
1281
+ d.setMonth(m);
1282
+ if (!keepTime) {
1283
+ clearTime(d);
1284
+ }
1285
+ while (d.getMonth() != check.getMonth()) {
1286
+ d.setDate(d.getDate() + (d < check ? 1 : -1));
1287
+ }
1288
+ }
1289
+ return d;
1290
+ }
1291
+
1292
+
1293
+ function addDays(d, n, keepTime) { // deals with daylight savings
1294
+ if (+d) {
1295
+ var dd = d.getDate() + n,
1296
+ check = cloneDate(d);
1297
+ check.setHours(9); // set to middle of day
1298
+ check.setDate(dd);
1299
+ d.setDate(dd);
1300
+ if (!keepTime) {
1301
+ clearTime(d);
1302
+ }
1303
+ fixDate(d, check);
1304
+ }
1305
+ return d;
1306
+ }
1307
+
1308
+
1309
+ function fixDate(d, check) { // force d to be on check's YMD, for daylight savings purposes
1310
+ if (+d) { // prevent infinite looping on invalid dates
1311
+ while (d.getDate() != check.getDate()) {
1312
+ d.setTime(+d + (d < check ? 1 : -1) * HOUR_MS);
1313
+ }
1314
+ }
1315
+ }
1316
+
1317
+
1318
+ function addMinutes(d, n) {
1319
+ d.setMinutes(d.getMinutes() + n);
1320
+ return d;
1321
+ }
1322
+
1323
+
1324
+ function clearTime(d) {
1325
+ d.setHours(0);
1326
+ d.setMinutes(0);
1327
+ d.setSeconds(0);
1328
+ d.setMilliseconds(0);
1329
+ return d;
1330
+ }
1331
+
1332
+
1333
+ function cloneDate(d, dontKeepTime) {
1334
+ if (dontKeepTime) {
1335
+ return clearTime(new Date(+d));
1336
+ }
1337
+ return new Date(+d);
1338
+ }
1339
+
1340
+
1341
+ function zeroDate() { // returns a Date with time 00:00:00 and dateOfMonth=1
1342
+ var i=0, d;
1343
+ do {
1344
+ d = new Date(1970, i++, 1);
1345
+ } while (d.getHours()); // != 0
1346
+ return d;
1347
+ }
1348
+
1349
+
1350
+ function skipWeekend(date, inc, excl) {
1351
+ inc = inc || 1;
1352
+ while (!date.getDay() || (excl && date.getDay()==1 || !excl && date.getDay()==6)) {
1353
+ addDays(date, inc);
1354
+ }
1355
+ return date;
1356
+ }
1357
+
1358
+
1359
+ function dayDiff(d1, d2) { // d1 - d2
1360
+ return Math.round((cloneDate(d1, true) - cloneDate(d2, true)) / DAY_MS);
1361
+ }
1362
+
1363
+
1364
+ function setYMD(date, y, m, d) {
1365
+ if (y !== undefined && y != date.getFullYear()) {
1366
+ date.setDate(1);
1367
+ date.setMonth(0);
1368
+ date.setFullYear(y);
1369
+ }
1370
+ if (m !== undefined && m != date.getMonth()) {
1371
+ date.setDate(1);
1372
+ date.setMonth(m);
1373
+ }
1374
+ if (d !== undefined) {
1375
+ date.setDate(d);
1376
+ }
1377
+ }
1378
+
1379
+
1380
+
1381
+ /* Date Parsing
1382
+ -----------------------------------------------------------------------------*/
1383
+
1384
+
1385
+ function parseDate(s, ignoreTimezone) { // ignoreTimezone defaults to true
1386
+ if (typeof s == 'object') { // already a Date object
1387
+ return s;
1388
+ }
1389
+ if (typeof s == 'number') { // a UNIX timestamp
1390
+ return new Date(s * 1000);
1391
+ }
1392
+ if (typeof s == 'string') {
1393
+ if (s.match(/^\d+(\.\d+)?$/)) { // a UNIX timestamp
1394
+ return new Date(parseFloat(s) * 1000);
1395
+ }
1396
+ if (ignoreTimezone === undefined) {
1397
+ ignoreTimezone = true;
1398
+ }
1399
+ return parseISO8601(s, ignoreTimezone) || (s ? new Date(s) : null);
1400
+ }
1401
+ // TODO: never return invalid dates (like from new Date(<string>)), return null instead
1402
+ return null;
1403
+ }
1404
+
1405
+
1406
+ function parseISO8601(s, ignoreTimezone) { // ignoreTimezone defaults to false
1407
+ // derived from http://delete.me.uk/2005/03/iso8601.html
1408
+ // TODO: for a know glitch/feature, read tests/issue_206_parseDate_dst.html
1409
+ var m = s.match(/^([0-9]{4})(-([0-9]{2})(-([0-9]{2})([T ]([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2})(:?([0-9]{2}))?))?)?)?)?$/);
1410
+ if (!m) {
1411
+ return null;
1412
+ }
1413
+ var date = new Date(m[1], 0, 1);
1414
+ if (ignoreTimezone || !m[13]) {
1415
+ var check = new Date(m[1], 0, 1, 9, 0);
1416
+ if (m[3]) {
1417
+ date.setMonth(m[3] - 1);
1418
+ check.setMonth(m[3] - 1);
1419
+ }
1420
+ if (m[5]) {
1421
+ date.setDate(m[5]);
1422
+ check.setDate(m[5]);
1423
+ }
1424
+ fixDate(date, check);
1425
+ if (m[7]) {
1426
+ date.setHours(m[7]);
1427
+ }
1428
+ if (m[8]) {
1429
+ date.setMinutes(m[8]);
1430
+ }
1431
+ if (m[10]) {
1432
+ date.setSeconds(m[10]);
1433
+ }
1434
+ if (m[12]) {
1435
+ date.setMilliseconds(Number("0." + m[12]) * 1000);
1436
+ }
1437
+ fixDate(date, check);
1438
+ }else{
1439
+ date.setUTCFullYear(
1440
+ m[1],
1441
+ m[3] ? m[3] - 1 : 0,
1442
+ m[5] || 1
1443
+ );
1444
+ date.setUTCHours(
1445
+ m[7] || 0,
1446
+ m[8] || 0,
1447
+ m[10] || 0,
1448
+ m[12] ? Number("0." + m[12]) * 1000 : 0
1449
+ );
1450
+ if (m[14]) {
1451
+ var offset = Number(m[16]) * 60 + (m[18] ? Number(m[18]) : 0);
1452
+ offset *= m[15] == '-' ? 1 : -1;
1453
+ date = new Date(+date + (offset * 60 * 1000));
1454
+ }
1455
+ }
1456
+ return date;
1457
+ }
1458
+
1459
+
1460
+ function parseTime(s) { // returns minutes since start of day
1461
+ if (typeof s == 'number') { // an hour
1462
+ return s * 60;
1463
+ }
1464
+ if (typeof s == 'object') { // a Date object
1465
+ return s.getHours() * 60 + s.getMinutes();
1466
+ }
1467
+ var m = s.match(/(\d+)(?::(\d+))?\s*(\w+)?/);
1468
+ if (m) {
1469
+ var h = parseInt(m[1], 10);
1470
+ if (m[3]) {
1471
+ h %= 12;
1472
+ if (m[3].toLowerCase().charAt(0) == 'p') {
1473
+ h += 12;
1474
+ }
1475
+ }
1476
+ return h * 60 + (m[2] ? parseInt(m[2], 10) : 0);
1477
+ }
1478
+ }
1479
+
1480
+
1481
+
1482
+ /* Date Formatting
1483
+ -----------------------------------------------------------------------------*/
1484
+ // TODO: use same function formatDate(date, [date2], format, [options])
1485
+
1486
+
1487
+ function formatDate(date, format, options) {
1488
+ return formatDates(date, null, format, options);
1489
+ }
1490
+
1491
+
1492
+ function formatDates(date1, date2, format, options) {
1493
+ options = options || defaults;
1494
+ var date = date1,
1495
+ otherDate = date2,
1496
+ i, len = format.length, c,
1497
+ i2, formatter,
1498
+ res = '';
1499
+ for (i=0; i<len; i++) {
1500
+ c = format.charAt(i);
1501
+ if (c == "'") {
1502
+ for (i2=i+1; i2<len; i2++) {
1503
+ if (format.charAt(i2) == "'") {
1504
+ if (date) {
1505
+ if (i2 == i+1) {
1506
+ res += "'";
1507
+ }else{
1508
+ res += format.substring(i+1, i2);
1509
+ }
1510
+ i = i2;
1511
+ }
1512
+ break;
1513
+ }
1514
+ }
1515
+ }
1516
+ else if (c == '(') {
1517
+ for (i2=i+1; i2<len; i2++) {
1518
+ if (format.charAt(i2) == ')') {
1519
+ var subres = formatDate(date, format.substring(i+1, i2), options);
1520
+ if (parseInt(subres.replace(/\D/, ''), 10)) {
1521
+ res += subres;
1522
+ }
1523
+ i = i2;
1524
+ break;
1525
+ }
1526
+ }
1527
+ }
1528
+ else if (c == '[') {
1529
+ for (i2=i+1; i2<len; i2++) {
1530
+ if (format.charAt(i2) == ']') {
1531
+ var subformat = format.substring(i+1, i2);
1532
+ var subres = formatDate(date, subformat, options);
1533
+ if (subres != formatDate(otherDate, subformat, options)) {
1534
+ res += subres;
1535
+ }
1536
+ i = i2;
1537
+ break;
1538
+ }
1539
+ }
1540
+ }
1541
+ else if (c == '{') {
1542
+ date = date2;
1543
+ otherDate = date1;
1544
+ }
1545
+ else if (c == '}') {
1546
+ date = date1;
1547
+ otherDate = date2;
1548
+ }
1549
+ else {
1550
+ for (i2=len; i2>i; i2--) {
1551
+ if (formatter = dateFormatters[format.substring(i, i2)]) {
1552
+ if (date) {
1553
+ res += formatter(date, options);
1554
+ }
1555
+ i = i2 - 1;
1556
+ break;
1557
+ }
1558
+ }
1559
+ if (i2 == i) {
1560
+ if (date) {
1561
+ res += c;
1562
+ }
1563
+ }
1564
+ }
1565
+ }
1566
+ return res;
1567
+ };
1568
+
1569
+
1570
+ var dateFormatters = {
1571
+ s : function(d) { return d.getSeconds() },
1572
+ ss : function(d) { return zeroPad(d.getSeconds()) },
1573
+ m : function(d) { return d.getMinutes() },
1574
+ mm : function(d) { return zeroPad(d.getMinutes()) },
1575
+ h : function(d) { return d.getHours() % 12 || 12 },
1576
+ hh : function(d) { return zeroPad(d.getHours() % 12 || 12) },
1577
+ H : function(d) { return d.getHours() },
1578
+ HH : function(d) { return zeroPad(d.getHours()) },
1579
+ d : function(d) { return d.getDate() },
1580
+ dd : function(d) { return zeroPad(d.getDate()) },
1581
+ ddd : function(d,o) { return o.dayNamesShort[d.getDay()] },
1582
+ dddd: function(d,o) { return o.dayNames[d.getDay()] },
1583
+ M : function(d) { return d.getMonth() + 1 },
1584
+ MM : function(d) { return zeroPad(d.getMonth() + 1) },
1585
+ MMM : function(d,o) { return o.monthNamesShort[d.getMonth()] },
1586
+ MMMM: function(d,o) { return o.monthNames[d.getMonth()] },
1587
+ yy : function(d) { return (d.getFullYear()+'').substring(2) },
1588
+ yyyy: function(d) { return d.getFullYear() },
1589
+ t : function(d) { return d.getHours() < 12 ? 'a' : 'p' },
1590
+ tt : function(d) { return d.getHours() < 12 ? 'am' : 'pm' },
1591
+ T : function(d) { return d.getHours() < 12 ? 'A' : 'P' },
1592
+ TT : function(d) { return d.getHours() < 12 ? 'AM' : 'PM' },
1593
+ u : function(d) { return formatDate(d, "yyyy-MM-dd'T'HH:mm:ss'Z'") },
1594
+ S : function(d) {
1595
+ var date = d.getDate();
1596
+ if (date > 10 && date < 20) {
1597
+ return 'th';
1598
+ }
1599
+ return ['st', 'nd', 'rd'][date%10-1] || 'th';
1600
+ }
1601
+ };
1602
+
1603
+
1604
+
1605
+ fc.applyAll = applyAll;
1606
+
1607
+
1608
+ /* Event Date Math
1609
+ -----------------------------------------------------------------------------*/
1610
+
1611
+
1612
+ function exclEndDay(event) {
1613
+ if (event.end) {
1614
+ return _exclEndDay(event.end, event.allDay);
1615
+ }else{
1616
+ return addDays(cloneDate(event.start), 1);
1617
+ }
1618
+ }
1619
+
1620
+
1621
+ function _exclEndDay(end, allDay) {
1622
+ end = cloneDate(end);
1623
+ return allDay || end.getHours() || end.getMinutes() ? addDays(end, 1) : clearTime(end);
1624
+ }
1625
+
1626
+
1627
+ function segCmp(a, b) {
1628
+ return (b.msLength - a.msLength) * 100 + (a.event.start - b.event.start);
1629
+ }
1630
+
1631
+
1632
+ function segsCollide(seg1, seg2) {
1633
+ return seg1.end > seg2.start && seg1.start < seg2.end;
1634
+ }
1635
+
1636
+
1637
+
1638
+ /* Event Sorting
1639
+ -----------------------------------------------------------------------------*/
1640
+
1641
+
1642
+ // event rendering utilities
1643
+ function sliceSegs(events, visEventEnds, start, end) {
1644
+ var segs = [],
1645
+ i, len=events.length, event,
1646
+ eventStart, eventEnd,
1647
+ segStart, segEnd,
1648
+ isStart, isEnd;
1649
+ for (i=0; i<len; i++) {
1650
+ event = events[i];
1651
+ eventStart = event.start;
1652
+ eventEnd = visEventEnds[i];
1653
+ if (eventEnd > start && eventStart < end) {
1654
+ if (eventStart < start) {
1655
+ segStart = cloneDate(start);
1656
+ isStart = false;
1657
+ }else{
1658
+ segStart = eventStart;
1659
+ isStart = true;
1660
+ }
1661
+ if (eventEnd > end) {
1662
+ segEnd = cloneDate(end);
1663
+ isEnd = false;
1664
+ }else{
1665
+ segEnd = eventEnd;
1666
+ isEnd = true;
1667
+ }
1668
+ segs.push({
1669
+ event: event,
1670
+ start: segStart,
1671
+ end: segEnd,
1672
+ isStart: isStart,
1673
+ isEnd: isEnd,
1674
+ msLength: segEnd - segStart
1675
+ });
1676
+ }
1677
+ }
1678
+ return segs.sort(segCmp);
1679
+ }
1680
+
1681
+
1682
+ // event rendering calculation utilities
1683
+ function stackSegs(segs) {
1684
+ var levels = [],
1685
+ i, len = segs.length, seg,
1686
+ j, collide, k;
1687
+ for (i=0; i<len; i++) {
1688
+ seg = segs[i];
1689
+ j = 0; // the level index where seg should belong
1690
+ while (true) {
1691
+ collide = false;
1692
+ if (levels[j]) {
1693
+ for (k=0; k<levels[j].length; k++) {
1694
+ if (segsCollide(levels[j][k], seg)) {
1695
+ collide = true;
1696
+ break;
1697
+ }
1698
+ }
1699
+ }
1700
+ if (collide) {
1701
+ j++;
1702
+ }else{
1703
+ break;
1704
+ }
1705
+ }
1706
+ if (levels[j]) {
1707
+ levels[j].push(seg);
1708
+ }else{
1709
+ levels[j] = [seg];
1710
+ }
1711
+ }
1712
+ return levels;
1713
+ }
1714
+
1715
+
1716
+
1717
+ /* Event Element Binding
1718
+ -----------------------------------------------------------------------------*/
1719
+
1720
+
1721
+ function lazySegBind(container, segs, bindHandlers) {
1722
+ container.unbind('mouseover').mouseover(function(ev) {
1723
+ var parent=ev.target, e,
1724
+ i, seg;
1725
+ while (parent != this) {
1726
+ e = parent;
1727
+ parent = parent.parentNode;
1728
+ }
1729
+ if ((i = e._fci) !== undefined) {
1730
+ e._fci = undefined;
1731
+ seg = segs[i];
1732
+ bindHandlers(seg.event, seg.element, seg);
1733
+ $(ev.target).trigger(ev);
1734
+ }
1735
+ ev.stopPropagation();
1736
+ });
1737
+ }
1738
+
1739
+
1740
+
1741
+ /* Element Dimensions
1742
+ -----------------------------------------------------------------------------*/
1743
+
1744
+
1745
+ function setOuterWidth(element, width, includeMargins) {
1746
+ for (var i=0, e; i<element.length; i++) {
1747
+ e = $(element[i]);
1748
+ e.width(Math.max(0, width - hsides(e, includeMargins)));
1749
+ }
1750
+ }
1751
+
1752
+
1753
+ function setOuterHeight(element, height, includeMargins) {
1754
+ for (var i=0, e; i<element.length; i++) {
1755
+ e = $(element[i]);
1756
+ e.height(Math.max(0, height - vsides(e, includeMargins)));
1757
+ }
1758
+ }
1759
+
1760
+
1761
+ function hsides(element, includeMargins) {
1762
+ return hpadding(element) + hborders(element) + (includeMargins ? hmargins(element) : 0);
1763
+ }
1764
+
1765
+
1766
+ function hpadding(element) {
1767
+ return (parseFloat($.css(element[0], 'paddingLeft', true)) || 0) +
1768
+ (parseFloat($.css(element[0], 'paddingRight', true)) || 0);
1769
+ }
1770
+
1771
+
1772
+ function hmargins(element) {
1773
+ return (parseFloat($.css(element[0], 'marginLeft', true)) || 0) +
1774
+ (parseFloat($.css(element[0], 'marginRight', true)) || 0);
1775
+ }
1776
+
1777
+
1778
+ function hborders(element) {
1779
+ return (parseFloat($.css(element[0], 'borderLeftWidth', true)) || 0) +
1780
+ (parseFloat($.css(element[0], 'borderRightWidth', true)) || 0);
1781
+ }
1782
+
1783
+
1784
+ function vsides(element, includeMargins) {
1785
+ return vpadding(element) + vborders(element) + (includeMargins ? vmargins(element) : 0);
1786
+ }
1787
+
1788
+
1789
+ function vpadding(element) {
1790
+ return (parseFloat($.css(element[0], 'paddingTop', true)) || 0) +
1791
+ (parseFloat($.css(element[0], 'paddingBottom', true)) || 0);
1792
+ }
1793
+
1794
+
1795
+ function vmargins(element) {
1796
+ return (parseFloat($.css(element[0], 'marginTop', true)) || 0) +
1797
+ (parseFloat($.css(element[0], 'marginBottom', true)) || 0);
1798
+ }
1799
+
1800
+
1801
+ function vborders(element) {
1802
+ return (parseFloat($.css(element[0], 'borderTopWidth', true)) || 0) +
1803
+ (parseFloat($.css(element[0], 'borderBottomWidth', true)) || 0);
1804
+ }
1805
+
1806
+
1807
+ function setMinHeight(element, height) {
1808
+ height = (typeof height == 'number' ? height + 'px' : height);
1809
+ element.each(function(i, _element) {
1810
+ _element.style.cssText += ';min-height:' + height + ';_height:' + height;
1811
+ // why can't we just use .css() ? i forget
1812
+ });
1813
+ }
1814
+
1815
+
1816
+
1817
+ /* Misc Utils
1818
+ -----------------------------------------------------------------------------*/
1819
+
1820
+
1821
+ //TODO: arraySlice
1822
+ //TODO: isFunction, grep ?
1823
+
1824
+
1825
+ function noop() { }
1826
+
1827
+
1828
+ function cmp(a, b) {
1829
+ return a - b;
1830
+ }
1831
+
1832
+
1833
+ function arrayMax(a) {
1834
+ return Math.max.apply(Math, a);
1835
+ }
1836
+
1837
+
1838
+ function zeroPad(n) {
1839
+ return (n < 10 ? '0' : '') + n;
1840
+ }
1841
+
1842
+
1843
+ function smartProperty(obj, name) { // get a camel-cased/namespaced property of an object
1844
+ if (obj[name] !== undefined) {
1845
+ return obj[name];
1846
+ }
1847
+ var parts = name.split(/(?=[A-Z])/),
1848
+ i=parts.length-1, res;
1849
+ for (; i>=0; i--) {
1850
+ res = obj[parts[i].toLowerCase()];
1851
+ if (res !== undefined) {
1852
+ return res;
1853
+ }
1854
+ }
1855
+ return obj[''];
1856
+ }
1857
+
1858
+
1859
+ function htmlEscape(s) {
1860
+ return s.replace(/&/g, '&amp;')
1861
+ .replace(/</g, '&lt;')
1862
+ .replace(/>/g, '&gt;')
1863
+ .replace(/'/g, '&#039;')
1864
+ .replace(/"/g, '&quot;')
1865
+ .replace(/\n/g, '<br />');
1866
+ }
1867
+
1868
+
1869
+ function cssKey(_element) {
1870
+ return _element.id + '/' + _element.className + '/' + _element.style.cssText.replace(/(^|;)\s*(top|left|width|height)\s*:[^;]*/ig, '');
1871
+ }
1872
+
1873
+
1874
+ function disableTextSelection(element) {
1875
+ element
1876
+ .attr('unselectable', 'on')
1877
+ .css('MozUserSelect', 'none')
1878
+ .bind('selectstart.ui', function() { return false; });
1879
+ }
1880
+
1881
+
1882
+ /*
1883
+ function enableTextSelection(element) {
1884
+ element
1885
+ .attr('unselectable', 'off')
1886
+ .css('MozUserSelect', '')
1887
+ .unbind('selectstart.ui');
1888
+ }
1889
+ */
1890
+
1891
+
1892
+ function markFirstLast(e) {
1893
+ e.children()
1894
+ .removeClass('fc-first fc-last')
1895
+ .filter(':first-child')
1896
+ .addClass('fc-first')
1897
+ .end()
1898
+ .filter(':last-child')
1899
+ .addClass('fc-last');
1900
+ }
1901
+
1902
+
1903
+ function setDayID(cell, date) {
1904
+ cell.each(function(i, _cell) {
1905
+ _cell.className = _cell.className.replace(/^fc-\w*/, 'fc-' + dayIDs[date.getDay()]);
1906
+ // TODO: make a way that doesn't rely on order of classes
1907
+ });
1908
+ }
1909
+
1910
+
1911
+ function getSkinCss(event, opt) {
1912
+ var source = event.source || {};
1913
+ var eventColor = event.color;
1914
+ var sourceColor = source.color;
1915
+ var optionColor = opt('eventColor');
1916
+ var backgroundColor =
1917
+ event.backgroundColor ||
1918
+ eventColor ||
1919
+ source.backgroundColor ||
1920
+ sourceColor ||
1921
+ opt('eventBackgroundColor') ||
1922
+ optionColor;
1923
+ var borderColor =
1924
+ event.borderColor ||
1925
+ eventColor ||
1926
+ source.borderColor ||
1927
+ sourceColor ||
1928
+ opt('eventBorderColor') ||
1929
+ optionColor;
1930
+ var textColor =
1931
+ event.textColor ||
1932
+ source.textColor ||
1933
+ opt('eventTextColor');
1934
+ var statements = [];
1935
+ if (backgroundColor) {
1936
+ statements.push('background-color:' + backgroundColor);
1937
+ }
1938
+ if (borderColor) {
1939
+ statements.push('border-color:' + borderColor);
1940
+ }
1941
+ if (textColor) {
1942
+ statements.push('color:' + textColor);
1943
+ }
1944
+ return statements.join(';');
1945
+ }
1946
+
1947
+
1948
+ function applyAll(functions, thisObj, args) {
1949
+ if ($.isFunction(functions)) {
1950
+ functions = [ functions ];
1951
+ }
1952
+ if (functions) {
1953
+ var i;
1954
+ var ret;
1955
+ for (i=0; i<functions.length; i++) {
1956
+ ret = functions[i].apply(thisObj, args) || ret;
1957
+ }
1958
+ return ret;
1959
+ }
1960
+ }
1961
+
1962
+
1963
+ function firstDefined() {
1964
+ for (var i=0; i<arguments.length; i++) {
1965
+ if (arguments[i] !== undefined) {
1966
+ return arguments[i];
1967
+ }
1968
+ }
1969
+ }
1970
+
1971
+
1972
+ fcViews.month = MonthView;
1973
+
1974
+ function MonthView(element, calendar) {
1975
+ var t = this;
1976
+
1977
+
1978
+ // exports
1979
+ t.render = render;
1980
+
1981
+
1982
+ // imports
1983
+ BasicView.call(t, element, calendar, 'month');
1984
+ var opt = t.opt;
1985
+ var renderBasic = t.renderBasic;
1986
+ var formatDate = calendar.formatDate;
1987
+
1988
+
1989
+
1990
+ function render(date, delta) {
1991
+ if (delta) {
1992
+ addMonths(date, delta);
1993
+ date.setDate(1);
1994
+ }
1995
+ var start = cloneDate(date, true);
1996
+ start.setDate(1);
1997
+ var end = addMonths(cloneDate(start), 1);
1998
+ var visStart = cloneDate(start);
1999
+ var visEnd = cloneDate(end);
2000
+ var firstDay = opt('firstDay');
2001
+ var nwe = opt('weekends') ? 0 : 1;
2002
+ if (nwe) {
2003
+ skipWeekend(visStart);
2004
+ skipWeekend(visEnd, -1, true);
2005
+ }
2006
+ addDays(visStart, -((visStart.getDay() - Math.max(firstDay, nwe) + 7) % 7));
2007
+ addDays(visEnd, (7 - visEnd.getDay() + Math.max(firstDay, nwe)) % 7);
2008
+ var rowCnt = Math.round((visEnd - visStart) / (DAY_MS * 7));
2009
+ if (opt('weekMode') == 'fixed') {
2010
+ addDays(visEnd, (6 - rowCnt) * 7);
2011
+ rowCnt = 6;
2012
+ }
2013
+ t.title = formatDate(start, opt('titleFormat'));
2014
+ t.start = start;
2015
+ t.end = end;
2016
+ t.visStart = visStart;
2017
+ t.visEnd = visEnd;
2018
+ renderBasic(6, rowCnt, nwe ? 5 : 7, true);
2019
+ }
2020
+
2021
+
2022
+ }
2023
+
2024
+ fcViews.basicWeek = BasicWeekView;
2025
+
2026
+ function BasicWeekView(element, calendar) {
2027
+ var t = this;
2028
+
2029
+
2030
+ // exports
2031
+ t.render = render;
2032
+
2033
+
2034
+ // imports
2035
+ BasicView.call(t, element, calendar, 'basicWeek');
2036
+ var opt = t.opt;
2037
+ var renderBasic = t.renderBasic;
2038
+ var formatDates = calendar.formatDates;
2039
+
2040
+
2041
+
2042
+ function render(date, delta) {
2043
+ if (delta) {
2044
+ addDays(date, delta * 7);
2045
+ }
2046
+ var start = addDays(cloneDate(date), -((date.getDay() - opt('firstDay') + 7) % 7));
2047
+ var end = addDays(cloneDate(start), 7);
2048
+ var visStart = cloneDate(start);
2049
+ var visEnd = cloneDate(end);
2050
+ var weekends = opt('weekends');
2051
+ if (!weekends) {
2052
+ skipWeekend(visStart);
2053
+ skipWeekend(visEnd, -1, true);
2054
+ }
2055
+ t.title = formatDates(
2056
+ visStart,
2057
+ addDays(cloneDate(visEnd), -1),
2058
+ opt('titleFormat')
2059
+ );
2060
+ t.start = start;
2061
+ t.end = end;
2062
+ t.visStart = visStart;
2063
+ t.visEnd = visEnd;
2064
+ renderBasic(1, 1, weekends ? 7 : 5, false);
2065
+ }
2066
+
2067
+
2068
+ }
2069
+
2070
+ fcViews.basicDay = BasicDayView;
2071
+
2072
+ //TODO: when calendar's date starts out on a weekend, shouldn't happen
2073
+
2074
+
2075
+ function BasicDayView(element, calendar) {
2076
+ var t = this;
2077
+
2078
+
2079
+ // exports
2080
+ t.render = render;
2081
+
2082
+
2083
+ // imports
2084
+ BasicView.call(t, element, calendar, 'basicDay');
2085
+ var opt = t.opt;
2086
+ var renderBasic = t.renderBasic;
2087
+ var formatDate = calendar.formatDate;
2088
+
2089
+
2090
+
2091
+ function render(date, delta) {
2092
+ if (delta) {
2093
+ addDays(date, delta);
2094
+ if (!opt('weekends')) {
2095
+ skipWeekend(date, delta < 0 ? -1 : 1);
2096
+ }
2097
+ }
2098
+ t.title = formatDate(date, opt('titleFormat'));
2099
+ t.start = t.visStart = cloneDate(date, true);
2100
+ t.end = t.visEnd = addDays(cloneDate(t.start), 1);
2101
+ renderBasic(1, 1, 1, false);
2102
+ }
2103
+
2104
+
2105
+ }
2106
+
2107
+ setDefaults({
2108
+ weekMode: 'fixed'
2109
+ });
2110
+
2111
+
2112
+ function BasicView(element, calendar, viewName) {
2113
+ var t = this;
2114
+
2115
+
2116
+ // exports
2117
+ t.renderBasic = renderBasic;
2118
+ t.setHeight = setHeight;
2119
+ t.setWidth = setWidth;
2120
+ t.renderDayOverlay = renderDayOverlay;
2121
+ t.defaultSelectionEnd = defaultSelectionEnd;
2122
+ t.renderSelection = renderSelection;
2123
+ t.clearSelection = clearSelection;
2124
+ t.reportDayClick = reportDayClick; // for selection (kinda hacky)
2125
+ t.dragStart = dragStart;
2126
+ t.dragStop = dragStop;
2127
+ t.defaultEventEnd = defaultEventEnd;
2128
+ t.getHoverListener = function() { return hoverListener };
2129
+ t.colContentLeft = colContentLeft;
2130
+ t.colContentRight = colContentRight;
2131
+ t.dayOfWeekCol = dayOfWeekCol;
2132
+ t.dateCell = dateCell;
2133
+ t.cellDate = cellDate;
2134
+ t.cellIsAllDay = function() { return true };
2135
+ t.allDayRow = allDayRow;
2136
+ t.allDayBounds = allDayBounds;
2137
+ t.getRowCnt = function() { return rowCnt };
2138
+ t.getColCnt = function() { return colCnt };
2139
+ t.getColWidth = function() { return colWidth };
2140
+ t.getDaySegmentContainer = function() { return daySegmentContainer };
2141
+
2142
+
2143
+ // imports
2144
+ View.call(t, element, calendar, viewName);
2145
+ OverlayManager.call(t);
2146
+ SelectionManager.call(t);
2147
+ BasicEventRenderer.call(t);
2148
+ var opt = t.opt;
2149
+ var trigger = t.trigger;
2150
+ var clearEvents = t.clearEvents;
2151
+ var renderOverlay = t.renderOverlay;
2152
+ var clearOverlays = t.clearOverlays;
2153
+ var daySelectionMousedown = t.daySelectionMousedown;
2154
+ var formatDate = calendar.formatDate;
2155
+
2156
+
2157
+ // locals
2158
+
2159
+ var head;
2160
+ var headCells;
2161
+ var body;
2162
+ var bodyRows;
2163
+ var bodyCells;
2164
+ var bodyFirstCells;
2165
+ var bodyCellTopInners;
2166
+ var daySegmentContainer;
2167
+
2168
+ var viewWidth;
2169
+ var viewHeight;
2170
+ var colWidth;
2171
+
2172
+ var rowCnt, colCnt;
2173
+ var coordinateGrid;
2174
+ var hoverListener;
2175
+ var colContentPositions;
2176
+
2177
+ var rtl, dis, dit;
2178
+ var firstDay;
2179
+ var nwe;
2180
+ var tm;
2181
+ var colFormat;
2182
+
2183
+
2184
+
2185
+ /* Rendering
2186
+ ------------------------------------------------------------*/
2187
+
2188
+
2189
+ disableTextSelection(element.addClass('fc-grid'));
2190
+
2191
+
2192
+ function renderBasic(maxr, r, c, showNumbers) {
2193
+ rowCnt = r;
2194
+ colCnt = c;
2195
+ updateOptions();
2196
+ var firstTime = !body;
2197
+ if (firstTime) {
2198
+ buildSkeleton(maxr, showNumbers);
2199
+ }else{
2200
+ clearEvents();
2201
+ }
2202
+ updateCells(firstTime);
2203
+ }
2204
+
2205
+
2206
+
2207
+ function updateOptions() {
2208
+ rtl = opt('isRTL');
2209
+ if (rtl) {
2210
+ dis = -1;
2211
+ dit = colCnt - 1;
2212
+ }else{
2213
+ dis = 1;
2214
+ dit = 0;
2215
+ }
2216
+ firstDay = opt('firstDay');
2217
+ nwe = opt('weekends') ? 0 : 1;
2218
+ tm = opt('theme') ? 'ui' : 'fc';
2219
+ colFormat = opt('columnFormat');
2220
+ }
2221
+
2222
+
2223
+
2224
+ function buildSkeleton(maxRowCnt, showNumbers) {
2225
+ var s;
2226
+ var headerClass = tm + "-widget-header";
2227
+ var contentClass = tm + "-widget-content";
2228
+ var i, j;
2229
+ var table;
2230
+
2231
+ s =
2232
+ "<table class='fc-border-separate' style='width:100%' cellspacing='0'>" +
2233
+ "<thead>" +
2234
+ "<tr>";
2235
+ for (i=0; i<colCnt; i++) {
2236
+ s +=
2237
+ "<th class='fc- " + headerClass + "'/>"; // need fc- for setDayID
2238
+ }
2239
+ s +=
2240
+ "</tr>" +
2241
+ "</thead>" +
2242
+ "<tbody>";
2243
+ for (i=0; i<maxRowCnt; i++) {
2244
+ s +=
2245
+ "<tr class='fc-week" + i + "'>";
2246
+ for (j=0; j<colCnt; j++) {
2247
+ s +=
2248
+ "<td class='fc- " + contentClass + " fc-day" + (i*colCnt+j) + "'>" + // need fc- for setDayID
2249
+ "<div>" +
2250
+ (showNumbers ?
2251
+ "<div class='fc-day-number'/>" :
2252
+ ''
2253
+ ) +
2254
+ "<div class='fc-day-content'>" +
2255
+ "<div style='position:relative'>&nbsp;</div>" +
2256
+ "</div>" +
2257
+ "</div>" +
2258
+ "</td>";
2259
+ }
2260
+ s +=
2261
+ "</tr>";
2262
+ }
2263
+ s +=
2264
+ "</tbody>" +
2265
+ "</table>";
2266
+ table = $(s).appendTo(element);
2267
+
2268
+ head = table.find('thead');
2269
+ headCells = head.find('th');
2270
+ body = table.find('tbody');
2271
+ bodyRows = body.find('tr');
2272
+ bodyCells = body.find('td');
2273
+ bodyFirstCells = bodyCells.filter(':first-child');
2274
+ bodyCellTopInners = bodyRows.eq(0).find('div.fc-day-content div');
2275
+
2276
+ markFirstLast(head.add(head.find('tr'))); // marks first+last tr/th's
2277
+ markFirstLast(bodyRows); // marks first+last td's
2278
+ bodyRows.eq(0).addClass('fc-first'); // fc-last is done in updateCells
2279
+
2280
+ dayBind(bodyCells);
2281
+
2282
+ daySegmentContainer =
2283
+ $("<div style='position:absolute;z-index:8;top:0;left:0'/>")
2284
+ .appendTo(element);
2285
+ }
2286
+
2287
+
2288
+
2289
+ function updateCells(firstTime) {
2290
+ var dowDirty = firstTime || rowCnt == 1; // could the cells' day-of-weeks need updating?
2291
+ var month = t.start.getMonth();
2292
+ var today = clearTime(new Date());
2293
+ var cell;
2294
+ var date;
2295
+ var row;
2296
+
2297
+ if (dowDirty) {
2298
+ headCells.each(function(i, _cell) {
2299
+ cell = $(_cell);
2300
+ date = indexDate(i);
2301
+ cell.html(formatDate(date, colFormat));
2302
+ setDayID(cell, date);
2303
+ });
2304
+ }
2305
+
2306
+ bodyCells.each(function(i, _cell) {
2307
+ cell = $(_cell);
2308
+ date = indexDate(i);
2309
+ if (date.getMonth() == month) {
2310
+ cell.removeClass('fc-other-month');
2311
+ }else{
2312
+ cell.addClass('fc-other-month');
2313
+ }
2314
+ if (+date == +today) {
2315
+ cell.addClass(tm + '-state-highlight fc-today');
2316
+ }else{
2317
+ cell.removeClass(tm + '-state-highlight fc-today');
2318
+ }
2319
+ cell.find('div.fc-day-number').text(date.getDate());
2320
+ if (dowDirty) {
2321
+ setDayID(cell, date);
2322
+ }
2323
+ });
2324
+
2325
+ bodyRows.each(function(i, _row) {
2326
+ row = $(_row);
2327
+ if (i < rowCnt) {
2328
+ row.show();
2329
+ if (i == rowCnt-1) {
2330
+ row.addClass('fc-last');
2331
+ }else{
2332
+ row.removeClass('fc-last');
2333
+ }
2334
+ }else{
2335
+ row.hide();
2336
+ }
2337
+ });
2338
+ }
2339
+
2340
+
2341
+
2342
+ function setHeight(height) {
2343
+ viewHeight = height;
2344
+
2345
+ var bodyHeight = viewHeight - head.height();
2346
+ var rowHeight;
2347
+ var rowHeightLast;
2348
+ var cell;
2349
+
2350
+ if (opt('weekMode') == 'variable') {
2351
+ rowHeight = rowHeightLast = Math.floor(bodyHeight / (rowCnt==1 ? 2 : 6));
2352
+ }else{
2353
+ rowHeight = Math.floor(bodyHeight / rowCnt);
2354
+ rowHeightLast = bodyHeight - rowHeight * (rowCnt-1);
2355
+ }
2356
+
2357
+ bodyFirstCells.each(function(i, _cell) {
2358
+ if (i < rowCnt) {
2359
+ cell = $(_cell);
2360
+ setMinHeight(
2361
+ cell.find('> div'),
2362
+ (i==rowCnt-1 ? rowHeightLast : rowHeight) - vsides(cell)
2363
+ );
2364
+ }
2365
+ });
2366
+
2367
+ }
2368
+
2369
+
2370
+ function setWidth(width) {
2371
+ viewWidth = width;
2372
+ colContentPositions.clear();
2373
+ colWidth = Math.floor(viewWidth / colCnt);
2374
+ setOuterWidth(headCells.slice(0, -1), colWidth);
2375
+ }
2376
+
2377
+
2378
+
2379
+ /* Day clicking and binding
2380
+ -----------------------------------------------------------*/
2381
+
2382
+
2383
+ function dayBind(days) {
2384
+ days.click(dayClick)
2385
+ .mousedown(daySelectionMousedown);
2386
+ }
2387
+
2388
+
2389
+ function dayClick(ev) {
2390
+ if (!opt('selectable')) { // if selectable, SelectionManager will worry about dayClick
2391
+ var index = parseInt(this.className.match(/fc\-day(\d+)/)[1]); // TODO: maybe use .data
2392
+ var date = indexDate(index);
2393
+ trigger('dayClick', this, date, true, ev);
2394
+ }
2395
+ }
2396
+
2397
+
2398
+
2399
+ /* Semi-transparent Overlay Helpers
2400
+ ------------------------------------------------------*/
2401
+
2402
+
2403
+ function renderDayOverlay(overlayStart, overlayEnd, refreshCoordinateGrid) { // overlayEnd is exclusive
2404
+ if (refreshCoordinateGrid) {
2405
+ coordinateGrid.build();
2406
+ }
2407
+ var rowStart = cloneDate(t.visStart);
2408
+ var rowEnd = addDays(cloneDate(rowStart), colCnt);
2409
+ for (var i=0; i<rowCnt; i++) {
2410
+ var stretchStart = new Date(Math.max(rowStart, overlayStart));
2411
+ var stretchEnd = new Date(Math.min(rowEnd, overlayEnd));
2412
+ if (stretchStart < stretchEnd) {
2413
+ var colStart, colEnd;
2414
+ if (rtl) {
2415
+ colStart = dayDiff(stretchEnd, rowStart)*dis+dit+1;
2416
+ colEnd = dayDiff(stretchStart, rowStart)*dis+dit+1;
2417
+ }else{
2418
+ colStart = dayDiff(stretchStart, rowStart);
2419
+ colEnd = dayDiff(stretchEnd, rowStart);
2420
+ }
2421
+ dayBind(
2422
+ renderCellOverlay(i, colStart, i, colEnd-1)
2423
+ );
2424
+ }
2425
+ addDays(rowStart, 7);
2426
+ addDays(rowEnd, 7);
2427
+ }
2428
+ }
2429
+
2430
+
2431
+ function renderCellOverlay(row0, col0, row1, col1) { // row1,col1 is inclusive
2432
+ var rect = coordinateGrid.rect(row0, col0, row1, col1, element);
2433
+ return renderOverlay(rect, element);
2434
+ }
2435
+
2436
+
2437
+
2438
+ /* Selection
2439
+ -----------------------------------------------------------------------*/
2440
+
2441
+
2442
+ function defaultSelectionEnd(startDate, allDay) {
2443
+ return cloneDate(startDate);
2444
+ }
2445
+
2446
+
2447
+ function renderSelection(startDate, endDate, allDay) {
2448
+ renderDayOverlay(startDate, addDays(cloneDate(endDate), 1), true); // rebuild every time???
2449
+ }
2450
+
2451
+
2452
+ function clearSelection() {
2453
+ clearOverlays();
2454
+ }
2455
+
2456
+
2457
+ function reportDayClick(date, allDay, ev) {
2458
+ var cell = dateCell(date);
2459
+ var _element = bodyCells[cell.row*colCnt + cell.col];
2460
+ trigger('dayClick', _element, date, allDay, ev);
2461
+ }
2462
+
2463
+
2464
+
2465
+ /* External Dragging
2466
+ -----------------------------------------------------------------------*/
2467
+
2468
+
2469
+ function dragStart(_dragElement, ev, ui) {
2470
+ hoverListener.start(function(cell) {
2471
+ clearOverlays();
2472
+ if (cell) {
2473
+ renderCellOverlay(cell.row, cell.col, cell.row, cell.col);
2474
+ }
2475
+ }, ev);
2476
+ }
2477
+
2478
+
2479
+ function dragStop(_dragElement, ev, ui) {
2480
+ var cell = hoverListener.stop();
2481
+ clearOverlays();
2482
+ if (cell) {
2483
+ var d = cellDate(cell);
2484
+ trigger('drop', _dragElement, d, true, ev, ui);
2485
+ }
2486
+ }
2487
+
2488
+
2489
+
2490
+ /* Utilities
2491
+ --------------------------------------------------------*/
2492
+
2493
+
2494
+ function defaultEventEnd(event) {
2495
+ return cloneDate(event.start);
2496
+ }
2497
+
2498
+
2499
+ coordinateGrid = new CoordinateGrid(function(rows, cols) {
2500
+ var e, n, p;
2501
+ headCells.each(function(i, _e) {
2502
+ e = $(_e);
2503
+ n = e.offset().left;
2504
+ if (i) {
2505
+ p[1] = n;
2506
+ }
2507
+ p = [n];
2508
+ cols[i] = p;
2509
+ });
2510
+ p[1] = n + e.outerWidth();
2511
+ bodyRows.each(function(i, _e) {
2512
+ if (i < rowCnt) {
2513
+ e = $(_e);
2514
+ n = e.offset().top;
2515
+ if (i) {
2516
+ p[1] = n;
2517
+ }
2518
+ p = [n];
2519
+ rows[i] = p;
2520
+ }
2521
+ });
2522
+ p[1] = n + e.outerHeight();
2523
+ });
2524
+
2525
+
2526
+ hoverListener = new HoverListener(coordinateGrid);
2527
+
2528
+
2529
+ colContentPositions = new HorizontalPositionCache(function(col) {
2530
+ return bodyCellTopInners.eq(col);
2531
+ });
2532
+
2533
+
2534
+ function colContentLeft(col) {
2535
+ return colContentPositions.left(col);
2536
+ }
2537
+
2538
+
2539
+ function colContentRight(col) {
2540
+ return colContentPositions.right(col);
2541
+ }
2542
+
2543
+
2544
+
2545
+
2546
+ function dateCell(date) {
2547
+ return {
2548
+ row: Math.floor(dayDiff(date, t.visStart) / 7),
2549
+ col: dayOfWeekCol(date.getDay())
2550
+ };
2551
+ }
2552
+
2553
+
2554
+ function cellDate(cell) {
2555
+ return _cellDate(cell.row, cell.col);
2556
+ }
2557
+
2558
+
2559
+ function _cellDate(row, col) {
2560
+ return addDays(cloneDate(t.visStart), row*7 + col*dis+dit);
2561
+ // what about weekends in middle of week?
2562
+ }
2563
+
2564
+
2565
+ function indexDate(index) {
2566
+ return _cellDate(Math.floor(index/colCnt), index%colCnt);
2567
+ }
2568
+
2569
+
2570
+ function dayOfWeekCol(dayOfWeek) {
2571
+ return ((dayOfWeek - Math.max(firstDay, nwe) + colCnt) % colCnt) * dis + dit;
2572
+ }
2573
+
2574
+
2575
+
2576
+
2577
+ function allDayRow(i) {
2578
+ return bodyRows.eq(i);
2579
+ }
2580
+
2581
+
2582
+ function allDayBounds(i) {
2583
+ return {
2584
+ left: 0,
2585
+ right: viewWidth
2586
+ };
2587
+ }
2588
+
2589
+
2590
+ }
2591
+
2592
+ function BasicEventRenderer() {
2593
+ var t = this;
2594
+
2595
+
2596
+ // exports
2597
+ t.renderEvents = renderEvents;
2598
+ t.compileDaySegs = compileSegs; // for DayEventRenderer
2599
+ t.clearEvents = clearEvents;
2600
+ t.bindDaySeg = bindDaySeg;
2601
+
2602
+
2603
+ // imports
2604
+ DayEventRenderer.call(t);
2605
+ var opt = t.opt;
2606
+ var trigger = t.trigger;
2607
+ //var setOverflowHidden = t.setOverflowHidden;
2608
+ var isEventDraggable = t.isEventDraggable;
2609
+ var isEventResizable = t.isEventResizable;
2610
+ var reportEvents = t.reportEvents;
2611
+ var reportEventClear = t.reportEventClear;
2612
+ var eventElementHandlers = t.eventElementHandlers;
2613
+ var showEvents = t.showEvents;
2614
+ var hideEvents = t.hideEvents;
2615
+ var eventDrop = t.eventDrop;
2616
+ var getDaySegmentContainer = t.getDaySegmentContainer;
2617
+ var getHoverListener = t.getHoverListener;
2618
+ var renderDayOverlay = t.renderDayOverlay;
2619
+ var clearOverlays = t.clearOverlays;
2620
+ var getRowCnt = t.getRowCnt;
2621
+ var getColCnt = t.getColCnt;
2622
+ var renderDaySegs = t.renderDaySegs;
2623
+ var resizableDayEvent = t.resizableDayEvent;
2624
+
2625
+
2626
+
2627
+ /* Rendering
2628
+ --------------------------------------------------------------------*/
2629
+
2630
+
2631
+ function renderEvents(events, modifiedEventId) {
2632
+ reportEvents(events);
2633
+ renderDaySegs(compileSegs(events), modifiedEventId);
2634
+ }
2635
+
2636
+
2637
+ function clearEvents() {
2638
+ reportEventClear();
2639
+ getDaySegmentContainer().empty();
2640
+ }
2641
+
2642
+
2643
+ function compileSegs(events) {
2644
+ var rowCnt = getRowCnt(),
2645
+ colCnt = getColCnt(),
2646
+ d1 = cloneDate(t.visStart),
2647
+ d2 = addDays(cloneDate(d1), colCnt),
2648
+ visEventsEnds = $.map(events, exclEndDay),
2649
+ i, row,
2650
+ j, level,
2651
+ k, seg,
2652
+ segs=[];
2653
+ for (i=0; i<rowCnt; i++) {
2654
+ row = stackSegs(sliceSegs(events, visEventsEnds, d1, d2));
2655
+ for (j=0; j<row.length; j++) {
2656
+ level = row[j];
2657
+ for (k=0; k<level.length; k++) {
2658
+ seg = level[k];
2659
+ seg.row = i;
2660
+ seg.level = j; // not needed anymore
2661
+ segs.push(seg);
2662
+ }
2663
+ }
2664
+ addDays(d1, 7);
2665
+ addDays(d2, 7);
2666
+ }
2667
+ return segs;
2668
+ }
2669
+
2670
+
2671
+ function bindDaySeg(event, eventElement, seg) {
2672
+ if (isEventDraggable(event)) {
2673
+ draggableDayEvent(event, eventElement);
2674
+ }
2675
+ if (seg.isEnd && isEventResizable(event)) {
2676
+ resizableDayEvent(event, eventElement, seg);
2677
+ }
2678
+ eventElementHandlers(event, eventElement);
2679
+ // needs to be after, because resizableDayEvent might stopImmediatePropagation on click
2680
+ }
2681
+
2682
+
2683
+
2684
+ /* Dragging
2685
+ ----------------------------------------------------------------------------*/
2686
+
2687
+
2688
+ function draggableDayEvent(event, eventElement) {
2689
+ var hoverListener = getHoverListener();
2690
+ var dayDelta;
2691
+ eventElement.draggable({
2692
+ zIndex: 9,
2693
+ delay: 50,
2694
+ opacity: opt('dragOpacity'),
2695
+ revertDuration: opt('dragRevertDuration'),
2696
+ start: function(ev, ui) {
2697
+ trigger('eventDragStart', eventElement, event, ev, ui);
2698
+ hideEvents(event, eventElement);
2699
+ hoverListener.start(function(cell, origCell, rowDelta, colDelta) {
2700
+ eventElement.draggable('option', 'revert', !cell || !rowDelta && !colDelta);
2701
+ clearOverlays();
2702
+ if (cell) {
2703
+ //setOverflowHidden(true);
2704
+ dayDelta = rowDelta*7 + colDelta * (opt('isRTL') ? -1 : 1);
2705
+ renderDayOverlay(
2706
+ addDays(cloneDate(event.start), dayDelta),
2707
+ addDays(exclEndDay(event), dayDelta)
2708
+ );
2709
+ }else{
2710
+ //setOverflowHidden(false);
2711
+ dayDelta = 0;
2712
+ }
2713
+ }, ev, 'drag');
2714
+ },
2715
+ stop: function(ev, ui) {
2716
+ hoverListener.stop();
2717
+ clearOverlays();
2718
+ trigger('eventDragStop', eventElement, event, ev, ui);
2719
+ if (dayDelta) {
2720
+ eventDrop(this, event, dayDelta, 0, event.allDay, ev, ui);
2721
+ }else{
2722
+ eventElement.css('filter', ''); // clear IE opacity side-effects
2723
+ showEvents(event, eventElement);
2724
+ }
2725
+ //setOverflowHidden(false);
2726
+ }
2727
+ });
2728
+ }
2729
+
2730
+
2731
+ }
2732
+
2733
+ fcViews.agendaWeek = AgendaWeekView;
2734
+
2735
+ function AgendaWeekView(element, calendar) {
2736
+ var t = this;
2737
+
2738
+
2739
+ // exports
2740
+ t.render = render;
2741
+
2742
+
2743
+ // imports
2744
+ AgendaView.call(t, element, calendar, 'agendaWeek');
2745
+ var opt = t.opt;
2746
+ var renderAgenda = t.renderAgenda;
2747
+ var formatDates = calendar.formatDates;
2748
+
2749
+
2750
+
2751
+ function render(date, delta) {
2752
+ if (delta) {
2753
+ addDays(date, delta * 7);
2754
+ }
2755
+ var start = addDays(cloneDate(date), -((date.getDay() - opt('firstDay') + 7) % 7));
2756
+ var end = addDays(cloneDate(start), 7);
2757
+ var visStart = cloneDate(start);
2758
+ var visEnd = cloneDate(end);
2759
+ var weekends = opt('weekends');
2760
+ if (!weekends) {
2761
+ skipWeekend(visStart);
2762
+ skipWeekend(visEnd, -1, true);
2763
+ }
2764
+ t.title = formatDates(
2765
+ visStart,
2766
+ addDays(cloneDate(visEnd), -1),
2767
+ opt('titleFormat')
2768
+ );
2769
+ t.start = start;
2770
+ t.end = end;
2771
+ t.visStart = visStart;
2772
+ t.visEnd = visEnd;
2773
+ renderAgenda(weekends ? 7 : 5);
2774
+ }
2775
+
2776
+
2777
+ }
2778
+
2779
+ fcViews.agendaDay = AgendaDayView;
2780
+
2781
+ function AgendaDayView(element, calendar) {
2782
+ var t = this;
2783
+
2784
+
2785
+ // exports
2786
+ t.render = render;
2787
+
2788
+
2789
+ // imports
2790
+ AgendaView.call(t, element, calendar, 'agendaDay');
2791
+ var opt = t.opt;
2792
+ var renderAgenda = t.renderAgenda;
2793
+ var formatDate = calendar.formatDate;
2794
+
2795
+
2796
+
2797
+ function render(date, delta) {
2798
+ if (delta) {
2799
+ addDays(date, delta);
2800
+ if (!opt('weekends')) {
2801
+ skipWeekend(date, delta < 0 ? -1 : 1);
2802
+ }
2803
+ }
2804
+ var start = cloneDate(date, true);
2805
+ var end = addDays(cloneDate(start), 1);
2806
+ t.title = formatDate(date, opt('titleFormat'));
2807
+ t.start = t.visStart = start;
2808
+ t.end = t.visEnd = end;
2809
+ renderAgenda(1);
2810
+ }
2811
+
2812
+
2813
+ }
2814
+
2815
+ setDefaults({
2816
+ allDaySlot: true,
2817
+ allDayText: 'all-day',
2818
+ firstHour: 6,
2819
+ slotMinutes: 30,
2820
+ defaultEventMinutes: 120,
2821
+ axisFormat: 'h(:mm)tt',
2822
+ timeFormat: {
2823
+ agenda: 'h:mm{ - h:mm}'
2824
+ },
2825
+ dragOpacity: {
2826
+ agenda: .5
2827
+ },
2828
+ minTime: 0,
2829
+ maxTime: 24
2830
+ });
2831
+
2832
+
2833
+ // TODO: make it work in quirks mode (event corners, all-day height)
2834
+ // TODO: test liquid width, especially in IE6
2835
+
2836
+
2837
+ function AgendaView(element, calendar, viewName) {
2838
+ var t = this;
2839
+
2840
+
2841
+ // exports
2842
+ t.renderAgenda = renderAgenda;
2843
+ t.setWidth = setWidth;
2844
+ t.setHeight = setHeight;
2845
+ t.beforeHide = beforeHide;
2846
+ t.afterShow = afterShow;
2847
+ t.defaultEventEnd = defaultEventEnd;
2848
+ t.timePosition = timePosition;
2849
+ t.dayOfWeekCol = dayOfWeekCol;
2850
+ t.dateCell = dateCell;
2851
+ t.cellDate = cellDate;
2852
+ t.cellIsAllDay = cellIsAllDay;
2853
+ t.allDayRow = getAllDayRow;
2854
+ t.allDayBounds = allDayBounds;
2855
+ t.getHoverListener = function() { return hoverListener };
2856
+ t.colContentLeft = colContentLeft;
2857
+ t.colContentRight = colContentRight;
2858
+ t.getDaySegmentContainer = function() { return daySegmentContainer };
2859
+ t.getSlotSegmentContainer = function() { return slotSegmentContainer };
2860
+ t.getMinMinute = function() { return minMinute };
2861
+ t.getMaxMinute = function() { return maxMinute };
2862
+ t.getBodyContent = function() { return slotContent }; // !!??
2863
+ t.getRowCnt = function() { return 1 };
2864
+ t.getColCnt = function() { return colCnt };
2865
+ t.getColWidth = function() { return colWidth };
2866
+ t.getSlotHeight = function() { return slotHeight };
2867
+ t.defaultSelectionEnd = defaultSelectionEnd;
2868
+ t.renderDayOverlay = renderDayOverlay;
2869
+ t.renderSelection = renderSelection;
2870
+ t.clearSelection = clearSelection;
2871
+ t.reportDayClick = reportDayClick; // selection mousedown hack
2872
+ t.dragStart = dragStart;
2873
+ t.dragStop = dragStop;
2874
+
2875
+
2876
+ // imports
2877
+ View.call(t, element, calendar, viewName);
2878
+ OverlayManager.call(t);
2879
+ SelectionManager.call(t);
2880
+ AgendaEventRenderer.call(t);
2881
+ var opt = t.opt;
2882
+ var trigger = t.trigger;
2883
+ var clearEvents = t.clearEvents;
2884
+ var renderOverlay = t.renderOverlay;
2885
+ var clearOverlays = t.clearOverlays;
2886
+ var reportSelection = t.reportSelection;
2887
+ var unselect = t.unselect;
2888
+ var daySelectionMousedown = t.daySelectionMousedown;
2889
+ var slotSegHtml = t.slotSegHtml;
2890
+ var formatDate = calendar.formatDate;
2891
+
2892
+
2893
+ // locals
2894
+
2895
+ var dayTable;
2896
+ var dayHead;
2897
+ var dayHeadCells;
2898
+ var dayBody;
2899
+ var dayBodyCells;
2900
+ var dayBodyCellInners;
2901
+ var dayBodyFirstCell;
2902
+ var dayBodyFirstCellStretcher;
2903
+ var slotLayer;
2904
+ var daySegmentContainer;
2905
+ var allDayTable;
2906
+ var allDayRow;
2907
+ var slotScroller;
2908
+ var slotContent;
2909
+ var slotSegmentContainer;
2910
+ var slotTable;
2911
+ var slotTableFirstInner;
2912
+ var axisFirstCells;
2913
+ var gutterCells;
2914
+ var selectionHelper;
2915
+
2916
+ var viewWidth;
2917
+ var viewHeight;
2918
+ var axisWidth;
2919
+ var colWidth;
2920
+ var gutterWidth;
2921
+ var slotHeight; // TODO: what if slotHeight changes? (see issue 650)
2922
+ var savedScrollTop;
2923
+
2924
+ var colCnt;
2925
+ var slotCnt;
2926
+ var coordinateGrid;
2927
+ var hoverListener;
2928
+ var colContentPositions;
2929
+ var slotTopCache = {};
2930
+
2931
+ var tm;
2932
+ var firstDay;
2933
+ var nwe; // no weekends (int)
2934
+ var rtl, dis, dit; // day index sign / translate
2935
+ var minMinute, maxMinute;
2936
+ var colFormat;
2937
+
2938
+
2939
+
2940
+ /* Rendering
2941
+ -----------------------------------------------------------------------------*/
2942
+
2943
+
2944
+ disableTextSelection(element.addClass('fc-agenda'));
2945
+
2946
+
2947
+ function renderAgenda(c) {
2948
+ colCnt = c;
2949
+ updateOptions();
2950
+ if (!dayTable) {
2951
+ buildSkeleton();
2952
+ }else{
2953
+ clearEvents();
2954
+ }
2955
+ updateCells();
2956
+ }
2957
+
2958
+
2959
+
2960
+ function updateOptions() {
2961
+ tm = opt('theme') ? 'ui' : 'fc';
2962
+ nwe = opt('weekends') ? 0 : 1;
2963
+ firstDay = opt('firstDay');
2964
+ if (rtl = opt('isRTL')) {
2965
+ dis = -1;
2966
+ dit = colCnt - 1;
2967
+ }else{
2968
+ dis = 1;
2969
+ dit = 0;
2970
+ }
2971
+ minMinute = parseTime(opt('minTime'));
2972
+ maxMinute = parseTime(opt('maxTime'));
2973
+ colFormat = opt('columnFormat');
2974
+ }
2975
+
2976
+
2977
+
2978
+ function buildSkeleton() {
2979
+ var headerClass = tm + "-widget-header";
2980
+ var contentClass = tm + "-widget-content";
2981
+ var s;
2982
+ var i;
2983
+ var d;
2984
+ var maxd;
2985
+ var minutes;
2986
+ var slotNormal = opt('slotMinutes') % 15 == 0;
2987
+
2988
+ s =
2989
+ "<table style='width:100%' class='fc-agenda-days fc-border-separate' cellspacing='0'>" +
2990
+ "<thead>" +
2991
+ "<tr>" +
2992
+ "<th class='fc-agenda-axis " + headerClass + "'>&nbsp;</th>";
2993
+ for (i=0; i<colCnt; i++) {
2994
+ s +=
2995
+ "<th class='fc- fc-col" + i + ' ' + headerClass + "'/>"; // fc- needed for setDayID
2996
+ }
2997
+ s +=
2998
+ "<th class='fc-agenda-gutter " + headerClass + "'>&nbsp;</th>" +
2999
+ "</tr>" +
3000
+ "</thead>" +
3001
+ "<tbody>" +
3002
+ "<tr>" +
3003
+ "<th class='fc-agenda-axis " + headerClass + "'>&nbsp;</th>";
3004
+ for (i=0; i<colCnt; i++) {
3005
+ s +=
3006
+ "<td class='fc- fc-col" + i + ' ' + contentClass + "'>" + // fc- needed for setDayID
3007
+ "<div>" +
3008
+ "<div class='fc-day-content'>" +
3009
+ "<div style='position:relative'>&nbsp;</div>" +
3010
+ "</div>" +
3011
+ "</div>" +
3012
+ "</td>";
3013
+ }
3014
+ s +=
3015
+ "<td class='fc-agenda-gutter " + contentClass + "'>&nbsp;</td>" +
3016
+ "</tr>" +
3017
+ "</tbody>" +
3018
+ "</table>";
3019
+ dayTable = $(s).appendTo(element);
3020
+ dayHead = dayTable.find('thead');
3021
+ dayHeadCells = dayHead.find('th').slice(1, -1);
3022
+ dayBody = dayTable.find('tbody');
3023
+ dayBodyCells = dayBody.find('td').slice(0, -1);
3024
+ dayBodyCellInners = dayBodyCells.find('div.fc-day-content div');
3025
+ dayBodyFirstCell = dayBodyCells.eq(0);
3026
+ dayBodyFirstCellStretcher = dayBodyFirstCell.find('> div');
3027
+
3028
+ markFirstLast(dayHead.add(dayHead.find('tr')));
3029
+ markFirstLast(dayBody.add(dayBody.find('tr')));
3030
+
3031
+ axisFirstCells = dayHead.find('th:first');
3032
+ gutterCells = dayTable.find('.fc-agenda-gutter');
3033
+
3034
+ slotLayer =
3035
+ $("<div style='position:absolute;z-index:2;left:0;width:100%'/>")
3036
+ .appendTo(element);
3037
+
3038
+ if (opt('allDaySlot')) {
3039
+
3040
+ daySegmentContainer =
3041
+ $("<div style='position:absolute;z-index:8;top:0;left:0'/>")
3042
+ .appendTo(slotLayer);
3043
+
3044
+ s =
3045
+ "<table style='width:100%' class='fc-agenda-allday' cellspacing='0'>" +
3046
+ "<tr>" +
3047
+ "<th class='" + headerClass + " fc-agenda-axis'>" + opt('allDayText') + "</th>" +
3048
+ "<td>" +
3049
+ "<div class='fc-day-content'><div style='position:relative'/></div>" +
3050
+ "</td>" +
3051
+ "<th class='" + headerClass + " fc-agenda-gutter'>&nbsp;</th>" +
3052
+ "</tr>" +
3053
+ "</table>";
3054
+ allDayTable = $(s).appendTo(slotLayer);
3055
+ allDayRow = allDayTable.find('tr');
3056
+
3057
+ dayBind(allDayRow.find('td'));
3058
+
3059
+ axisFirstCells = axisFirstCells.add(allDayTable.find('th:first'));
3060
+ gutterCells = gutterCells.add(allDayTable.find('th.fc-agenda-gutter'));
3061
+
3062
+ slotLayer.append(
3063
+ "<div class='fc-agenda-divider " + headerClass + "'>" +
3064
+ "<div class='fc-agenda-divider-inner'/>" +
3065
+ "</div>"
3066
+ );
3067
+
3068
+ }else{
3069
+
3070
+ daySegmentContainer = $([]); // in jQuery 1.4, we can just do $()
3071
+
3072
+ }
3073
+
3074
+ slotScroller =
3075
+ $("<div style='position:absolute;width:100%;overflow-x:hidden;overflow-y:auto'/>")
3076
+ .appendTo(slotLayer);
3077
+
3078
+ slotContent =
3079
+ $("<div style='position:relative;width:100%;overflow:hidden'/>")
3080
+ .appendTo(slotScroller);
3081
+
3082
+ slotSegmentContainer =
3083
+ $("<div style='position:absolute;z-index:8;top:0;left:0'/>")
3084
+ .appendTo(slotContent);
3085
+
3086
+ s =
3087
+ "<table class='fc-agenda-slots' style='width:100%' cellspacing='0'>" +
3088
+ "<tbody>";
3089
+ d = zeroDate();
3090
+ maxd = addMinutes(cloneDate(d), maxMinute);
3091
+ addMinutes(d, minMinute);
3092
+ slotCnt = 0;
3093
+ for (i=0; d < maxd; i++) {
3094
+ minutes = d.getMinutes();
3095
+ s +=
3096
+ "<tr class='fc-slot" + i + ' ' + (!minutes ? '' : 'fc-minor') + "'>" +
3097
+ "<th class='fc-agenda-axis " + headerClass + "'>" +
3098
+ ((!slotNormal || !minutes) ? formatDate(d, opt('axisFormat')) : '&nbsp;') +
3099
+ "</th>" +
3100
+ "<td class='" + contentClass + "'>" +
3101
+ "<div style='position:relative'>&nbsp;</div>" +
3102
+ "</td>" +
3103
+ "</tr>";
3104
+ addMinutes(d, opt('slotMinutes'));
3105
+ slotCnt++;
3106
+ }
3107
+ s +=
3108
+ "</tbody>" +
3109
+ "</table>";
3110
+ slotTable = $(s).appendTo(slotContent);
3111
+ slotTableFirstInner = slotTable.find('div:first');
3112
+
3113
+ slotBind(slotTable.find('td'));
3114
+
3115
+ axisFirstCells = axisFirstCells.add(slotTable.find('th:first'));
3116
+ }
3117
+
3118
+
3119
+
3120
+ function updateCells() {
3121
+ var i;
3122
+ var headCell;
3123
+ var bodyCell;
3124
+ var date;
3125
+ var today = clearTime(new Date());
3126
+ for (i=0; i<colCnt; i++) {
3127
+ date = colDate(i);
3128
+ headCell = dayHeadCells.eq(i);
3129
+ headCell.html(formatDate(date, colFormat));
3130
+ bodyCell = dayBodyCells.eq(i);
3131
+ if (+date == +today) {
3132
+ bodyCell.addClass(tm + '-state-highlight fc-today');
3133
+ }else{
3134
+ bodyCell.removeClass(tm + '-state-highlight fc-today');
3135
+ }
3136
+ setDayID(headCell.add(bodyCell), date);
3137
+ }
3138
+ }
3139
+
3140
+
3141
+
3142
+ function setHeight(height, dateChanged) {
3143
+ if (height === undefined) {
3144
+ height = viewHeight;
3145
+ }
3146
+ viewHeight = height;
3147
+ slotTopCache = {};
3148
+
3149
+ var headHeight = dayBody.position().top;
3150
+ var allDayHeight = slotScroller.position().top; // including divider
3151
+ var bodyHeight = Math.min( // total body height, including borders
3152
+ height - headHeight, // when scrollbars
3153
+ slotTable.height() + allDayHeight + 1 // when no scrollbars. +1 for bottom border
3154
+ );
3155
+
3156
+ dayBodyFirstCellStretcher
3157
+ .height(bodyHeight - vsides(dayBodyFirstCell));
3158
+
3159
+ slotLayer.css('top', headHeight);
3160
+
3161
+ slotScroller.height(bodyHeight - allDayHeight - 1);
3162
+
3163
+ slotHeight = slotTableFirstInner.height() + 1; // +1 for border
3164
+
3165
+ if (dateChanged) {
3166
+ resetScroll();
3167
+ }
3168
+ }
3169
+
3170
+
3171
+
3172
+ function setWidth(width) {
3173
+ viewWidth = width;
3174
+ colContentPositions.clear();
3175
+
3176
+ axisWidth = 0;
3177
+ setOuterWidth(
3178
+ axisFirstCells
3179
+ .width('')
3180
+ .each(function(i, _cell) {
3181
+ axisWidth = Math.max(axisWidth, $(_cell).outerWidth());
3182
+ }),
3183
+ axisWidth
3184
+ );
3185
+
3186
+ var slotTableWidth = slotScroller[0].clientWidth; // needs to be done after axisWidth (for IE7)
3187
+ //slotTable.width(slotTableWidth);
3188
+
3189
+ gutterWidth = slotScroller.width() - slotTableWidth;
3190
+ if (gutterWidth) {
3191
+ setOuterWidth(gutterCells, gutterWidth);
3192
+ gutterCells
3193
+ .show()
3194
+ .prev()
3195
+ .removeClass('fc-last');
3196
+ }else{
3197
+ gutterCells
3198
+ .hide()
3199
+ .prev()
3200
+ .addClass('fc-last');
3201
+ }
3202
+
3203
+ colWidth = Math.floor((slotTableWidth - axisWidth) / colCnt);
3204
+ setOuterWidth(dayHeadCells.slice(0, -1), colWidth);
3205
+ }
3206
+
3207
+
3208
+
3209
+ function resetScroll() {
3210
+ var d0 = zeroDate();
3211
+ var scrollDate = cloneDate(d0);
3212
+ scrollDate.setHours(opt('firstHour'));
3213
+ var top = timePosition(d0, scrollDate) + 1; // +1 for the border
3214
+ function scroll() {
3215
+ slotScroller.scrollTop(top);
3216
+ }
3217
+ scroll();
3218
+ setTimeout(scroll, 0); // overrides any previous scroll state made by the browser
3219
+ }
3220
+
3221
+
3222
+ function beforeHide() {
3223
+ savedScrollTop = slotScroller.scrollTop();
3224
+ }
3225
+
3226
+
3227
+ function afterShow() {
3228
+ slotScroller.scrollTop(savedScrollTop);
3229
+ }
3230
+
3231
+
3232
+
3233
+ /* Slot/Day clicking and binding
3234
+ -----------------------------------------------------------------------*/
3235
+
3236
+
3237
+ function dayBind(cells) {
3238
+ cells.click(slotClick)
3239
+ .mousedown(daySelectionMousedown);
3240
+ }
3241
+
3242
+
3243
+ function slotBind(cells) {
3244
+ cells.click(slotClick)
3245
+ .mousedown(slotSelectionMousedown);
3246
+ }
3247
+
3248
+
3249
+ function slotClick(ev) {
3250
+ if (!opt('selectable')) { // if selectable, SelectionManager will worry about dayClick
3251
+ var col = Math.min(colCnt-1, Math.floor((ev.pageX - dayTable.offset().left - axisWidth) / colWidth));
3252
+ var date = colDate(col);
3253
+ var rowMatch = this.parentNode.className.match(/fc-slot(\d+)/); // TODO: maybe use data
3254
+ if (rowMatch) {
3255
+ var mins = parseInt(rowMatch[1]) * opt('slotMinutes');
3256
+ var hours = Math.floor(mins/60);
3257
+ date.setHours(hours);
3258
+ date.setMinutes(mins%60 + minMinute);
3259
+ trigger('dayClick', dayBodyCells[col], date, false, ev);
3260
+ }else{
3261
+ trigger('dayClick', dayBodyCells[col], date, true, ev);
3262
+ }
3263
+ }
3264
+ }
3265
+
3266
+
3267
+
3268
+ /* Semi-transparent Overlay Helpers
3269
+ -----------------------------------------------------*/
3270
+
3271
+
3272
+ function renderDayOverlay(startDate, endDate, refreshCoordinateGrid) { // endDate is exclusive
3273
+ if (refreshCoordinateGrid) {
3274
+ coordinateGrid.build();
3275
+ }
3276
+ var visStart = cloneDate(t.visStart);
3277
+ var startCol, endCol;
3278
+ if (rtl) {
3279
+ startCol = dayDiff(endDate, visStart)*dis+dit+1;
3280
+ endCol = dayDiff(startDate, visStart)*dis+dit+1;
3281
+ }else{
3282
+ startCol = dayDiff(startDate, visStart);
3283
+ endCol = dayDiff(endDate, visStart);
3284
+ }
3285
+ startCol = Math.max(0, startCol);
3286
+ endCol = Math.min(colCnt, endCol);
3287
+ if (startCol < endCol) {
3288
+ dayBind(
3289
+ renderCellOverlay(0, startCol, 0, endCol-1)
3290
+ );
3291
+ }
3292
+ }
3293
+
3294
+
3295
+ function renderCellOverlay(row0, col0, row1, col1) { // only for all-day?
3296
+ var rect = coordinateGrid.rect(row0, col0, row1, col1, slotLayer);
3297
+ return renderOverlay(rect, slotLayer);
3298
+ }
3299
+
3300
+
3301
+ function renderSlotOverlay(overlayStart, overlayEnd) {
3302
+ var dayStart = cloneDate(t.visStart);
3303
+ var dayEnd = addDays(cloneDate(dayStart), 1);
3304
+ for (var i=0; i<colCnt; i++) {
3305
+ var stretchStart = new Date(Math.max(dayStart, overlayStart));
3306
+ var stretchEnd = new Date(Math.min(dayEnd, overlayEnd));
3307
+ if (stretchStart < stretchEnd) {
3308
+ var col = i*dis+dit;
3309
+ var rect = coordinateGrid.rect(0, col, 0, col, slotContent); // only use it for horizontal coords
3310
+ var top = timePosition(dayStart, stretchStart);
3311
+ var bottom = timePosition(dayStart, stretchEnd);
3312
+ rect.top = top;
3313
+ rect.height = bottom - top;
3314
+ slotBind(
3315
+ renderOverlay(rect, slotContent)
3316
+ );
3317
+ }
3318
+ addDays(dayStart, 1);
3319
+ addDays(dayEnd, 1);
3320
+ }
3321
+ }
3322
+
3323
+
3324
+
3325
+ /* Coordinate Utilities
3326
+ -----------------------------------------------------------------------------*/
3327
+
3328
+
3329
+ coordinateGrid = new CoordinateGrid(function(rows, cols) {
3330
+ var e, n, p;
3331
+ dayHeadCells.each(function(i, _e) {
3332
+ e = $(_e);
3333
+ n = e.offset().left;
3334
+ if (i) {
3335
+ p[1] = n;
3336
+ }
3337
+ p = [n];
3338
+ cols[i] = p;
3339
+ });
3340
+ p[1] = n + e.outerWidth();
3341
+ if (opt('allDaySlot')) {
3342
+ e = allDayRow;
3343
+ n = e.offset().top;
3344
+ rows[0] = [n, n+e.outerHeight()];
3345
+ }
3346
+ var slotTableTop = slotContent.offset().top;
3347
+ var slotScrollerTop = slotScroller.offset().top;
3348
+ var slotScrollerBottom = slotScrollerTop + slotScroller.outerHeight();
3349
+ function constrain(n) {
3350
+ return Math.max(slotScrollerTop, Math.min(slotScrollerBottom, n));
3351
+ }
3352
+ for (var i=0; i<slotCnt; i++) {
3353
+ rows.push([
3354
+ constrain(slotTableTop + slotHeight*i),
3355
+ constrain(slotTableTop + slotHeight*(i+1))
3356
+ ]);
3357
+ }
3358
+ });
3359
+
3360
+
3361
+ hoverListener = new HoverListener(coordinateGrid);
3362
+
3363
+
3364
+ colContentPositions = new HorizontalPositionCache(function(col) {
3365
+ return dayBodyCellInners.eq(col);
3366
+ });
3367
+
3368
+
3369
+ function colContentLeft(col) {
3370
+ return colContentPositions.left(col);
3371
+ }
3372
+
3373
+
3374
+ function colContentRight(col) {
3375
+ return colContentPositions.right(col);
3376
+ }
3377
+
3378
+
3379
+
3380
+
3381
+ function dateCell(date) { // "cell" terminology is now confusing
3382
+ return {
3383
+ row: Math.floor(dayDiff(date, t.visStart) / 7),
3384
+ col: dayOfWeekCol(date.getDay())
3385
+ };
3386
+ }
3387
+
3388
+
3389
+ function cellDate(cell) {
3390
+ var d = colDate(cell.col);
3391
+ var slotIndex = cell.row;
3392
+ if (opt('allDaySlot')) {
3393
+ slotIndex--;
3394
+ }
3395
+ if (slotIndex >= 0) {
3396
+ addMinutes(d, minMinute + slotIndex * opt('slotMinutes'));
3397
+ }
3398
+ return d;
3399
+ }
3400
+
3401
+
3402
+ function colDate(col) { // returns dates with 00:00:00
3403
+ return addDays(cloneDate(t.visStart), col*dis+dit);
3404
+ }
3405
+
3406
+
3407
+ function cellIsAllDay(cell) {
3408
+ return opt('allDaySlot') && !cell.row;
3409
+ }
3410
+
3411
+
3412
+ function dayOfWeekCol(dayOfWeek) {
3413
+ return ((dayOfWeek - Math.max(firstDay, nwe) + colCnt) % colCnt)*dis+dit;
3414
+ }
3415
+
3416
+
3417
+
3418
+
3419
+ // get the Y coordinate of the given time on the given day (both Date objects)
3420
+ function timePosition(day, time) { // both date objects. day holds 00:00 of current day
3421
+ day = cloneDate(day, true);
3422
+ if (time < addMinutes(cloneDate(day), minMinute)) {
3423
+ return 0;
3424
+ }
3425
+ if (time >= addMinutes(cloneDate(day), maxMinute)) {
3426
+ return slotTable.height();
3427
+ }
3428
+ var slotMinutes = opt('slotMinutes'),
3429
+ minutes = time.getHours()*60 + time.getMinutes() - minMinute,
3430
+ slotI = Math.floor(minutes / slotMinutes),
3431
+ slotTop = slotTopCache[slotI];
3432
+ if (slotTop === undefined) {
3433
+ slotTop = slotTopCache[slotI] = slotTable.find('tr:eq(' + slotI + ') td div')[0].offsetTop; //.position().top; // need this optimization???
3434
+ }
3435
+ return Math.max(0, Math.round(
3436
+ slotTop - 1 + slotHeight * ((minutes % slotMinutes) / slotMinutes)
3437
+ ));
3438
+ }
3439
+
3440
+
3441
+ function allDayBounds() {
3442
+ return {
3443
+ left: axisWidth,
3444
+ right: viewWidth - gutterWidth
3445
+ }
3446
+ }
3447
+
3448
+
3449
+ function getAllDayRow(index) {
3450
+ return allDayRow;
3451
+ }
3452
+
3453
+
3454
+ function defaultEventEnd(event) {
3455
+ var start = cloneDate(event.start);
3456
+ if (event.allDay) {
3457
+ return start;
3458
+ }
3459
+ return addMinutes(start, opt('defaultEventMinutes'));
3460
+ }
3461
+
3462
+
3463
+
3464
+ /* Selection
3465
+ ---------------------------------------------------------------------------------*/
3466
+
3467
+
3468
+ function defaultSelectionEnd(startDate, allDay) {
3469
+ if (allDay) {
3470
+ return cloneDate(startDate);
3471
+ }
3472
+ return addMinutes(cloneDate(startDate), opt('slotMinutes'));
3473
+ }
3474
+
3475
+
3476
+ function renderSelection(startDate, endDate, allDay) { // only for all-day
3477
+ if (allDay) {
3478
+ if (opt('allDaySlot')) {
3479
+ renderDayOverlay(startDate, addDays(cloneDate(endDate), 1), true);
3480
+ }
3481
+ }else{
3482
+ renderSlotSelection(startDate, endDate);
3483
+ }
3484
+ }
3485
+
3486
+
3487
+ function renderSlotSelection(startDate, endDate) {
3488
+ var helperOption = opt('selectHelper');
3489
+ coordinateGrid.build();
3490
+ if (helperOption) {
3491
+ var col = dayDiff(startDate, t.visStart) * dis + dit;
3492
+ if (col >= 0 && col < colCnt) { // only works when times are on same day
3493
+ var rect = coordinateGrid.rect(0, col, 0, col, slotContent); // only for horizontal coords
3494
+ var top = timePosition(startDate, startDate);
3495
+ var bottom = timePosition(startDate, endDate);
3496
+ if (bottom > top) { // protect against selections that are entirely before or after visible range
3497
+ rect.top = top;
3498
+ rect.height = bottom - top;
3499
+ rect.left += 2;
3500
+ rect.width -= 5;
3501
+ if ($.isFunction(helperOption)) {
3502
+ var helperRes = helperOption(startDate, endDate);
3503
+ if (helperRes) {
3504
+ rect.position = 'absolute';
3505
+ rect.zIndex = 8;
3506
+ selectionHelper = $(helperRes)
3507
+ .css(rect)
3508
+ .appendTo(slotContent);
3509
+ }
3510
+ }else{
3511
+ rect.isStart = true; // conside rect a "seg" now
3512
+ rect.isEnd = true; //
3513
+ selectionHelper = $(slotSegHtml(
3514
+ {
3515
+ title: '',
3516
+ start: startDate,
3517
+ end: endDate,
3518
+ className: ['fc-select-helper'],
3519
+ editable: false
3520
+ },
3521
+ rect
3522
+ ));
3523
+ selectionHelper.css('opacity', opt('dragOpacity'));
3524
+ }
3525
+ if (selectionHelper) {
3526
+ slotBind(selectionHelper);
3527
+ slotContent.append(selectionHelper);
3528
+ setOuterWidth(selectionHelper, rect.width, true); // needs to be after appended
3529
+ setOuterHeight(selectionHelper, rect.height, true);
3530
+ }
3531
+ }
3532
+ }
3533
+ }else{
3534
+ renderSlotOverlay(startDate, endDate);
3535
+ }
3536
+ }
3537
+
3538
+
3539
+ function clearSelection() {
3540
+ clearOverlays();
3541
+ if (selectionHelper) {
3542
+ selectionHelper.remove();
3543
+ selectionHelper = null;
3544
+ }
3545
+ }
3546
+
3547
+
3548
+ function slotSelectionMousedown(ev) {
3549
+ if (ev.which == 1 && opt('selectable')) { // ev.which==1 means left mouse button
3550
+ unselect(ev);
3551
+ var dates;
3552
+ hoverListener.start(function(cell, origCell) {
3553
+ clearSelection();
3554
+ if (cell && cell.col == origCell.col && !cellIsAllDay(cell)) {
3555
+ var d1 = cellDate(origCell);
3556
+ var d2 = cellDate(cell);
3557
+ dates = [
3558
+ d1,
3559
+ addMinutes(cloneDate(d1), opt('slotMinutes')),
3560
+ d2,
3561
+ addMinutes(cloneDate(d2), opt('slotMinutes'))
3562
+ ].sort(cmp);
3563
+ renderSlotSelection(dates[0], dates[3]);
3564
+ }else{
3565
+ dates = null;
3566
+ }
3567
+ }, ev);
3568
+ $(document).one('mouseup', function(ev) {
3569
+ hoverListener.stop();
3570
+ if (dates) {
3571
+ if (+dates[0] == +dates[1]) {
3572
+ reportDayClick(dates[0], false, ev);
3573
+ }
3574
+ reportSelection(dates[0], dates[3], false, ev);
3575
+ }
3576
+ });
3577
+ }
3578
+ }
3579
+
3580
+
3581
+ function reportDayClick(date, allDay, ev) {
3582
+ trigger('dayClick', dayBodyCells[dayOfWeekCol(date.getDay())], date, allDay, ev);
3583
+ }
3584
+
3585
+
3586
+
3587
+ /* External Dragging
3588
+ --------------------------------------------------------------------------------*/
3589
+
3590
+
3591
+ function dragStart(_dragElement, ev, ui) {
3592
+ hoverListener.start(function(cell) {
3593
+ clearOverlays();
3594
+ if (cell) {
3595
+ if (cellIsAllDay(cell)) {
3596
+ renderCellOverlay(cell.row, cell.col, cell.row, cell.col);
3597
+ }else{
3598
+ var d1 = cellDate(cell);
3599
+ var d2 = addMinutes(cloneDate(d1), opt('defaultEventMinutes'));
3600
+ renderSlotOverlay(d1, d2);
3601
+ }
3602
+ }
3603
+ }, ev);
3604
+ }
3605
+
3606
+
3607
+ function dragStop(_dragElement, ev, ui) {
3608
+ var cell = hoverListener.stop();
3609
+ clearOverlays();
3610
+ if (cell) {
3611
+ trigger('drop', _dragElement, cellDate(cell), cellIsAllDay(cell), ev, ui);
3612
+ }
3613
+ }
3614
+
3615
+
3616
+ }
3617
+
3618
+ function AgendaEventRenderer() {
3619
+ var t = this;
3620
+
3621
+
3622
+ // exports
3623
+ t.renderEvents = renderEvents;
3624
+ t.compileDaySegs = compileDaySegs; // for DayEventRenderer
3625
+ t.clearEvents = clearEvents;
3626
+ t.slotSegHtml = slotSegHtml;
3627
+ t.bindDaySeg = bindDaySeg;
3628
+
3629
+
3630
+ // imports
3631
+ DayEventRenderer.call(t);
3632
+ var opt = t.opt;
3633
+ var trigger = t.trigger;
3634
+ //var setOverflowHidden = t.setOverflowHidden;
3635
+ var isEventDraggable = t.isEventDraggable;
3636
+ var isEventResizable = t.isEventResizable;
3637
+ var eventEnd = t.eventEnd;
3638
+ var reportEvents = t.reportEvents;
3639
+ var reportEventClear = t.reportEventClear;
3640
+ var eventElementHandlers = t.eventElementHandlers;
3641
+ var setHeight = t.setHeight;
3642
+ var getDaySegmentContainer = t.getDaySegmentContainer;
3643
+ var getSlotSegmentContainer = t.getSlotSegmentContainer;
3644
+ var getHoverListener = t.getHoverListener;
3645
+ var getMaxMinute = t.getMaxMinute;
3646
+ var getMinMinute = t.getMinMinute;
3647
+ var timePosition = t.timePosition;
3648
+ var colContentLeft = t.colContentLeft;
3649
+ var colContentRight = t.colContentRight;
3650
+ var renderDaySegs = t.renderDaySegs;
3651
+ var resizableDayEvent = t.resizableDayEvent; // TODO: streamline binding architecture
3652
+ var getColCnt = t.getColCnt;
3653
+ var getColWidth = t.getColWidth;
3654
+ var getSlotHeight = t.getSlotHeight;
3655
+ var getBodyContent = t.getBodyContent;
3656
+ var reportEventElement = t.reportEventElement;
3657
+ var showEvents = t.showEvents;
3658
+ var hideEvents = t.hideEvents;
3659
+ var eventDrop = t.eventDrop;
3660
+ var eventResize = t.eventResize;
3661
+ var renderDayOverlay = t.renderDayOverlay;
3662
+ var clearOverlays = t.clearOverlays;
3663
+ var calendar = t.calendar;
3664
+ var formatDate = calendar.formatDate;
3665
+ var formatDates = calendar.formatDates;
3666
+
3667
+
3668
+
3669
+ /* Rendering
3670
+ ----------------------------------------------------------------------------*/
3671
+
3672
+
3673
+ function renderEvents(events, modifiedEventId) {
3674
+ reportEvents(events);
3675
+ var i, len=events.length,
3676
+ dayEvents=[],
3677
+ slotEvents=[];
3678
+ for (i=0; i<len; i++) {
3679
+ if (events[i].allDay) {
3680
+ dayEvents.push(events[i]);
3681
+ }else{
3682
+ slotEvents.push(events[i]);
3683
+ }
3684
+ }
3685
+ if (opt('allDaySlot')) {
3686
+ renderDaySegs(compileDaySegs(dayEvents), modifiedEventId);
3687
+ setHeight(); // no params means set to viewHeight
3688
+ }
3689
+ renderSlotSegs(compileSlotSegs(slotEvents), modifiedEventId);
3690
+ }
3691
+
3692
+
3693
+ function clearEvents() {
3694
+ reportEventClear();
3695
+ getDaySegmentContainer().empty();
3696
+ getSlotSegmentContainer().empty();
3697
+ }
3698
+
3699
+
3700
+ function compileDaySegs(events) {
3701
+ var levels = stackSegs(sliceSegs(events, $.map(events, exclEndDay), t.visStart, t.visEnd)),
3702
+ i, levelCnt=levels.length, level,
3703
+ j, seg,
3704
+ segs=[];
3705
+ for (i=0; i<levelCnt; i++) {
3706
+ level = levels[i];
3707
+ for (j=0; j<level.length; j++) {
3708
+ seg = level[j];
3709
+ seg.row = 0;
3710
+ seg.level = i; // not needed anymore
3711
+ segs.push(seg);
3712
+ }
3713
+ }
3714
+ return segs;
3715
+ }
3716
+
3717
+
3718
+ function compileSlotSegs(events) {
3719
+ var colCnt = getColCnt(),
3720
+ minMinute = getMinMinute(),
3721
+ maxMinute = getMaxMinute(),
3722
+ d = addMinutes(cloneDate(t.visStart), minMinute),
3723
+ visEventEnds = $.map(events, slotEventEnd),
3724
+ i, col,
3725
+ j, level,
3726
+ k, seg,
3727
+ segs=[];
3728
+ for (i=0; i<colCnt; i++) {
3729
+ col = stackSegs(sliceSegs(events, visEventEnds, d, addMinutes(cloneDate(d), maxMinute-minMinute)));
3730
+ countForwardSegs(col);
3731
+ for (j=0; j<col.length; j++) {
3732
+ level = col[j];
3733
+ for (k=0; k<level.length; k++) {
3734
+ seg = level[k];
3735
+ seg.col = i;
3736
+ seg.level = j;
3737
+ segs.push(seg);
3738
+ }
3739
+ }
3740
+ addDays(d, 1, true);
3741
+ }
3742
+ return segs;
3743
+ }
3744
+
3745
+
3746
+ function slotEventEnd(event) {
3747
+ if (event.end) {
3748
+ return cloneDate(event.end);
3749
+ }else{
3750
+ return addMinutes(cloneDate(event.start), opt('defaultEventMinutes'));
3751
+ }
3752
+ }
3753
+
3754
+
3755
+ // renders events in the 'time slots' at the bottom
3756
+
3757
+ function renderSlotSegs(segs, modifiedEventId) {
3758
+
3759
+ var i, segCnt=segs.length, seg,
3760
+ event,
3761
+ classes,
3762
+ top, bottom,
3763
+ colI, levelI, forward,
3764
+ leftmost,
3765
+ availWidth,
3766
+ outerWidth,
3767
+ left,
3768
+ html='',
3769
+ eventElements,
3770
+ eventElement,
3771
+ triggerRes,
3772
+ vsideCache={},
3773
+ hsideCache={},
3774
+ key, val,
3775
+ contentElement,
3776
+ height,
3777
+ slotSegmentContainer = getSlotSegmentContainer(),
3778
+ rtl, dis, dit,
3779
+ colCnt = getColCnt();
3780
+
3781
+ if (rtl = opt('isRTL')) {
3782
+ dis = -1;
3783
+ dit = colCnt - 1;
3784
+ }else{
3785
+ dis = 1;
3786
+ dit = 0;
3787
+ }
3788
+
3789
+ // calculate position/dimensions, create html
3790
+ for (i=0; i<segCnt; i++) {
3791
+ seg = segs[i];
3792
+ event = seg.event;
3793
+ top = timePosition(seg.start, seg.start);
3794
+ bottom = timePosition(seg.start, seg.end);
3795
+ colI = seg.col;
3796
+ levelI = seg.level;
3797
+ forward = seg.forward || 0;
3798
+ leftmost = colContentLeft(colI*dis + dit);
3799
+ availWidth = colContentRight(colI*dis + dit) - leftmost;
3800
+ availWidth = Math.min(availWidth-6, availWidth*.95); // TODO: move this to CSS
3801
+ if (levelI) {
3802
+ // indented and thin
3803
+ outerWidth = availWidth / (levelI + forward + 1);
3804
+ }else{
3805
+ if (forward) {
3806
+ // moderately wide, aligned left still
3807
+ outerWidth = ((availWidth / (forward + 1)) - (12/2)) * 2; // 12 is the predicted width of resizer =
3808
+ }else{
3809
+ // can be entire width, aligned left
3810
+ outerWidth = availWidth;
3811
+ }
3812
+ }
3813
+ left = leftmost + // leftmost possible
3814
+ (availWidth / (levelI + forward + 1) * levelI) // indentation
3815
+ * dis + (rtl ? availWidth - outerWidth : 0); // rtl
3816
+ seg.top = top;
3817
+ seg.left = left;
3818
+ seg.outerWidth = outerWidth;
3819
+ seg.outerHeight = bottom - top;
3820
+ html += slotSegHtml(event, seg);
3821
+ }
3822
+ slotSegmentContainer[0].innerHTML = html; // faster than html()
3823
+ eventElements = slotSegmentContainer.children();
3824
+
3825
+ // retrieve elements, run through eventRender callback, bind event handlers
3826
+ for (i=0; i<segCnt; i++) {
3827
+ seg = segs[i];
3828
+ event = seg.event;
3829
+ eventElement = $(eventElements[i]); // faster than eq()
3830
+ triggerRes = trigger('eventRender', event, event, eventElement,t);
3831
+ if (triggerRes === false) {
3832
+ eventElement.remove();
3833
+ }else{
3834
+ if (triggerRes && triggerRes !== true) {
3835
+ eventElement.remove();
3836
+ eventElement = $(triggerRes)
3837
+ .css({
3838
+ position: 'absolute',
3839
+ top: seg.top,
3840
+ left: seg.left
3841
+ })
3842
+ .appendTo(slotSegmentContainer);
3843
+ }
3844
+ seg.element = eventElement;
3845
+ if (event._id === modifiedEventId) {
3846
+ bindSlotSeg(event, eventElement, seg);
3847
+ }else{
3848
+ eventElement[0]._fci = i; // for lazySegBind
3849
+ }
3850
+ reportEventElement(event, eventElement);
3851
+ }
3852
+ }
3853
+
3854
+ lazySegBind(slotSegmentContainer, segs, bindSlotSeg);
3855
+
3856
+ // record event sides and title positions
3857
+ for (i=0; i<segCnt; i++) {
3858
+ seg = segs[i];
3859
+ if (eventElement = seg.element) {
3860
+ val = vsideCache[key = seg.key = cssKey(eventElement[0])];
3861
+ seg.vsides = val === undefined ? (vsideCache[key] = vsides(eventElement, true)) : val;
3862
+ val = hsideCache[key];
3863
+ seg.hsides = val === undefined ? (hsideCache[key] = hsides(eventElement, true)) : val;
3864
+ contentElement = eventElement.find('div.fc-event-content');
3865
+ if (contentElement.length) {
3866
+ seg.contentTop = contentElement[0].offsetTop;
3867
+ }
3868
+ }
3869
+ }
3870
+
3871
+ // set all positions/dimensions at once
3872
+ for (i=0; i<segCnt; i++) {
3873
+ seg = segs[i];
3874
+ if (eventElement = seg.element) {
3875
+ eventElement[0].style.width = Math.max(0, seg.outerWidth - seg.hsides) + 'px';
3876
+ height = Math.max(0, seg.outerHeight - seg.vsides);
3877
+ eventElement[0].style.height = height + 'px';
3878
+ event = seg.event;
3879
+ if (seg.contentTop !== undefined && height - seg.contentTop < 10) {
3880
+ // not enough room for title, put it in the time header
3881
+ eventElement.find('div.fc-event-time')
3882
+ .text(formatDate(event.start, opt('timeFormat')) + ' - ' + event.title);
3883
+ eventElement.find('div.fc-event-title')
3884
+ .remove();
3885
+ }
3886
+ trigger('eventAfterRender', event, event, eventElement);
3887
+ }
3888
+ }
3889
+
3890
+ }
3891
+
3892
+
3893
+ function slotSegHtml(event, seg) {
3894
+ var html = "<";
3895
+ var url = event.url;
3896
+ var skinCss = getSkinCss(event, opt);
3897
+ var skinCssAttr = (skinCss ? " style='" + skinCss + "'" : '');
3898
+ var classes = ['fc-event', 'fc-event-skin', 'fc-event-vert'];
3899
+ if (isEventDraggable(event)) {
3900
+ classes.push('fc-event-draggable');
3901
+ }
3902
+ if (seg.isStart) {
3903
+ classes.push('fc-corner-top');
3904
+ }
3905
+ if (seg.isEnd) {
3906
+ classes.push('fc-corner-bottom');
3907
+ }
3908
+ classes = classes.concat(event.className);
3909
+ if (event.source) {
3910
+ classes = classes.concat(event.source.className || []);
3911
+ }
3912
+ if (url) {
3913
+ html += "a href='" + htmlEscape(event.url) + "'";
3914
+ }else{
3915
+ html += "div";
3916
+ }
3917
+ html +=
3918
+ " class='" + classes.join(' ') + "'" +
3919
+ " style='position:absolute;z-index:8;top:" + seg.top + "px;left:" + seg.left + "px;" + skinCss + "'" +
3920
+ ">" +
3921
+ "<div class='fc-event-inner fc-event-skin'" + skinCssAttr + ">" +
3922
+ "<div class='fc-event-head fc-event-skin'" + skinCssAttr + ">" +
3923
+ "<div class='fc-event-time'>" +
3924
+ htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) +
3925
+ "</div>" +
3926
+ "</div>" +
3927
+ "<div class='fc-event-content'>" +
3928
+ "<div class='fc-event-title'>" +
3929
+ htmlEscape(event.title) +
3930
+ "</div>" +
3931
+ "</div>" +
3932
+ "<div class='fc-event-bg'></div>" +
3933
+ "</div>"; // close inner
3934
+ if (seg.isEnd && isEventResizable(event)) {
3935
+ html +=
3936
+ "<div class='ui-resizable-handle ui-resizable-s'>=</div>";
3937
+ }
3938
+ html +=
3939
+ "</" + (url ? "a" : "div") + ">";
3940
+ return html;
3941
+ }
3942
+
3943
+
3944
+ function bindDaySeg(event, eventElement, seg) {
3945
+ if (isEventDraggable(event)) {
3946
+ draggableDayEvent(event, eventElement, seg.isStart);
3947
+ }
3948
+ if (seg.isEnd && isEventResizable(event)) {
3949
+ resizableDayEvent(event, eventElement, seg);
3950
+ }
3951
+ eventElementHandlers(event, eventElement);
3952
+ // needs to be after, because resizableDayEvent might stopImmediatePropagation on click
3953
+ }
3954
+
3955
+
3956
+ function bindSlotSeg(event, eventElement, seg) {
3957
+ var timeElement = eventElement.find('div.fc-event-time');
3958
+ if (isEventDraggable(event)) {
3959
+ draggableSlotEvent(event, eventElement, timeElement);
3960
+ }
3961
+ if (seg.isEnd && isEventResizable(event)) {
3962
+ resizableSlotEvent(event, eventElement, timeElement);
3963
+ }
3964
+ eventElementHandlers(event, eventElement);
3965
+ }
3966
+
3967
+
3968
+
3969
+ /* Dragging
3970
+ -----------------------------------------------------------------------------------*/
3971
+
3972
+
3973
+ // when event starts out FULL-DAY
3974
+
3975
+ function draggableDayEvent(event, eventElement, isStart) {
3976
+ var origWidth;
3977
+ var revert;
3978
+ var allDay=true;
3979
+ var dayDelta;
3980
+ var dis = opt('isRTL') ? -1 : 1;
3981
+ var hoverListener = getHoverListener();
3982
+ var colWidth = getColWidth();
3983
+ var slotHeight = getSlotHeight();
3984
+ var minMinute = getMinMinute();
3985
+ eventElement.draggable({
3986
+ zIndex: 9,
3987
+ opacity: opt('dragOpacity', 'month'), // use whatever the month view was using
3988
+ revertDuration: opt('dragRevertDuration'),
3989
+ start: function(ev, ui) {
3990
+ trigger('eventDragStart', eventElement, event, ev, ui);
3991
+ hideEvents(event, eventElement);
3992
+ origWidth = eventElement.width();
3993
+ hoverListener.start(function(cell, origCell, rowDelta, colDelta) {
3994
+ clearOverlays();
3995
+ if (cell) {
3996
+ //setOverflowHidden(true);
3997
+ revert = false;
3998
+ dayDelta = colDelta * dis;
3999
+ if (!cell.row) {
4000
+ // on full-days
4001
+ renderDayOverlay(
4002
+ addDays(cloneDate(event.start), dayDelta),
4003
+ addDays(exclEndDay(event), dayDelta)
4004
+ );
4005
+ resetElement();
4006
+ }else{
4007
+ // mouse is over bottom slots
4008
+ if (isStart) {
4009
+ if (allDay) {
4010
+ // convert event to temporary slot-event
4011
+ eventElement.width(colWidth - 10); // don't use entire width
4012
+ setOuterHeight(
4013
+ eventElement,
4014
+ slotHeight * Math.round(
4015
+ (event.end ? ((event.end - event.start) / MINUTE_MS) : opt('defaultEventMinutes'))
4016
+ / opt('slotMinutes')
4017
+ )
4018
+ );
4019
+ eventElement.draggable('option', 'grid', [colWidth, 1]);
4020
+ allDay = false;
4021
+ }
4022
+ }else{
4023
+ revert = true;
4024
+ }
4025
+ }
4026
+ revert = revert || (allDay && !dayDelta);
4027
+ }else{
4028
+ resetElement();
4029
+ //setOverflowHidden(false);
4030
+ revert = true;
4031
+ }
4032
+ eventElement.draggable('option', 'revert', revert);
4033
+ }, ev, 'drag');
4034
+ },
4035
+ stop: function(ev, ui) {
4036
+ hoverListener.stop();
4037
+ clearOverlays();
4038
+ trigger('eventDragStop', eventElement, event, ev, ui);
4039
+ if (revert) {
4040
+ // hasn't moved or is out of bounds (draggable has already reverted)
4041
+ resetElement();
4042
+ eventElement.css('filter', ''); // clear IE opacity side-effects
4043
+ showEvents(event, eventElement);
4044
+ }else{
4045
+ // changed!
4046
+ var minuteDelta = 0;
4047
+ if (!allDay) {
4048
+ minuteDelta = Math.round((eventElement.offset().top - getBodyContent().offset().top) / slotHeight)
4049
+ * opt('slotMinutes')
4050
+ + minMinute
4051
+ - (event.start.getHours() * 60 + event.start.getMinutes());
4052
+ }
4053
+ eventDrop(this, event, dayDelta, minuteDelta, allDay, ev, ui);
4054
+ }
4055
+ //setOverflowHidden(false);
4056
+ }
4057
+ });
4058
+ function resetElement() {
4059
+ if (!allDay) {
4060
+ eventElement
4061
+ .width(origWidth)
4062
+ .height('')
4063
+ .draggable('option', 'grid', null);
4064
+ allDay = true;
4065
+ }
4066
+ }
4067
+ }
4068
+
4069
+
4070
+ // when event starts out IN TIMESLOTS
4071
+
4072
+ function draggableSlotEvent(event, eventElement, timeElement) {
4073
+ var origPosition;
4074
+ var allDay=false;
4075
+ var dayDelta;
4076
+ var minuteDelta;
4077
+ var prevMinuteDelta;
4078
+ var dis = opt('isRTL') ? -1 : 1;
4079
+ var hoverListener = getHoverListener();
4080
+ var colCnt = getColCnt();
4081
+ var colWidth = getColWidth();
4082
+ var slotHeight = getSlotHeight();
4083
+ eventElement.draggable({
4084
+ zIndex: 9,
4085
+ scroll: false,
4086
+ grid: [colWidth, slotHeight],
4087
+ axis: colCnt==1 ? 'y' : false,
4088
+ opacity: opt('dragOpacity'),
4089
+ revertDuration: opt('dragRevertDuration'),
4090
+ start: function(ev, ui) {
4091
+ trigger('eventDragStart', eventElement, event, ev, ui);
4092
+ hideEvents(event, eventElement);
4093
+ origPosition = eventElement.position();
4094
+ minuteDelta = prevMinuteDelta = 0;
4095
+ hoverListener.start(function(cell, origCell, rowDelta, colDelta) {
4096
+ eventElement.draggable('option', 'revert', !cell);
4097
+ clearOverlays();
4098
+ if (cell) {
4099
+ dayDelta = colDelta * dis;
4100
+ if (opt('allDaySlot') && !cell.row) {
4101
+ // over full days
4102
+ if (!allDay) {
4103
+ // convert to temporary all-day event
4104
+ allDay = true;
4105
+ timeElement.hide();
4106
+ eventElement.draggable('option', 'grid', null);
4107
+ }
4108
+ renderDayOverlay(
4109
+ addDays(cloneDate(event.start), dayDelta),
4110
+ addDays(exclEndDay(event), dayDelta)
4111
+ );
4112
+ }else{
4113
+ // on slots
4114
+ resetElement();
4115
+ }
4116
+ }
4117
+ }, ev, 'drag');
4118
+ },
4119
+ drag: function(ev, ui) {
4120
+ minuteDelta = Math.round((ui.position.top - origPosition.top) / slotHeight) * opt('slotMinutes');
4121
+ if (minuteDelta != prevMinuteDelta) {
4122
+ if (!allDay) {
4123
+ updateTimeText(minuteDelta);
4124
+ }
4125
+ prevMinuteDelta = minuteDelta;
4126
+ }
4127
+ },
4128
+ stop: function(ev, ui) {
4129
+ var cell = hoverListener.stop();
4130
+ clearOverlays();
4131
+ trigger('eventDragStop', eventElement, event, ev, ui);
4132
+ if (cell && (dayDelta || minuteDelta || allDay)) {
4133
+ // changed!
4134
+ eventDrop(this, event, dayDelta, allDay ? 0 : minuteDelta, allDay, ev, ui);
4135
+ }else{
4136
+ // either no change or out-of-bounds (draggable has already reverted)
4137
+ resetElement();
4138
+ eventElement.css('filter', ''); // clear IE opacity side-effects
4139
+ eventElement.css(origPosition); // sometimes fast drags make event revert to wrong position
4140
+ updateTimeText(0);
4141
+ showEvents(event, eventElement);
4142
+ }
4143
+ }
4144
+ });
4145
+ function updateTimeText(minuteDelta) {
4146
+ var newStart = addMinutes(cloneDate(event.start), minuteDelta);
4147
+ var newEnd;
4148
+ if (event.end) {
4149
+ newEnd = addMinutes(cloneDate(event.end), minuteDelta);
4150
+ }
4151
+ timeElement.text(formatDates(newStart, newEnd, opt('timeFormat')));
4152
+ }
4153
+ function resetElement() {
4154
+ // convert back to original slot-event
4155
+ if (allDay) {
4156
+ timeElement.css('display', ''); // show() was causing display=inline
4157
+ eventElement.draggable('option', 'grid', [colWidth, slotHeight]);
4158
+ allDay = false;
4159
+ }
4160
+ }
4161
+ }
4162
+
4163
+
4164
+
4165
+ /* Resizing
4166
+ --------------------------------------------------------------------------------------*/
4167
+
4168
+
4169
+ function resizableSlotEvent(event, eventElement, timeElement) {
4170
+ var slotDelta, prevSlotDelta;
4171
+ var slotHeight = getSlotHeight();
4172
+ eventElement.resizable({
4173
+ handles: {
4174
+ s: 'div.ui-resizable-s'
4175
+ },
4176
+ grid: slotHeight,
4177
+ start: function(ev, ui) {
4178
+ slotDelta = prevSlotDelta = 0;
4179
+ hideEvents(event, eventElement);
4180
+ eventElement.css('z-index', 9);
4181
+ trigger('eventResizeStart', this, event, ev, ui);
4182
+ },
4183
+ resize: function(ev, ui) {
4184
+ // don't rely on ui.size.height, doesn't take grid into account
4185
+ slotDelta = Math.round((Math.max(slotHeight, eventElement.height()) - ui.originalSize.height) / slotHeight);
4186
+ if (slotDelta != prevSlotDelta) {
4187
+ timeElement.text(
4188
+ formatDates(
4189
+ event.start,
4190
+ (!slotDelta && !event.end) ? null : // no change, so don't display time range
4191
+ addMinutes(eventEnd(event), opt('slotMinutes')*slotDelta),
4192
+ opt('timeFormat')
4193
+ )
4194
+ );
4195
+ prevSlotDelta = slotDelta;
4196
+ }
4197
+ },
4198
+ stop: function(ev, ui) {
4199
+ trigger('eventResizeStop', this, event, ev, ui);
4200
+ if (slotDelta) {
4201
+ eventResize(this, event, 0, opt('slotMinutes')*slotDelta, ev, ui);
4202
+ }else{
4203
+ eventElement.css('z-index', 8);
4204
+ showEvents(event, eventElement);
4205
+ // BUG: if event was really short, need to put title back in span
4206
+ }
4207
+ }
4208
+ });
4209
+ }
4210
+
4211
+
4212
+ }
4213
+
4214
+
4215
+ function countForwardSegs(levels) {
4216
+ var i, j, k, level, segForward, segBack;
4217
+ for (i=levels.length-1; i>0; i--) {
4218
+ level = levels[i];
4219
+ for (j=0; j<level.length; j++) {
4220
+ segForward = level[j];
4221
+ for (k=0; k<levels[i-1].length; k++) {
4222
+ segBack = levels[i-1][k];
4223
+ if (segsCollide(segForward, segBack)) {
4224
+ segBack.forward = Math.max(segBack.forward||0, (segForward.forward||0)+1);
4225
+ }
4226
+ }
4227
+ }
4228
+ }
4229
+ }
4230
+
4231
+
4232
+
4233
+
4234
+ function View(element, calendar, viewName) {
4235
+ var t = this;
4236
+
4237
+
4238
+ // exports
4239
+ t.element = element;
4240
+ t.calendar = calendar;
4241
+ t.name = viewName;
4242
+ t.opt = opt;
4243
+ t.trigger = trigger;
4244
+ //t.setOverflowHidden = setOverflowHidden;
4245
+ t.isEventDraggable = isEventDraggable;
4246
+ t.isEventResizable = isEventResizable;
4247
+ t.reportEvents = reportEvents;
4248
+ t.eventEnd = eventEnd;
4249
+ t.reportEventElement = reportEventElement;
4250
+ t.reportEventClear = reportEventClear;
4251
+ t.eventElementHandlers = eventElementHandlers;
4252
+ t.showEvents = showEvents;
4253
+ t.hideEvents = hideEvents;
4254
+ t.eventDrop = eventDrop;
4255
+ t.eventResize = eventResize;
4256
+ // t.title
4257
+ // t.start, t.end
4258
+ // t.visStart, t.visEnd
4259
+
4260
+
4261
+ // imports
4262
+ var defaultEventEnd = t.defaultEventEnd;
4263
+ var normalizeEvent = calendar.normalizeEvent; // in EventManager
4264
+ var reportEventChange = calendar.reportEventChange;
4265
+
4266
+
4267
+ // locals
4268
+ var eventsByID = {};
4269
+ var eventElements = [];
4270
+ var eventElementsByID = {};
4271
+ var options = calendar.options;
4272
+
4273
+
4274
+
4275
+ function opt(name, viewNameOverride) {
4276
+ var v = options[name];
4277
+ if (typeof v == 'object') {
4278
+ return smartProperty(v, viewNameOverride || viewName);
4279
+ }
4280
+ return v;
4281
+ }
4282
+
4283
+
4284
+ function trigger(name, thisObj) {
4285
+ return calendar.trigger.apply(
4286
+ calendar,
4287
+ [name, thisObj || t].concat(Array.prototype.slice.call(arguments, 2), [t])
4288
+ );
4289
+ }
4290
+
4291
+
4292
+ /*
4293
+ function setOverflowHidden(bool) {
4294
+ element.css('overflow', bool ? 'hidden' : '');
4295
+ }
4296
+ */
4297
+
4298
+
4299
+ function isEventDraggable(event) {
4300
+ return isEventEditable(event) && !opt('disableDragging');
4301
+ }
4302
+
4303
+
4304
+ function isEventResizable(event) { // but also need to make sure the seg.isEnd == true
4305
+ return isEventEditable(event) && !opt('disableResizing');
4306
+ }
4307
+
4308
+
4309
+ function isEventEditable(event) {
4310
+ return firstDefined(event.editable, (event.source || {}).editable, opt('editable'));
4311
+ }
4312
+
4313
+
4314
+
4315
+ /* Event Data
4316
+ ------------------------------------------------------------------------------*/
4317
+
4318
+
4319
+ // report when view receives new events
4320
+ function reportEvents(events) { // events are already normalized at this point
4321
+ eventsByID = {};
4322
+ var i, len=events.length, event;
4323
+ for (i=0; i<len; i++) {
4324
+ event = events[i];
4325
+ if (eventsByID[event._id]) {
4326
+ eventsByID[event._id].push(event);
4327
+ }else{
4328
+ eventsByID[event._id] = [event];
4329
+ }
4330
+ }
4331
+ }
4332
+
4333
+
4334
+ // returns a Date object for an event's end
4335
+ function eventEnd(event) {
4336
+ return event.end ? cloneDate(event.end) : defaultEventEnd(event);
4337
+ }
4338
+
4339
+
4340
+
4341
+ /* Event Elements
4342
+ ------------------------------------------------------------------------------*/
4343
+
4344
+
4345
+ // report when view creates an element for an event
4346
+ function reportEventElement(event, element) {
4347
+ eventElements.push(element);
4348
+ if (eventElementsByID[event._id]) {
4349
+ eventElementsByID[event._id].push(element);
4350
+ }else{
4351
+ eventElementsByID[event._id] = [element];
4352
+ }
4353
+ }
4354
+
4355
+
4356
+ function reportEventClear() {
4357
+ eventElements = [];
4358
+ eventElementsByID = {};
4359
+ }
4360
+
4361
+
4362
+ // attaches eventClick, eventMouseover, eventMouseout
4363
+ function eventElementHandlers(event, eventElement) {
4364
+ eventElement
4365
+ .click(function(ev) {
4366
+ if (!eventElement.hasClass('ui-draggable-dragging') &&
4367
+ !eventElement.hasClass('ui-resizable-resizing')) {
4368
+ return trigger('eventClick', this, event, ev);
4369
+ }
4370
+ })
4371
+ .hover(
4372
+ function(ev) {
4373
+ trigger('eventMouseover', this, event, ev);
4374
+ },
4375
+ function(ev) {
4376
+ trigger('eventMouseout', this, event, ev);
4377
+ }
4378
+ );
4379
+ // TODO: don't fire eventMouseover/eventMouseout *while* dragging is occuring (on subject element)
4380
+ // TODO: same for resizing
4381
+ }
4382
+
4383
+
4384
+ function showEvents(event, exceptElement) {
4385
+ eachEventElement(event, exceptElement, 'show');
4386
+ }
4387
+
4388
+
4389
+ function hideEvents(event, exceptElement) {
4390
+ eachEventElement(event, exceptElement, 'hide');
4391
+ }
4392
+
4393
+
4394
+ function eachEventElement(event, exceptElement, funcName) {
4395
+ var elements = eventElementsByID[event._id],
4396
+ i, len = elements.length;
4397
+ for (i=0; i<len; i++) {
4398
+ if (!exceptElement || elements[i][0] != exceptElement[0]) {
4399
+ elements[i][funcName]();
4400
+ }
4401
+ }
4402
+ }
4403
+
4404
+
4405
+
4406
+ /* Event Modification Reporting
4407
+ ---------------------------------------------------------------------------------*/
4408
+
4409
+
4410
+ function eventDrop(e, event, dayDelta, minuteDelta, allDay, ev, ui) {
4411
+ var oldAllDay = event.allDay;
4412
+ var eventId = event._id;
4413
+ moveEvents(eventsByID[eventId], dayDelta, minuteDelta, allDay);
4414
+ trigger(
4415
+ 'eventDrop',
4416
+ e,
4417
+ event,
4418
+ dayDelta,
4419
+ minuteDelta,
4420
+ allDay,
4421
+ function() {
4422
+ // TODO: investigate cases where this inverse technique might not work
4423
+ moveEvents(eventsByID[eventId], -dayDelta, -minuteDelta, oldAllDay);
4424
+ reportEventChange(eventId);
4425
+ },
4426
+ ev,
4427
+ ui
4428
+ );
4429
+ reportEventChange(eventId);
4430
+ }
4431
+
4432
+
4433
+ function eventResize(e, event, dayDelta, minuteDelta, ev, ui) {
4434
+ var eventId = event._id;
4435
+ elongateEvents(eventsByID[eventId], dayDelta, minuteDelta);
4436
+ trigger(
4437
+ 'eventResize',
4438
+ e,
4439
+ event,
4440
+ dayDelta,
4441
+ minuteDelta,
4442
+ function() {
4443
+ // TODO: investigate cases where this inverse technique might not work
4444
+ elongateEvents(eventsByID[eventId], -dayDelta, -minuteDelta);
4445
+ reportEventChange(eventId);
4446
+ },
4447
+ ev,
4448
+ ui
4449
+ );
4450
+ reportEventChange(eventId);
4451
+ }
4452
+
4453
+
4454
+
4455
+ /* Event Modification Math
4456
+ ---------------------------------------------------------------------------------*/
4457
+
4458
+
4459
+ function moveEvents(events, dayDelta, minuteDelta, allDay) {
4460
+ minuteDelta = minuteDelta || 0;
4461
+ for (var e, len=events.length, i=0; i<len; i++) {
4462
+ e = events[i];
4463
+ if (allDay !== undefined) {
4464
+ e.allDay = allDay;
4465
+ }
4466
+ addMinutes(addDays(e.start, dayDelta, true), minuteDelta);
4467
+ if (e.end) {
4468
+ e.end = addMinutes(addDays(e.end, dayDelta, true), minuteDelta);
4469
+ }
4470
+ normalizeEvent(e, options);
4471
+ }
4472
+ }
4473
+
4474
+
4475
+ function elongateEvents(events, dayDelta, minuteDelta) {
4476
+ minuteDelta = minuteDelta || 0;
4477
+ for (var e, len=events.length, i=0; i<len; i++) {
4478
+ e = events[i];
4479
+ e.end = addMinutes(addDays(eventEnd(e), dayDelta, true), minuteDelta);
4480
+ normalizeEvent(e, options);
4481
+ }
4482
+ }
4483
+
4484
+
4485
+ }
4486
+
4487
+ function DayEventRenderer() {
4488
+ var t = this;
4489
+
4490
+
4491
+ // exports
4492
+ t.renderDaySegs = renderDaySegs;
4493
+ t.resizableDayEvent = resizableDayEvent;
4494
+
4495
+
4496
+ // imports
4497
+ var opt = t.opt;
4498
+ var trigger = t.trigger;
4499
+ var isEventDraggable = t.isEventDraggable;
4500
+ var isEventResizable = t.isEventResizable;
4501
+ var eventEnd = t.eventEnd;
4502
+ var reportEventElement = t.reportEventElement;
4503
+ var showEvents = t.showEvents;
4504
+ var hideEvents = t.hideEvents;
4505
+ var eventResize = t.eventResize;
4506
+ var getRowCnt = t.getRowCnt;
4507
+ var getColCnt = t.getColCnt;
4508
+ var getColWidth = t.getColWidth;
4509
+ var allDayRow = t.allDayRow;
4510
+ var allDayBounds = t.allDayBounds;
4511
+ var colContentLeft = t.colContentLeft;
4512
+ var colContentRight = t.colContentRight;
4513
+ var dayOfWeekCol = t.dayOfWeekCol;
4514
+ var dateCell = t.dateCell;
4515
+ var compileDaySegs = t.compileDaySegs;
4516
+ var getDaySegmentContainer = t.getDaySegmentContainer;
4517
+ var bindDaySeg = t.bindDaySeg; //TODO: streamline this
4518
+ var formatDates = t.calendar.formatDates;
4519
+ var renderDayOverlay = t.renderDayOverlay;
4520
+ var clearOverlays = t.clearOverlays;
4521
+ var clearSelection = t.clearSelection;
4522
+
4523
+
4524
+
4525
+ /* Rendering
4526
+ -----------------------------------------------------------------------------*/
4527
+
4528
+
4529
+ function renderDaySegs(segs, modifiedEventId) {
4530
+ var segmentContainer = getDaySegmentContainer();
4531
+ var rowDivs;
4532
+ var rowCnt = getRowCnt();
4533
+ var colCnt = getColCnt();
4534
+ var i = 0;
4535
+ var rowI;
4536
+ var levelI;
4537
+ var colHeights;
4538
+ var j;
4539
+ var segCnt = segs.length;
4540
+ var seg;
4541
+ var top;
4542
+ var k;
4543
+ segmentContainer[0].innerHTML = daySegHTML(segs); // faster than .html()
4544
+ daySegElementResolve(segs, segmentContainer.children());
4545
+ daySegElementReport(segs);
4546
+ daySegHandlers(segs, segmentContainer, modifiedEventId);
4547
+ daySegCalcHSides(segs);
4548
+ daySegSetWidths(segs);
4549
+ daySegCalcHeights(segs);
4550
+ rowDivs = getRowDivs();
4551
+ // set row heights, calculate event tops (in relation to row top)
4552
+ for (rowI=0; rowI<rowCnt; rowI++) {
4553
+ levelI = 0;
4554
+ colHeights = [];
4555
+ for (j=0; j<colCnt; j++) {
4556
+ colHeights[j] = 0;
4557
+ }
4558
+ while (i<segCnt && (seg = segs[i]).row == rowI) {
4559
+ // loop through segs in a row
4560
+ top = arrayMax(colHeights.slice(seg.startCol, seg.endCol));
4561
+ seg.top = top;
4562
+ top += seg.outerHeight;
4563
+ for (k=seg.startCol; k<seg.endCol; k++) {
4564
+ colHeights[k] = top;
4565
+ }
4566
+ i++;
4567
+ }
4568
+ rowDivs[rowI].height(arrayMax(colHeights));
4569
+ }
4570
+ daySegSetTops(segs, getRowTops(rowDivs));
4571
+ }
4572
+
4573
+
4574
+ function renderTempDaySegs(segs, adjustRow, adjustTop) {
4575
+ var tempContainer = $("<div/>");
4576
+ var elements;
4577
+ var segmentContainer = getDaySegmentContainer();
4578
+ var i;
4579
+ var segCnt = segs.length;
4580
+ var element;
4581
+ tempContainer[0].innerHTML = daySegHTML(segs); // faster than .html()
4582
+ elements = tempContainer.children();
4583
+ segmentContainer.append(elements);
4584
+ daySegElementResolve(segs, elements);
4585
+ daySegCalcHSides(segs);
4586
+ daySegSetWidths(segs);
4587
+ daySegCalcHeights(segs);
4588
+ daySegSetTops(segs, getRowTops(getRowDivs()));
4589
+ elements = [];
4590
+ for (i=0; i<segCnt; i++) {
4591
+ element = segs[i].element;
4592
+ if (element) {
4593
+ if (segs[i].row === adjustRow) {
4594
+ element.css('top', adjustTop);
4595
+ }
4596
+ elements.push(element[0]);
4597
+ }
4598
+ }
4599
+ return $(elements);
4600
+ }
4601
+
4602
+
4603
+ function daySegHTML(segs) { // also sets seg.left and seg.outerWidth
4604
+ var rtl = opt('isRTL');
4605
+ var i;
4606
+ var segCnt=segs.length;
4607
+ var seg;
4608
+ var event;
4609
+ var url;
4610
+ var classes;
4611
+ var bounds = allDayBounds();
4612
+ var minLeft = bounds.left;
4613
+ var maxLeft = bounds.right;
4614
+ var leftCol;
4615
+ var rightCol;
4616
+ var left;
4617
+ var right;
4618
+ var skinCss;
4619
+ var html = '';
4620
+ // calculate desired position/dimensions, create html
4621
+ for (i=0; i<segCnt; i++) {
4622
+ seg = segs[i];
4623
+ event = seg.event;
4624
+ classes = ['fc-event', 'fc-event-skin', 'fc-event-hori'];
4625
+ if (isEventDraggable(event)) {
4626
+ classes.push('fc-event-draggable');
4627
+ }
4628
+ if (rtl) {
4629
+ if (seg.isStart) {
4630
+ classes.push('fc-corner-right');
4631
+ }
4632
+ if (seg.isEnd) {
4633
+ classes.push('fc-corner-left');
4634
+ }
4635
+ leftCol = dayOfWeekCol(seg.end.getDay()-1);
4636
+ rightCol = dayOfWeekCol(seg.start.getDay());
4637
+ left = seg.isEnd ? colContentLeft(leftCol) : minLeft;
4638
+ right = seg.isStart ? colContentRight(rightCol) : maxLeft;
4639
+ }else{
4640
+ if (seg.isStart) {
4641
+ classes.push('fc-corner-left');
4642
+ }
4643
+ if (seg.isEnd) {
4644
+ classes.push('fc-corner-right');
4645
+ }
4646
+ leftCol = dayOfWeekCol(seg.start.getDay());
4647
+ rightCol = dayOfWeekCol(seg.end.getDay()-1);
4648
+ left = seg.isStart ? colContentLeft(leftCol) : minLeft;
4649
+ right = seg.isEnd ? colContentRight(rightCol) : maxLeft;
4650
+ }
4651
+ classes = classes.concat(event.className);
4652
+ if (event.source) {
4653
+ classes = classes.concat(event.source.className || []);
4654
+ }
4655
+ url = event.url;
4656
+ skinCss = getSkinCss(event, opt);
4657
+ if (url) {
4658
+ html += "<a href='" + htmlEscape(url) + "'";
4659
+ }else{
4660
+ html += "<div";
4661
+ }
4662
+ html +=
4663
+ " class='" + classes.join(' ') + "'" +
4664
+ " style='position:absolute;z-index:8;left:"+left+"px;" + skinCss + "'" +
4665
+ ">" +
4666
+ "<div" +
4667
+ " class='fc-event-inner fc-event-skin'" +
4668
+ (skinCss ? " style='" + skinCss + "'" : '') +
4669
+ ">";
4670
+ if (!event.allDay && seg.isStart) {
4671
+ html +=
4672
+ "<span class='fc-event-time'>" +
4673
+ htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) +
4674
+ "</span>";
4675
+ }
4676
+ html +=
4677
+ "<span class='fc-event-title'>" + htmlEscape(event.title) + "</span>" +
4678
+ "</div>";
4679
+ if (seg.isEnd && isEventResizable(event)) {
4680
+ html +=
4681
+ "<div class='ui-resizable-handle ui-resizable-" + (rtl ? 'w' : 'e') + "'>" +
4682
+ "&nbsp;&nbsp;&nbsp;" + // makes hit area a lot better for IE6/7
4683
+ "</div>";
4684
+ }
4685
+ html +=
4686
+ "</" + (url ? "a" : "div" ) + ">";
4687
+ seg.left = left;
4688
+ seg.outerWidth = right - left;
4689
+ seg.startCol = leftCol;
4690
+ seg.endCol = rightCol + 1; // needs to be exclusive
4691
+ }
4692
+ return html;
4693
+ }
4694
+
4695
+
4696
+ function daySegElementResolve(segs, elements) { // sets seg.element
4697
+ var i;
4698
+ var segCnt = segs.length;
4699
+ var seg;
4700
+ var event;
4701
+ var element;
4702
+ var triggerRes;
4703
+ for (i=0; i<segCnt; i++) {
4704
+ seg = segs[i];
4705
+ event = seg.event;
4706
+ element = $(elements[i]); // faster than .eq()
4707
+ triggerRes = trigger('eventRender', event, event, element, t);
4708
+ if (triggerRes === false) {
4709
+ element.remove();
4710
+ }else{
4711
+ if (triggerRes && triggerRes !== true) {
4712
+ triggerRes = $(triggerRes)
4713
+ .css({
4714
+ position: 'absolute',
4715
+ left: seg.left
4716
+ });
4717
+ element.replaceWith(triggerRes);
4718
+ element = triggerRes;
4719
+ }
4720
+ seg.element = element;
4721
+ }
4722
+ }
4723
+ }
4724
+
4725
+
4726
+ function daySegElementReport(segs) {
4727
+ var i;
4728
+ var segCnt = segs.length;
4729
+ var seg;
4730
+ var element;
4731
+ for (i=0; i<segCnt; i++) {
4732
+ seg = segs[i];
4733
+ element = seg.element;
4734
+ if (element) {
4735
+ reportEventElement(seg.event, element);
4736
+ }
4737
+ }
4738
+ }
4739
+
4740
+
4741
+ function daySegHandlers(segs, segmentContainer, modifiedEventId) {
4742
+ var i;
4743
+ var segCnt = segs.length;
4744
+ var seg;
4745
+ var element;
4746
+ var event;
4747
+ // retrieve elements, run through eventRender callback, bind handlers
4748
+ for (i=0; i<segCnt; i++) {
4749
+ seg = segs[i];
4750
+ element = seg.element;
4751
+ if (element) {
4752
+ event = seg.event;
4753
+ if (event._id === modifiedEventId) {
4754
+ bindDaySeg(event, element, seg);
4755
+ }else{
4756
+ element[0]._fci = i; // for lazySegBind
4757
+ }
4758
+ }
4759
+ }
4760
+ lazySegBind(segmentContainer, segs, bindDaySeg);
4761
+ }
4762
+
4763
+
4764
+ function daySegCalcHSides(segs) { // also sets seg.key
4765
+ var i;
4766
+ var segCnt = segs.length;
4767
+ var seg;
4768
+ var element;
4769
+ var key, val;
4770
+ var hsideCache = {};
4771
+ // record event horizontal sides
4772
+ for (i=0; i<segCnt; i++) {
4773
+ seg = segs[i];
4774
+ element = seg.element;
4775
+ if (element) {
4776
+ key = seg.key = cssKey(element[0]);
4777
+ val = hsideCache[key];
4778
+ if (val === undefined) {
4779
+ val = hsideCache[key] = hsides(element, true);
4780
+ }
4781
+ seg.hsides = val;
4782
+ }
4783
+ }
4784
+ }
4785
+
4786
+
4787
+ function daySegSetWidths(segs) {
4788
+ var i;
4789
+ var segCnt = segs.length;
4790
+ var seg;
4791
+ var element;
4792
+ for (i=0; i<segCnt; i++) {
4793
+ seg = segs[i];
4794
+ element = seg.element;
4795
+ if (element) {
4796
+ element[0].style.width = Math.max(0, seg.outerWidth - seg.hsides) + 'px';
4797
+ }
4798
+ }
4799
+ }
4800
+
4801
+
4802
+ function daySegCalcHeights(segs) {
4803
+ var i;
4804
+ var segCnt = segs.length;
4805
+ var seg;
4806
+ var element;
4807
+ var key, val;
4808
+ var vmarginCache = {};
4809
+ // record event heights
4810
+ for (i=0; i<segCnt; i++) {
4811
+ seg = segs[i];
4812
+ element = seg.element;
4813
+ if (element) {
4814
+ key = seg.key; // created in daySegCalcHSides
4815
+ val = vmarginCache[key];
4816
+ if (val === undefined) {
4817
+ val = vmarginCache[key] = vmargins(element);
4818
+ }
4819
+ seg.outerHeight = element[0].offsetHeight + val;
4820
+ }
4821
+ }
4822
+ }
4823
+
4824
+
4825
+ function getRowDivs() {
4826
+ var i;
4827
+ var rowCnt = getRowCnt();
4828
+ var rowDivs = [];
4829
+ for (i=0; i<rowCnt; i++) {
4830
+ rowDivs[i] = allDayRow(i)
4831
+ .find('td:first div.fc-day-content > div'); // optimal selector?
4832
+ }
4833
+ return rowDivs;
4834
+ }
4835
+
4836
+
4837
+ function getRowTops(rowDivs) {
4838
+ var i;
4839
+ var rowCnt = rowDivs.length;
4840
+ var tops = [];
4841
+ for (i=0; i<rowCnt; i++) {
4842
+ tops[i] = rowDivs[i][0].offsetTop; // !!?? but this means the element needs position:relative if in a table cell!!!!
4843
+ }
4844
+ return tops;
4845
+ }
4846
+
4847
+
4848
+ function daySegSetTops(segs, rowTops) { // also triggers eventAfterRender
4849
+ var i;
4850
+ var segCnt = segs.length;
4851
+ var seg;
4852
+ var element;
4853
+ var event;
4854
+ for (i=0; i<segCnt; i++) {
4855
+ seg = segs[i];
4856
+ element = seg.element;
4857
+ if (element) {
4858
+ element[0].style.top = rowTops[seg.row] + (seg.top||0) + 'px';
4859
+ event = seg.event;
4860
+ trigger('eventAfterRender', event, event, element);
4861
+ }
4862
+ }
4863
+ }
4864
+
4865
+
4866
+
4867
+ /* Resizing
4868
+ -----------------------------------------------------------------------------------*/
4869
+
4870
+
4871
+ function resizableDayEvent(event, element, seg) {
4872
+ var rtl = opt('isRTL');
4873
+ var direction = rtl ? 'w' : 'e';
4874
+ var handle = element.find('div.ui-resizable-' + direction);
4875
+ var isResizing = false;
4876
+
4877
+ // TODO: look into using jquery-ui mouse widget for this stuff
4878
+ disableTextSelection(element); // prevent native <a> selection for IE
4879
+ element
4880
+ .mousedown(function(ev) { // prevent native <a> selection for others
4881
+ ev.preventDefault();
4882
+ })
4883
+ .click(function(ev) {
4884
+ if (isResizing) {
4885
+ ev.preventDefault(); // prevent link from being visited (only method that worked in IE6)
4886
+ ev.stopImmediatePropagation(); // prevent fullcalendar eventClick handler from being called
4887
+ // (eventElementHandlers needs to be bound after resizableDayEvent)
4888
+ }
4889
+ });
4890
+
4891
+ handle.mousedown(function(ev) {
4892
+ if (ev.which != 1) {
4893
+ return; // needs to be left mouse button
4894
+ }
4895
+ isResizing = true;
4896
+ var hoverListener = t.getHoverListener();
4897
+ var rowCnt = getRowCnt();
4898
+ var colCnt = getColCnt();
4899
+ var dis = rtl ? -1 : 1;
4900
+ var dit = rtl ? colCnt-1 : 0;
4901
+ var elementTop = element.css('top');
4902
+ var dayDelta;
4903
+ var helpers;
4904
+ var eventCopy = $.extend({}, event);
4905
+ var minCell = dateCell(event.start);
4906
+ clearSelection();
4907
+ $('body')
4908
+ .css('cursor', direction + '-resize')
4909
+ .one('mouseup', mouseup);
4910
+ trigger('eventResizeStart', this, event, ev);
4911
+ hoverListener.start(function(cell, origCell) {
4912
+ if (cell) {
4913
+ var r = Math.max(minCell.row, cell.row);
4914
+ var c = cell.col;
4915
+ if (rowCnt == 1) {
4916
+ r = 0; // hack for all-day area in agenda views
4917
+ }
4918
+ if (r == minCell.row) {
4919
+ if (rtl) {
4920
+ c = Math.min(minCell.col, c);
4921
+ }else{
4922
+ c = Math.max(minCell.col, c);
4923
+ }
4924
+ }
4925
+ dayDelta = (r*7 + c*dis+dit) - (origCell.row*7 + origCell.col*dis+dit);
4926
+ var newEnd = addDays(eventEnd(event), dayDelta, true);
4927
+ if (dayDelta) {
4928
+ eventCopy.end = newEnd;
4929
+ var oldHelpers = helpers;
4930
+ helpers = renderTempDaySegs(compileDaySegs([eventCopy]), seg.row, elementTop);
4931
+ helpers.find('*').css('cursor', direction + '-resize');
4932
+ if (oldHelpers) {
4933
+ oldHelpers.remove();
4934
+ }
4935
+ hideEvents(event);
4936
+ }else{
4937
+ if (helpers) {
4938
+ showEvents(event);
4939
+ helpers.remove();
4940
+ helpers = null;
4941
+ }
4942
+ }
4943
+ clearOverlays();
4944
+ renderDayOverlay(event.start, addDays(cloneDate(newEnd), 1)); // coordinate grid already rebuild at hoverListener.start
4945
+ }
4946
+ }, ev);
4947
+
4948
+ function mouseup(ev) {
4949
+ trigger('eventResizeStop', this, event, ev);
4950
+ $('body').css('cursor', '');
4951
+ hoverListener.stop();
4952
+ clearOverlays();
4953
+ if (dayDelta) {
4954
+ eventResize(this, event, dayDelta, 0, ev);
4955
+ // event redraw will clear helpers
4956
+ }
4957
+ // otherwise, the drag handler already restored the old events
4958
+
4959
+ setTimeout(function() { // make this happen after the element's click event
4960
+ isResizing = false;
4961
+ },0);
4962
+ }
4963
+
4964
+ });
4965
+ }
4966
+
4967
+
4968
+ }
4969
+
4970
+ //BUG: unselect needs to be triggered when events are dragged+dropped
4971
+
4972
+ function SelectionManager() {
4973
+ var t = this;
4974
+
4975
+
4976
+ // exports
4977
+ t.select = select;
4978
+ t.unselect = unselect;
4979
+ t.reportSelection = reportSelection;
4980
+ t.daySelectionMousedown = daySelectionMousedown;
4981
+
4982
+
4983
+ // imports
4984
+ var opt = t.opt;
4985
+ var trigger = t.trigger;
4986
+ var defaultSelectionEnd = t.defaultSelectionEnd;
4987
+ var renderSelection = t.renderSelection;
4988
+ var clearSelection = t.clearSelection;
4989
+
4990
+
4991
+ // locals
4992
+ var selected = false;
4993
+
4994
+
4995
+
4996
+ // unselectAuto
4997
+ if (opt('selectable') && opt('unselectAuto')) {
4998
+ $(document).mousedown(function(ev) {
4999
+ var ignore = opt('unselectCancel');
5000
+ if (ignore) {
5001
+ if ($(ev.target).parents(ignore).length) { // could be optimized to stop after first match
5002
+ return;
5003
+ }
5004
+ }
5005
+ unselect(ev);
5006
+ });
5007
+ }
5008
+
5009
+
5010
+ function select(startDate, endDate, allDay) {
5011
+ unselect();
5012
+ if (!endDate) {
5013
+ endDate = defaultSelectionEnd(startDate, allDay);
5014
+ }
5015
+ renderSelection(startDate, endDate, allDay);
5016
+ reportSelection(startDate, endDate, allDay);
5017
+ }
5018
+
5019
+
5020
+ function unselect(ev) {
5021
+ if (selected) {
5022
+ selected = false;
5023
+ clearSelection();
5024
+ trigger('unselect', null, ev);
5025
+ }
5026
+ }
5027
+
5028
+
5029
+ function reportSelection(startDate, endDate, allDay, ev) {
5030
+ selected = true;
5031
+ trigger('select', null, startDate, endDate, allDay, ev);
5032
+ }
5033
+
5034
+
5035
+ function daySelectionMousedown(ev) { // not really a generic manager method, oh well
5036
+ var cellDate = t.cellDate;
5037
+ var cellIsAllDay = t.cellIsAllDay;
5038
+ var hoverListener = t.getHoverListener();
5039
+ var reportDayClick = t.reportDayClick; // this is hacky and sort of weird
5040
+ if (ev.which == 1 && opt('selectable')) { // which==1 means left mouse button
5041
+ unselect(ev);
5042
+ var _mousedownElement = this;
5043
+ var dates;
5044
+ hoverListener.start(function(cell, origCell) { // TODO: maybe put cellDate/cellIsAllDay info in cell
5045
+ clearSelection();
5046
+ if (cell && cellIsAllDay(cell)) {
5047
+ dates = [ cellDate(origCell), cellDate(cell) ].sort(cmp);
5048
+ renderSelection(dates[0], dates[1], true);
5049
+ }else{
5050
+ dates = null;
5051
+ }
5052
+ }, ev);
5053
+ $(document).one('mouseup', function(ev) {
5054
+ hoverListener.stop();
5055
+ if (dates) {
5056
+ if (+dates[0] == +dates[1]) {
5057
+ reportDayClick(dates[0], true, ev);
5058
+ }
5059
+ reportSelection(dates[0], dates[1], true, ev);
5060
+ }
5061
+ });
5062
+ }
5063
+ }
5064
+
5065
+
5066
+ }
5067
+
5068
+ function OverlayManager() {
5069
+ var t = this;
5070
+
5071
+
5072
+ // exports
5073
+ t.renderOverlay = renderOverlay;
5074
+ t.clearOverlays = clearOverlays;
5075
+
5076
+
5077
+ // locals
5078
+ var usedOverlays = [];
5079
+ var unusedOverlays = [];
5080
+
5081
+
5082
+ function renderOverlay(rect, parent) {
5083
+ var e = unusedOverlays.shift();
5084
+ if (!e) {
5085
+ e = $("<div class='fc-cell-overlay' style='position:absolute;z-index:3'/>");
5086
+ }
5087
+ if (e[0].parentNode != parent[0]) {
5088
+ e.appendTo(parent);
5089
+ }
5090
+ usedOverlays.push(e.css(rect).show());
5091
+ return e;
5092
+ }
5093
+
5094
+
5095
+ function clearOverlays() {
5096
+ var e;
5097
+ while (e = usedOverlays.shift()) {
5098
+ unusedOverlays.push(e.hide().unbind());
5099
+ }
5100
+ }
5101
+
5102
+
5103
+ }
5104
+
5105
+ function CoordinateGrid(buildFunc) {
5106
+
5107
+ var t = this;
5108
+ var rows;
5109
+ var cols;
5110
+
5111
+
5112
+ t.build = function() {
5113
+ rows = [];
5114
+ cols = [];
5115
+ buildFunc(rows, cols);
5116
+ };
5117
+
5118
+
5119
+ t.cell = function(x, y) {
5120
+ var rowCnt = rows.length;
5121
+ var colCnt = cols.length;
5122
+ var i, r=-1, c=-1;
5123
+ for (i=0; i<rowCnt; i++) {
5124
+ if (y >= rows[i][0] && y < rows[i][1]) {
5125
+ r = i;
5126
+ break;
5127
+ }
5128
+ }
5129
+ for (i=0; i<colCnt; i++) {
5130
+ if (x >= cols[i][0] && x < cols[i][1]) {
5131
+ c = i;
5132
+ break;
5133
+ }
5134
+ }
5135
+ return (r>=0 && c>=0) ? { row:r, col:c } : null;
5136
+ };
5137
+
5138
+
5139
+ t.rect = function(row0, col0, row1, col1, originElement) { // row1,col1 is inclusive
5140
+ var origin = originElement.offset();
5141
+ return {
5142
+ top: rows[row0][0] - origin.top,
5143
+ left: cols[col0][0] - origin.left,
5144
+ width: cols[col1][1] - cols[col0][0],
5145
+ height: rows[row1][1] - rows[row0][0]
5146
+ };
5147
+ };
5148
+
5149
+ }
5150
+
5151
+ function HoverListener(coordinateGrid) {
5152
+
5153
+
5154
+ var t = this;
5155
+ var bindType;
5156
+ var change;
5157
+ var firstCell;
5158
+ var cell;
5159
+
5160
+
5161
+ t.start = function(_change, ev, _bindType) {
5162
+ change = _change;
5163
+ firstCell = cell = null;
5164
+ coordinateGrid.build();
5165
+ mouse(ev);
5166
+ bindType = _bindType || 'mousemove';
5167
+ $(document).bind(bindType, mouse);
5168
+ };
5169
+
5170
+
5171
+ function mouse(ev) {
5172
+ _fixUIEvent(ev); // see below
5173
+ var newCell = coordinateGrid.cell(ev.pageX, ev.pageY);
5174
+ if (!newCell != !cell || newCell && (newCell.row != cell.row || newCell.col != cell.col)) {
5175
+ if (newCell) {
5176
+ if (!firstCell) {
5177
+ firstCell = newCell;
5178
+ }
5179
+ change(newCell, firstCell, newCell.row-firstCell.row, newCell.col-firstCell.col);
5180
+ }else{
5181
+ change(newCell, firstCell);
5182
+ }
5183
+ cell = newCell;
5184
+ }
5185
+ }
5186
+
5187
+
5188
+ t.stop = function() {
5189
+ $(document).unbind(bindType, mouse);
5190
+ return cell;
5191
+ };
5192
+
5193
+
5194
+ }
5195
+
5196
+
5197
+
5198
+ // this fix was only necessary for jQuery UI 1.8.16 (and jQuery 1.7 or 1.7.1)
5199
+ // upgrading to jQuery UI 1.8.17 (and using either jQuery 1.7 or 1.7.1) fixed the problem
5200
+ // but keep this in here for 1.8.16 users
5201
+ // and maybe remove it down the line
5202
+
5203
+ function _fixUIEvent(event) { // for issue 1168
5204
+ if (event.pageX === undefined) {
5205
+ event.pageX = event.originalEvent.pageX;
5206
+ event.pageY = event.originalEvent.pageY;
5207
+ }
5208
+ }
5209
+ function HorizontalPositionCache(getElement) {
5210
+
5211
+ var t = this,
5212
+ elements = {},
5213
+ lefts = {},
5214
+ rights = {};
5215
+
5216
+ function e(i) {
5217
+ return elements[i] = elements[i] || getElement(i);
5218
+ }
5219
+
5220
+ t.left = function(i) {
5221
+ return lefts[i] = lefts[i] === undefined ? e(i).position().left : lefts[i];
5222
+ };
5223
+
5224
+ t.right = function(i) {
5225
+ return rights[i] = rights[i] === undefined ? t.left(i) + e(i).width() : rights[i];
5226
+ };
5227
+
5228
+ t.clear = function() {
5229
+ elements = {};
5230
+ lefts = {};
5231
+ rights = {};
5232
+ };
5233
+
5234
+ }
5235
+
5236
+ })(jQuery);
js/fullcalendar.min.js ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ - * NOTE: This is an edited version of the FullCalendar v1.5.4 (eventorganiser branch)
3
+ -*/
4
+ /*
5
+
6
+ FullCalendar v1.5.4
7
+ http://arshaw.com/fullcalendar/
8
+
9
+ Use fullcalendar.css for basic styling.
10
+ For event drag & drop, requires jQuery UI draggable.
11
+ For event resizing, requires jQuery UI resizable.
12
+
13
+ Copyright (c) 2011 Adam Shaw
14
+ Dual licensed under the MIT and GPL licenses, located in
15
+ MIT-LICENSE.txt and GPL-LICENSE.txt respectively.
16
+
17
+ Date: Sat Nov 3 20:29:34 2012 +0000
18
+
19
+ */
20
+ (function(l,na){function wb(a){l.extend(true,Ya,a)}function Yb(a,b,e){function d(k){if(E){u();o();la();T(k)}else f()}function f(){B=b.theme?"ui":"fc";a.addClass("fc");b.isRTL&&a.addClass("fc-rtl");b.theme&&a.addClass("ui-widget");E=l("<div class='fc-content' style='position:relative'/>").prependTo(a);C=new Zb(U,b);(Q=C.render())&&a.prepend(Q);y(b.defaultView);l(window).resize(oa);t()||g()}function g(){setTimeout(function(){!n.start&&t()&&T()},0)}function m(){l(window).unbind("resize",oa);C.destroy();
21
+ E.remove();a.removeClass("fc fc-rtl ui-widget")}function j(){return i.offsetWidth!==0}function t(){return l("body")[0].offsetWidth!==0}function y(k){if(!n||k!=n.name){F++;ra();var D=n,Z;if(D){(D.beforeHide||xb)();Za(E,E.height());D.element.hide()}else Za(E,1);E.css("overflow","hidden");if(n=Y[k])n.element.show();else n=Y[k]=new Ja[k](Z=s=l("<div class='fc-view fc-view-"+k+"' style='position:absolute'/>").appendTo(E),U);D&&C.deactivateButton(D.name);C.activateButton(k);T();E.css("overflow","");D&&
22
+ Za(E,1);Z||(n.afterShow||xb)();F--}}function T(k){if(j()){F++;ra();p===na&&u();var D=false;if(!n.start||k||r<n.start||r>=n.end){n.render(r,k||0);ea(true);D=true}else if(n.sizeDirty){n.clearEvents();ea();D=true}else if(n.eventsDirty){n.clearEvents();D=true}n.sizeDirty=false;n.eventsDirty=false;ga(D);X=a.outerWidth();C.updateTitle(n.title);k=new Date;k>=n.start&&k<n.end?C.disableButton("today"):C.enableButton("today");F--;n.trigger("viewDisplay",i)}}function R(){o();if(j()){u();ea();ra();n.clearEvents();
23
+ n.renderEvents(I);n.sizeDirty=false}}function o(){l.each(Y,function(k,D){D.sizeDirty=true})}function u(){p=b.contentHeight?b.contentHeight:b.height?b.height-(Q?Q.height():0)-Sa(E):Math.round(E.width()/Math.max(b.aspectRatio,0.5))}function ea(k){F++;n.setHeight(p,k);if(s){s.css("position","relative");s=null}n.setWidth(E.width(),k);F--}function oa(){if(!F)if(n.start){var k=++v;setTimeout(function(){if(k==v&&!F&&j())if(X!=(X=a.outerWidth())){F++;R();n.trigger("windowResize",i);F--}},200)}else g()}function ga(k){if(!b.lazyFetching||
24
+ ya(n.visStart,n.visEnd))pa();else k&&ca()}function pa(){J(n.visStart,n.visEnd,b)}function qa(k){I=k;ca()}function ha(k){ca(k)}function ca(k){la();if(j()){n.clearEvents();n.renderEvents(I,k);n.eventsDirty=false}}function la(){l.each(Y,function(k,D){D.eventsDirty=true})}function ua(k,D,Z){n.select(k,D,Z===na?true:Z)}function ra(){n&&n.unselect()}function V(){T(-1)}function da(){T(1)}function ka(){gb(r,-1);T()}function sa(){gb(r,1);T()}function G(){r=new Date;T()}function q(k,D,Z){if(k instanceof Date)r=
25
+ M(k);else yb(r,k,D,Z);T()}function K(k,D,Z){k!==na&&gb(r,k);D!==na&&hb(r,D);Z!==na&&ba(r,Z);T()}function c(){return M(r)}function z(){return n}function O(k,D){if(D===na)return b[k];if(k=="height"||k=="contentHeight"||k=="aspectRatio"){b[k]=D;R()}else{b[k]=D;ca()}}function N(k,D){if(b[k])return b[k].apply(D||i,Array.prototype.slice.call(arguments,2))}var U=this;U.options=b;U.render=d;U.destroy=m;U.refetchEvents=pa;U.reportEvents=qa;U.reportEventChange=ha;U.rerenderEvents=ca;U.changeView=y;U.select=
26
+ ua;U.unselect=ra;U.prev=V;U.next=da;U.prevYear=ka;U.nextYear=sa;U.today=G;U.gotoDate=q;U.incrementDate=K;U.formatDate=function(k,D){return Oa(k,D,b)};U.formatDates=function(k,D,Z){return ib(k,D,Z,b)};U.getDate=c;U.getView=z;U.option=O;U.trigger=N;$b.call(U,b,e);var ya=U.isFetchNeeded,J=U.fetchEvents,i=a[0],C,Q,E,B,n,Y={},X,p,s,v=0,F=0,r=new Date,I=[],L;yb(r,b.year,b.month,b.date);b.droppable&&l(document).bind("dragstart",function(k,D){var Z=k.target,ja=l(Z);if(!ja.parents(".fc").length){var ia=b.dropAccept;
27
+ if(l.isFunction(ia)?ia.call(Z,ja):ja.is(ia)){L=Z;n.dragStart(L,k,D)}}}).bind("dragstop",function(k,D){if(L){n.dragStop(L,k,D);L=null}})}function Zb(a,b){function e(){o=b.theme?"ui":"fc";if(b.header)return R=l("<table class='fc-header' style='width:100%'/>").append(l("<tr/>").append(f("left")).append(f("center")).append(f("right")))}function d(){R.remove()}function f(u){var ea=l("<td class='fc-header-"+u+"'/>");(u=b.header[u])&&l.each(u.split(" "),function(oa){oa>0&&ea.append("<span class='fc-header-space'/>");
28
+ var ga;l.each(this.split(","),function(pa,qa){if(qa=="title"){ea.append("<span class='fc-header-title'><h2>&nbsp;</h2></span>");ga&&ga.addClass(o+"-corner-right");ga=null}else if(l.isFunction(b.customButtons[qa])){pa=b.customButtons[qa](b);ea.append(pa)}else{var ha;if(a[qa])ha=a[qa];else if(Ja[qa])ha=function(){la.removeClass(o+"-state-hover");a.changeView(qa)};if(ha){pa=b.theme?jb(b.buttonIcons,qa):null;var ca=jb(b.buttonText,qa);if(b.buttonui){var la=l("<span class='fc-button fc-button-"+qa+" "+
29
+ o+"-state-default'>"+ca+"</span>");la.button()}else la=l("<span class='fc-button fc-button-"+qa+" "+o+"-state-default'><span class='fc-button-inner'><span class='fc-button-content'>"+(pa?"<span class='fc-icon-wrap'><span class='ui-icon ui-icon-"+pa+"'/></span>":ca)+"</span><span class='fc-button-effect'><span></span></span></span></span>");if(la){la.click(function(){la.hasClass(o+"-state-disabled")||ha()}).mousedown(function(){la.not("."+o+"-state-active").not("."+o+"-state-disabled").addClass(o+
30
+ "-state-down")}).mouseup(function(){la.removeClass(o+"-state-down")}).hover(function(){la.not("."+o+"-state-active").not("."+o+"-state-disabled").addClass(o+"-state-hover")},function(){la.removeClass(o+"-state-hover").removeClass(o+"-state-down")}).appendTo(ea);ga||la.addClass(o+"-corner-left");ga=la}}}});ga&&ga.addClass(o+"-corner-right")});return ea}function g(u){R.find("h2").html(u)}function m(u){R.find("span.fc-button-"+u).addClass(o+"-state-active")}function j(u){R.find("span.fc-button-"+u).removeClass(o+
31
+ "-state-active")}function t(u){R.find("span.fc-button-"+u).addClass(o+"-state-disabled")}function y(u){R.find("span.fc-button-"+u).removeClass(o+"-state-disabled")}var T=this;T.render=e;T.destroy=d;T.updateTitle=g;T.activateButton=m;T.deactivateButton=j;T.disableButton=t;T.enableButton=y;var R=l([]),o}function $b(a,b){function e(c,z){return!da||c<da||z>ka}function d(c,z,O){da=c;ka=z;K=[];c=++sa;G=z=V.length;for(var N=0;N<z;N++)f(V[N],c,O)}function f(c,z,O){g(c,function(N){if(z==sa){if(N){for(var U=
32
+ 0;U<N.length;U++){N[U].source=c;oa(N[U])}K=K.concat(N)}G--;G||ua(K)}},O)}function g(c,z){var O,N=Aa.sourceFetchers,U;for(O=0;O<N.length;O++){U=N[O](c,da,ka,z);if(U===true)return;else if(typeof U=="object"){g(U,z,a);return}}if(O=c.events)if(l.isFunction(O)){u();O(M(da),M(ka),function(C){z(C);ea()},a)}else l.isArray(O)?z(O):z();else if(c.url){var ya=c.success,J=c.error,i=c.complete;O=l.extend({},c.data||{});N=Ta(c.startParam,a.startParam);U=Ta(c.endParam,a.endParam);if(N)O[N]=Math.round(+da/1E3);if(U)O[U]=
33
+ Math.round(+ka/1E3);u();l.ajax(l.extend({},ac,c,{data:O,success:function(C){C=C||[];var Q=$a(ya,this,arguments);if(l.isArray(Q))C=Q;z(C)},error:function(){$a(J,this,arguments);z()},complete:function(){$a(i,this,arguments);ea()}}))}else z()}function m(c){if(c=j(c)){G++;f(c,sa)}}function j(c){if(l.isFunction(c)||l.isArray(c))c={events:c};else if(typeof c=="string")c={url:c};if(typeof c=="object"){ga(c);V.push(c);return c}}function t(c){V=l.grep(V,function(z){return!pa(z,c)});K=l.grep(K,function(z){return!pa(z.source,
34
+ c)});ua(K)}function y(c){var z,O=K.length,N,U=la().defaultEventEnd,ya=c.start-c._start,J=c.end?c.end-(c._end||U(c)):0;for(z=0;z<O;z++){N=K[z];if(N._id==c._id&&N!=c){N.start=new Date(+N.start+ya);N.end=c.end?N.end?new Date(+N.end+J):new Date(+U(N)+J):null;N.title=c.title;N.url=c.url;N.allDay=c.allDay;N.className=c.className;N.editable=c.editable;N.color=c.color;N.backgroudColor=c.backgroudColor;N.borderColor=c.borderColor;N.textColor=c.textColor;oa(N)}}oa(c);ua(K)}function T(c,z){oa(c);if(!c.source){if(z){ra.events.push(c);
35
+ c.source=ra}K.push(c)}ua(K)}function R(c){if(c){if(!l.isFunction(c)){var z=c+"";c=function(N){return N._id==z}}K=l.grep(K,c,true);for(O=0;O<V.length;O++)if(l.isArray(V[O].events))V[O].events=l.grep(V[O].events,c,true)}else{K=[];for(var O=0;O<V.length;O++)if(l.isArray(V[O].events))V[O].events=[]}ua(K)}function o(c){if(l.isFunction(c))return l.grep(K,c);else if(c){c+="";return l.grep(K,function(z){return z._id==c})}return K}function u(){q++||ca("loading",null,true)}function ea(){--q||ca("loading",null,
36
+ false)}function oa(c){var z=c.source||{},O=Ta(z.ignoreTimezone,a.ignoreTimezone);c._id=c._id||(c.id===na?"_fc"+bc++:c.id+"");if(c.date){if(!c.start)c.start=c.date;delete c.date}c._start=M(c.start=kb(c.start,O));c.end=kb(c.end,O);if(c.end&&c.end<=c.start)c.end=null;c._end=c.end?M(c.end):null;if(c.allDay===na)c.allDay=Ta(z.allDayDefault,a.allDayDefault);if(c.className){if(typeof c.className=="string")c.className=c.className.split(/\s+/)}else c.className=[]}function ga(c){if(c.className){if(typeof c.className==
37
+ "string")c.className=c.className.split(/\s+/)}else c.className=[];for(var z=Aa.sourceNormalizers,O=0;O<z.length;O++)z[O](c)}function pa(c,z){return c&&z&&qa(c)==qa(z)}function qa(c){return(typeof c=="object"?c.events||c.url:"")||c}var ha=this;ha.isFetchNeeded=e;ha.fetchEvents=d;ha.addEventSource=m;ha.removeEventSource=t;ha.updateEvent=y;ha.renderEvent=T;ha.removeEvents=R;ha.clientEvents=o;ha.normalizeEvent=oa;var ca=ha.trigger,la=ha.getView,ua=ha.reportEvents,ra={events:[]},V=[ra],da,ka,sa=0,G=0,
38
+ q=0,K=[];for(ha=0;ha<b.length;ha++)j(b[ha])}function gb(a,b,e){a.setFullYear(a.getFullYear()+b);e||Ka(a);return a}function hb(a,b,e){if(+a){b=a.getMonth()+b;var d=M(a);d.setDate(1);d.setMonth(b);a.setMonth(b);for(e||Ka(a);a.getMonth()!=d.getMonth();)a.setDate(a.getDate()+(a<d?1:-1))}return a}function ba(a,b,e){if(+a){b=a.getDate()+b;var d=M(a);d.setHours(9);d.setDate(b);a.setDate(b);e||Ka(a);lb(a,d)}return a}function lb(a,b){if(+a)for(;a.getDate()!=b.getDate();)a.setTime(+a+(a<b?1:-1)*cc)}function xa(a,
39
+ b){a.setMinutes(a.getMinutes()+b);return a}function Ka(a){a.setHours(0);a.setMinutes(0);a.setSeconds(0);a.setMilliseconds(0);return a}function M(a,b){if(b)return Ka(new Date(+a));return new Date(+a)}function zb(){var a=0,b;do b=new Date(1970,a++,1);while(b.getHours());return b}function Fa(a,b,e){for(b=b||1;!a.getDay()||e&&a.getDay()==1||!e&&a.getDay()==6;)ba(a,b);return a}function Ca(a,b){return Math.round((M(a,true)-M(b,true))/Ab)}function yb(a,b,e,d){if(b!==na&&b!=a.getFullYear()){a.setDate(1);
40
+ a.setMonth(0);a.setFullYear(b)}if(e!==na&&e!=a.getMonth()){a.setDate(1);a.setMonth(e)}d!==na&&a.setDate(d)}function kb(a,b){if(typeof a=="object")return a;if(typeof a=="number")return new Date(a*1E3);if(typeof a=="string"){if(a.match(/^\d+(\.\d+)?$/))return new Date(parseFloat(a)*1E3);if(b===na)b=true;return Bb(a,b)||(a?new Date(a):null)}return null}function Bb(a,b){a=a.match(/^([0-9]{4})(-([0-9]{2})(-([0-9]{2})([T ]([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2})(:?([0-9]{2}))?))?)?)?)?$/);
41
+ if(!a)return null;var e=new Date(a[1],0,1);if(b||!a[13]){b=new Date(a[1],0,1,9,0);if(a[3]){e.setMonth(a[3]-1);b.setMonth(a[3]-1)}if(a[5]){e.setDate(a[5]);b.setDate(a[5])}lb(e,b);a[7]&&e.setHours(a[7]);a[8]&&e.setMinutes(a[8]);a[10]&&e.setSeconds(a[10]);a[12]&&e.setMilliseconds(Number("0."+a[12])*1E3);lb(e,b)}else{e.setUTCFullYear(a[1],a[3]?a[3]-1:0,a[5]||1);e.setUTCHours(a[7]||0,a[8]||0,a[10]||0,a[12]?Number("0."+a[12])*1E3:0);if(a[14]){b=Number(a[16])*60+(a[18]?Number(a[18]):0);b*=a[15]=="-"?1:-1;
42
+ e=new Date(+e+b*60*1E3)}}return e}function mb(a){if(typeof a=="number")return a*60;if(typeof a=="object")return a.getHours()*60+a.getMinutes();if(a=a.match(/(\d+)(?::(\d+))?\s*(\w+)?/)){var b=parseInt(a[1],10);if(a[3]){b%=12;if(a[3].toLowerCase().charAt(0)=="p")b+=12}return b*60+(a[2]?parseInt(a[2],10):0)}}function Oa(a,b,e){return ib(a,null,b,e)}function ib(a,b,e,d){d=d||Ya;var f=a,g=b,m,j=e.length,t,y,T,R="";for(m=0;m<j;m++){t=e.charAt(m);if(t=="'")for(y=m+1;y<j;y++){if(e.charAt(y)=="'"){if(f){R+=
43
+ y==m+1?"'":e.substring(m+1,y);m=y}break}}else if(t=="(")for(y=m+1;y<j;y++){if(e.charAt(y)==")"){m=Oa(f,e.substring(m+1,y),d);if(parseInt(m.replace(/\D/,""),10))R+=m;m=y;break}}else if(t=="[")for(y=m+1;y<j;y++){if(e.charAt(y)=="]"){t=e.substring(m+1,y);m=Oa(f,t,d);if(m!=Oa(g,t,d))R+=m;m=y;break}}else if(t=="{"){f=b;g=a}else if(t=="}"){f=a;g=b}else{for(y=j;y>m;y--)if(T=dc[e.substring(m,y)]){if(f)R+=T(f,d);m=y-1;break}if(y==m)if(f)R+=t}}return R}function Ua(a){return a.end?ec(a.end,a.allDay):ba(M(a.start),
44
+ 1)}function ec(a,b){a=M(a);return b||a.getHours()||a.getMinutes()?ba(a,1):Ka(a)}function fc(a,b){return(b.msLength-a.msLength)*100+(a.event.start-b.event.start)}function Cb(a,b){return a.end>b.start&&a.start<b.end}function nb(a,b,e,d){var f=[],g,m=a.length,j,t,y,T,R;for(g=0;g<m;g++){j=a[g];t=j.start;y=b[g];if(y>e&&t<d){if(t<e){t=M(e);T=false}else{t=t;T=true}if(y>d){y=M(d);R=false}else{y=y;R=true}f.push({event:j,start:t,end:y,isStart:T,isEnd:R,msLength:y-t})}}return f.sort(fc)}function ob(a){var b=
45
+ [],e,d=a.length,f,g,m,j;for(e=0;e<d;e++){f=a[e];for(g=0;;){m=false;if(b[g])for(j=0;j<b[g].length;j++)if(Cb(b[g][j],f)){m=true;break}if(m)g++;else break}if(b[g])b[g].push(f);else b[g]=[f]}return b}function Db(a,b,e){a.unbind("mouseover").mouseover(function(d){for(var f=d.target,g;f!=this;){g=f;f=f.parentNode}if((f=g._fci)!==na){g._fci=na;g=b[f];e(g.event,g.element,g);l(d.target).trigger(d)}d.stopPropagation()})}function Va(a,b,e){for(var d=0,f;d<a.length;d++){f=l(a[d]);f.width(Math.max(0,b-pb(f,e)))}}
46
+ function Eb(a,b,e){for(var d=0,f;d<a.length;d++){f=l(a[d]);f.height(Math.max(0,b-Sa(f,e)))}}function pb(a,b){return gc(a)+hc(a)+(b?ic(a):0)}function gc(a){return(parseFloat(l.css(a[0],"paddingLeft",true))||0)+(parseFloat(l.css(a[0],"paddingRight",true))||0)}function ic(a){return(parseFloat(l.css(a[0],"marginLeft",true))||0)+(parseFloat(l.css(a[0],"marginRight",true))||0)}function hc(a){return(parseFloat(l.css(a[0],"borderLeftWidth",true))||0)+(parseFloat(l.css(a[0],"borderRightWidth",true))||0)}function Sa(a,
47
+ b){return jc(a)+kc(a)+(b?Fb(a):0)}function jc(a){return(parseFloat(l.css(a[0],"paddingTop",true))||0)+(parseFloat(l.css(a[0],"paddingBottom",true))||0)}function Fb(a){return(parseFloat(l.css(a[0],"marginTop",true))||0)+(parseFloat(l.css(a[0],"marginBottom",true))||0)}function kc(a){return(parseFloat(l.css(a[0],"borderTopWidth",true))||0)+(parseFloat(l.css(a[0],"borderBottomWidth",true))||0)}function Za(a,b){b=typeof b=="number"?b+"px":b;a.each(function(e,d){d.style.cssText+=";min-height:"+b+";_height:"+
48
+ b})}function xb(){}function Gb(a,b){return a-b}function Hb(a){return Math.max.apply(Math,a)}function Pa(a){return(a<10?"0":"")+a}function jb(a,b){if(a[b]!==na)return a[b];b=b.split(/(?=[A-Z])/);for(var e=b.length-1,d;e>=0;e--){d=a[b[e].toLowerCase()];if(d!==na)return d}return a[""]}function Qa(a){return a.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/'/g,"&#039;").replace(/"/g,"&quot;").replace(/\n/g,"<br />")}function Ib(a){return a.id+"/"+a.className+"/"+a.style.cssText.replace(/(^|;)\s*(top|left|width|height)\s*:[^;]*/ig,
49
+ "")}function qb(a){a.attr("unselectable","on").css("MozUserSelect","none").bind("selectstart.ui",function(){return false})}function ab(a){a.children().removeClass("fc-first fc-last").filter(":first-child").addClass("fc-first").end().filter(":last-child").addClass("fc-last")}function rb(a,b){a.each(function(e,d){d.className=d.className.replace(/^fc-\w*/,"fc-"+lc[b.getDay()])})}function Jb(a,b){var e=a.source||{},d=a.color,f=e.color,g=b("eventColor"),m=a.backgroundColor||d||e.backgroundColor||f||b("eventBackgroundColor")||
50
+ g;d=a.borderColor||d||e.borderColor||f||b("eventBorderColor")||g;a=a.textColor||e.textColor||b("eventTextColor");b=[];m&&b.push("background-color:"+m);d&&b.push("border-color:"+d);a&&b.push("color:"+a);return b.join(";")}function $a(a,b,e){if(l.isFunction(a))a=[a];if(a){var d,f;for(d=0;d<a.length;d++)f=a[d].apply(b,e)||f;return f}}function Ta(){for(var a=0;a<arguments.length;a++)if(arguments[a]!==na)return arguments[a]}function mc(a,b){function e(j,t){if(t){hb(j,t);j.setDate(1)}j=M(j,true);j.setDate(1);
51
+ t=hb(M(j),1);var y=M(j),T=M(t),R=f("firstDay"),o=f("weekends")?0:1;if(o){Fa(y);Fa(T,-1,true)}ba(y,-((y.getDay()-Math.max(R,o)+7)%7));ba(T,(7-T.getDay()+Math.max(R,o))%7);R=Math.round((T-y)/(Ab*7));if(f("weekMode")=="fixed"){ba(T,(6-R)*7);R=6}d.title=m(j,f("titleFormat"));d.start=j;d.end=t;d.visStart=y;d.visEnd=T;g(6,R,o?5:7,true)}var d=this;d.render=e;sb.call(d,a,b,"month");var f=d.opt,g=d.renderBasic,m=b.formatDate}function nc(a,b){function e(j,t){t&&ba(j,t*7);j=ba(M(j),-((j.getDay()-f("firstDay")+
52
+ 7)%7));t=ba(M(j),7);var y=M(j),T=M(t),R=f("weekends");if(!R){Fa(y);Fa(T,-1,true)}d.title=m(y,ba(M(T),-1),f("titleFormat"));d.start=j;d.end=t;d.visStart=y;d.visEnd=T;g(1,1,R?7:5,false)}var d=this;d.render=e;sb.call(d,a,b,"basicWeek");var f=d.opt,g=d.renderBasic,m=b.formatDates}function oc(a,b){function e(j,t){if(t){ba(j,t);f("weekends")||Fa(j,t<0?-1:1)}d.title=m(j,f("titleFormat"));d.start=d.visStart=M(j,true);d.end=d.visEnd=ba(M(d.start),1);g(1,1,1,false)}var d=this;d.render=e;sb.call(d,a,b,"basicDay");
53
+ var f=d.opt,g=d.renderBasic,m=b.formatDate}function sb(a,b,e){function d(w,H,S,W){v=H;F=S;f();(H=!C)?g(w,W):z();m(H)}function f(){if(k=K("isRTL")){D=-1;Z=F-1}else{D=1;Z=0}ja=K("firstDay");ia=K("weekends")?0:1;ma=K("theme")?"ui":"fc";$=K("columnFormat")}function g(w,H){var S,W=ma+"-widget-header",fa=ma+"-widget-content",aa;S="<table class='fc-border-separate' style='width:100%' cellspacing='0'><thead><tr>";for(aa=0;aa<F;aa++)S+="<th class='fc- "+W+"'/>";S+="</tr></thead><tbody>";for(aa=0;aa<w;aa++){S+=
54
+ "<tr class='fc-week"+aa+"'>";for(W=0;W<F;W++)S+="<td class='fc- "+fa+" fc-day"+(aa*F+W)+"'><div>"+(H?"<div class='fc-day-number'/>":"")+"<div class='fc-day-content'><div style='position:relative'>&nbsp;</div></div></div></td>";S+="</tr>"}S+="</tbody></table>";w=l(S).appendTo(a);J=w.find("thead");i=J.find("th");C=w.find("tbody");Q=C.find("tr");E=C.find("td");B=E.filter(":first-child");n=Q.eq(0).find("div.fc-day-content div");ab(J.add(J.find("tr")));ab(Q);Q.eq(0).addClass("fc-first");y(E);Y=l("<div style='position:absolute;z-index:8;top:0;left:0'/>").appendTo(a)}
55
+ function m(w){var H=w||v==1,S=q.start.getMonth(),W=Ka(new Date),fa,aa,va;H&&i.each(function(wa,Ga){fa=l(Ga);aa=da(wa);fa.html(ya(aa,$));rb(fa,aa)});E.each(function(wa,Ga){fa=l(Ga);aa=da(wa);aa.getMonth()==S?fa.removeClass("fc-other-month"):fa.addClass("fc-other-month");+aa==+W?fa.addClass(ma+"-state-highlight fc-today"):fa.removeClass(ma+"-state-highlight fc-today");fa.find("div.fc-day-number").text(aa.getDate());H&&rb(fa,aa)});Q.each(function(wa,Ga){va=l(Ga);if(wa<v){va.show();wa==v-1?va.addClass("fc-last"):
56
+ va.removeClass("fc-last")}else va.hide()})}function j(w){p=w;w=p-J.height();var H,S,W;if(K("weekMode")=="variable")H=S=Math.floor(w/(v==1?2:6));else{H=Math.floor(w/v);S=w-H*(v-1)}B.each(function(fa,aa){if(fa<v){W=l(aa);Za(W.find("> div"),(fa==v-1?S:H)-Sa(W))}})}function t(w){X=w;L.clear();s=Math.floor(X/F);Va(i.slice(0,-1),s)}function y(w){w.click(T).mousedown(U)}function T(w){if(!K("selectable")){var H=parseInt(this.className.match(/fc\-day(\d+)/)[1]);H=da(H);c("dayClick",this,H,true,w)}}function R(w,
57
+ H,S){S&&r.build();S=M(q.visStart);for(var W=ba(M(S),F),fa=0;fa<v;fa++){var aa=new Date(Math.max(S,w)),va=new Date(Math.min(W,H));if(aa<va){var wa;if(k){wa=Ca(va,S)*D+Z+1;aa=Ca(aa,S)*D+Z+1}else{wa=Ca(aa,S);aa=Ca(va,S)}y(o(fa,wa,fa,aa-1))}ba(S,7);ba(W,7)}}function o(w,H,S,W){w=r.rect(w,H,S,W,a);return O(w,a)}function u(w){return M(w)}function ea(w,H){R(w,ba(M(H),1),true)}function oa(){N()}function ga(w,H,S){var W=ua(w);c("dayClick",E[W.row*F+W.col],w,H,S)}function pa(w,H){I.start(function(S){N();S&&
58
+ o(S.row,S.col,S.row,S.col)},H)}function qa(w,H,S){var W=I.stop();N();if(W){W=ra(W);c("drop",w,W,true,H,S)}}function ha(w){return M(w.start)}function ca(w){return L.left(w)}function la(w){return L.right(w)}function ua(w){return{row:Math.floor(Ca(w,q.visStart)/7),col:ka(w.getDay())}}function ra(w){return V(w.row,w.col)}function V(w,H){return ba(M(q.visStart),w*7+H*D+Z)}function da(w){return V(Math.floor(w/F),w%F)}function ka(w){return(w-Math.max(ja,ia)+F)%F*D+Z}function sa(w){return Q.eq(w)}function G(){return{left:0,
59
+ right:X}}var q=this;q.renderBasic=d;q.setHeight=j;q.setWidth=t;q.renderDayOverlay=R;q.defaultSelectionEnd=u;q.renderSelection=ea;q.clearSelection=oa;q.reportDayClick=ga;q.dragStart=pa;q.dragStop=qa;q.defaultEventEnd=ha;q.getHoverListener=function(){return I};q.colContentLeft=ca;q.colContentRight=la;q.dayOfWeekCol=ka;q.dateCell=ua;q.cellDate=ra;q.cellIsAllDay=function(){return true};q.allDayRow=sa;q.allDayBounds=G;q.getRowCnt=function(){return v};q.getColCnt=function(){return F};q.getColWidth=function(){return s};
60
+ q.getDaySegmentContainer=function(){return Y};Kb.call(q,a,b,e);Lb.call(q);Mb.call(q);pc.call(q);var K=q.opt,c=q.trigger,z=q.clearEvents,O=q.renderOverlay,N=q.clearOverlays,U=q.daySelectionMousedown,ya=b.formatDate,J,i,C,Q,E,B,n,Y,X,p,s,v,F,r,I,L,k,D,Z,ja,ia,ma,$;qb(a.addClass("fc-grid"));r=new Nb(function(w,H){var S,W,fa;i.each(function(aa,va){S=l(va);W=S.offset().left;if(aa)fa[1]=W;fa=[W];H[aa]=fa});fa[1]=W+S.outerWidth();Q.each(function(aa,va){if(aa<v){S=l(va);W=S.offset().top;if(aa)fa[1]=W;fa=
61
+ [W];w[aa]=fa}});fa[1]=W+S.outerHeight()});I=new Ob(r);L=new Pb(function(w){return n.eq(w)})}function pc(){function a(V,da){T(V);ua(e(V),da)}function b(){R();ga().empty()}function e(V){var da=ca(),ka=la(),sa=M(g.visStart);ka=ba(M(sa),ka);var G=l.map(V,Ua),q,K,c,z,O,N,U=[];for(q=0;q<da;q++){K=ob(nb(V,G,sa,ka));for(c=0;c<K.length;c++){z=K[c];for(O=0;O<z.length;O++){N=z[O];N.row=q;N.level=c;U.push(N)}}ba(sa,7);ba(ka,7)}return U}function d(V,da,ka){t(V)&&f(V,da);ka.isEnd&&y(V)&&ra(V,da,ka);o(V,da)}function f(V,
62
+ da){var ka=pa(),sa;da.draggable({zIndex:9,delay:50,opacity:m("dragOpacity"),revertDuration:m("dragRevertDuration"),start:function(G,q){j("eventDragStart",da,V,G,q);ea(V,da);ka.start(function(K,c,z,O){da.draggable("option","revert",!K||!z&&!O);ha();if(K){sa=z*7+O*(m("isRTL")?-1:1);qa(ba(M(V.start),sa),ba(Ua(V),sa))}else sa=0},G,"drag")},stop:function(G,q){ka.stop();ha();j("eventDragStop",da,V,G,q);if(sa)oa(this,V,sa,0,V.allDay,G,q);else{da.css("filter","");u(V,da)}}})}var g=this;g.renderEvents=a;g.compileDaySegs=
63
+ e;g.clearEvents=b;g.bindDaySeg=d;Qb.call(g);var m=g.opt,j=g.trigger,t=g.isEventDraggable,y=g.isEventResizable,T=g.reportEvents,R=g.reportEventClear,o=g.eventElementHandlers,u=g.showEvents,ea=g.hideEvents,oa=g.eventDrop,ga=g.getDaySegmentContainer,pa=g.getHoverListener,qa=g.renderDayOverlay,ha=g.clearOverlays,ca=g.getRowCnt,la=g.getColCnt,ua=g.renderDaySegs,ra=g.resizableDayEvent}function qc(a,b){function e(j,t){t&&ba(j,t*7);j=ba(M(j),-((j.getDay()-f("firstDay")+7)%7));t=ba(M(j),7);var y=M(j),T=M(t),
64
+ R=f("weekends");if(!R){Fa(y);Fa(T,-1,true)}d.title=m(y,ba(M(T),-1),f("titleFormat"));d.start=j;d.end=t;d.visStart=y;d.visEnd=T;g(R?7:5)}var d=this;d.render=e;Rb.call(d,a,b,"agendaWeek");var f=d.opt,g=d.renderAgenda,m=b.formatDates}function rc(a,b){function e(j,t){if(t){ba(j,t);f("weekends")||Fa(j,t<0?-1:1)}t=M(j,true);var y=ba(M(t),1);d.title=m(j,f("titleFormat"));d.start=d.visStart=t;d.end=d.visEnd=y;g(1)}var d=this;d.render=e;Rb.call(d,a,b,"agendaDay");var f=d.opt,g=d.renderAgenda,m=b.formatDate}
65
+ function Rb(a,b,e){function d(h){Ba=h;f();v?Q():g();m()}function f(){Wa=i("theme")?"ui":"fc";Sb=i("weekends")?0:1;Tb=i("firstDay");if(Ub=i("isRTL")){Ha=-1;Ia=Ba-1}else{Ha=1;Ia=0}La=mb(i("minTime"));bb=mb(i("maxTime"));Vb=i("columnFormat")}function g(){var h=Wa+"-widget-header",P=Wa+"-widget-content",x,A,ta,za,Da,Ea=i("slotMinutes")%15==0;x="<table style='width:100%' class='fc-agenda-days fc-border-separate' cellspacing='0'><thead><tr><th class='fc-agenda-axis "+h+"'>&nbsp;</th>";for(A=0;A<Ba;A++)x+=
66
+ "<th class='fc- fc-col"+A+" "+h+"'/>";x+="<th class='fc-agenda-gutter "+h+"'>&nbsp;</th></tr></thead><tbody><tr><th class='fc-agenda-axis "+h+"'>&nbsp;</th>";for(A=0;A<Ba;A++)x+="<td class='fc- fc-col"+A+" "+P+"'><div><div class='fc-day-content'><div style='position:relative'>&nbsp;</div></div></div></td>";x+="<td class='fc-agenda-gutter "+P+"'>&nbsp;</td></tr></tbody></table>";v=l(x).appendTo(a);F=v.find("thead");r=F.find("th").slice(1,-1);I=v.find("tbody");L=I.find("td").slice(0,-1);k=L.find("div.fc-day-content div");
67
+ D=L.eq(0);Z=D.find("> div");ab(F.add(F.find("tr")));ab(I.add(I.find("tr")));aa=F.find("th:first");va=v.find(".fc-agenda-gutter");ja=l("<div style='position:absolute;z-index:2;left:0;width:100%'/>").appendTo(a);if(i("allDaySlot")){ia=l("<div style='position:absolute;z-index:8;top:0;left:0'/>").appendTo(ja);x="<table style='width:100%' class='fc-agenda-allday' cellspacing='0'><tr><th class='"+h+" fc-agenda-axis'>"+i("allDayText")+"</th><td><div class='fc-day-content'><div style='position:relative'/></div></td><th class='"+
68
+ h+" fc-agenda-gutter'>&nbsp;</th></tr></table>";ma=l(x).appendTo(ja);$=ma.find("tr");o($.find("td"));aa=aa.add(ma.find("th:first"));va=va.add(ma.find("th.fc-agenda-gutter"));ja.append("<div class='fc-agenda-divider "+h+"'><div class='fc-agenda-divider-inner'/></div>")}else ia=l([]);w=l("<div style='position:absolute;width:100%;overflow-x:hidden;overflow-y:auto'/>").appendTo(ja);H=l("<div style='position:relative;width:100%;overflow:hidden'/>").appendTo(w);S=l("<div style='position:absolute;z-index:8;top:0;left:0'/>").appendTo(H);
69
+ x="<table class='fc-agenda-slots' style='width:100%' cellspacing='0'><tbody>";ta=zb();za=xa(M(ta),bb);xa(ta,La);for(A=tb=0;ta<za;A++){Da=ta.getMinutes();x+="<tr class='fc-slot"+A+" "+(!Da?"":"fc-minor")+"'><th class='fc-agenda-axis "+h+"'>"+(!Ea||!Da?s(ta,i("axisFormat")):"&nbsp;")+"</th><td class='"+P+"'><div style='position:relative'>&nbsp;</div></td></tr>";xa(ta,i("slotMinutes"));tb++}x+="</tbody></table>";W=l(x).appendTo(H);fa=W.find("div:first");u(W.find("td"));aa=aa.add(W.find("th:first"))}
70
+ function m(){var h,P,x,A,ta=Ka(new Date);for(h=0;h<Ba;h++){A=ua(h);P=r.eq(h);P.html(s(A,Vb));x=L.eq(h);+A==+ta?x.addClass(Wa+"-state-highlight fc-today"):x.removeClass(Wa+"-state-highlight fc-today");rb(P.add(x),A)}}function j(h,P){if(h===na)h=Wb;Wb=h;ub={};var x=I.position().top,A=w.position().top;h=Math.min(h-x,W.height()+A+1);Z.height(h-Sa(D));ja.css("top",x);w.height(h-A-1);Xa=fa.height()+1;P&&y()}function t(h){Ga=h;cb.clear();Ma=0;Va(aa.width("").each(function(P,x){Ma=Math.max(Ma,l(x).outerWidth())}),
71
+ Ma);h=w[0].clientWidth;if(vb=w.width()-h){Va(va,vb);va.show().prev().removeClass("fc-last")}else va.hide().prev().addClass("fc-last");db=Math.floor((h-Ma)/Ba);Va(r.slice(0,-1),db)}function y(){function h(){w.scrollTop(A)}var P=zb(),x=M(P);x.setHours(i("firstHour"));var A=da(P,x)+1;h();setTimeout(h,0)}function T(){Xb=w.scrollTop()}function R(){w.scrollTop(Xb)}function o(h){h.click(ea).mousedown(X)}function u(h){h.click(ea).mousedown(O)}function ea(h){if(!i("selectable")){var P=Math.min(Ba-1,Math.floor((h.pageX-
72
+ v.offset().left-Ma)/db)),x=ua(P),A=this.parentNode.className.match(/fc-slot(\d+)/);if(A){A=parseInt(A[1])*i("slotMinutes");var ta=Math.floor(A/60);x.setHours(ta);x.setMinutes(A%60+La);C("dayClick",L[P],x,false,h)}else C("dayClick",L[P],x,true,h)}}function oa(h,P,x){x&&Na.build();var A=M(J.visStart);if(Ub){x=Ca(P,A)*Ha+Ia+1;h=Ca(h,A)*Ha+Ia+1}else{x=Ca(h,A);h=Ca(P,A)}x=Math.max(0,x);h=Math.min(Ba,h);x<h&&o(ga(0,x,0,h-1))}function ga(h,P,x,A){h=Na.rect(h,P,x,A,ja);return E(h,ja)}function pa(h,P){for(var x=
73
+ M(J.visStart),A=ba(M(x),1),ta=0;ta<Ba;ta++){var za=new Date(Math.max(x,h)),Da=new Date(Math.min(A,P));if(za<Da){var Ea=ta*Ha+Ia;Ea=Na.rect(0,Ea,0,Ea,H);za=da(x,za);Da=da(x,Da);Ea.top=za;Ea.height=Da-za;u(E(Ea,H))}ba(x,1);ba(A,1)}}function qa(h){return cb.left(h)}function ha(h){return cb.right(h)}function ca(h){return{row:Math.floor(Ca(h,J.visStart)/7),col:V(h.getDay())}}function la(h){var P=ua(h.col);h=h.row;i("allDaySlot")&&h--;h>=0&&xa(P,La+h*i("slotMinutes"));return P}function ua(h){return ba(M(J.visStart),
74
+ h*Ha+Ia)}function ra(h){return i("allDaySlot")&&!h.row}function V(h){return(h-Math.max(Tb,Sb)+Ba)%Ba*Ha+Ia}function da(h,P){h=M(h,true);if(P<xa(M(h),La))return 0;if(P>=xa(M(h),bb))return W.height();h=i("slotMinutes");P=P.getHours()*60+P.getMinutes()-La;var x=Math.floor(P/h),A=ub[x];if(A===na)A=ub[x]=W.find("tr:eq("+x+") td div")[0].offsetTop;return Math.max(0,Math.round(A-1+Xa*(P%h/h)))}function ka(){return{left:Ma,right:Ga-vb}}function sa(){return $}function G(h){var P=M(h.start);if(h.allDay)return P;
75
+ return xa(P,i("defaultEventMinutes"))}function q(h,P){if(P)return M(h);return xa(M(h),i("slotMinutes"))}function K(h,P,x){if(x)i("allDaySlot")&&oa(h,ba(M(P),1),true);else c(h,P)}function c(h,P){var x=i("selectHelper");Na.build();if(x){var A=Ca(h,J.visStart)*Ha+Ia;if(A>=0&&A<Ba){A=Na.rect(0,A,0,A,H);var ta=da(h,h),za=da(h,P);if(za>ta){A.top=ta;A.height=za-ta;A.left+=2;A.width-=5;if(l.isFunction(x)){if(h=x(h,P)){A.position="absolute";A.zIndex=8;wa=l(h).css(A).appendTo(H)}}else{A.isStart=true;A.isEnd=
76
+ true;wa=l(p({title:"",start:h,end:P,className:["fc-select-helper"],editable:false},A));wa.css("opacity",i("dragOpacity"))}if(wa){u(wa);H.append(wa);Va(wa,A.width,true);Eb(wa,A.height,true)}}}}else pa(h,P)}function z(){B();if(wa){wa.remove();wa=null}}function O(h){if(h.which==1&&i("selectable")){Y(h);var P;Ra.start(function(x,A){z();if(x&&x.col==A.col&&!ra(x)){A=la(A);x=la(x);P=[A,xa(M(A),i("slotMinutes")),x,xa(M(x),i("slotMinutes"))].sort(Gb);c(P[0],P[3])}else P=null},h);l(document).one("mouseup",
77
+ function(x){Ra.stop();if(P){+P[0]==+P[1]&&N(P[0],false,x);n(P[0],P[3],false,x)}})}}function N(h,P,x){C("dayClick",L[V(h.getDay())],h,P,x)}function U(h,P){Ra.start(function(x){B();if(x)if(ra(x))ga(x.row,x.col,x.row,x.col);else{x=la(x);var A=xa(M(x),i("defaultEventMinutes"));pa(x,A)}},P)}function ya(h,P,x){var A=Ra.stop();B();A&&C("drop",h,la(A),ra(A),P,x)}var J=this;J.renderAgenda=d;J.setWidth=t;J.setHeight=j;J.beforeHide=T;J.afterShow=R;J.defaultEventEnd=G;J.timePosition=da;J.dayOfWeekCol=V;J.dateCell=
78
+ ca;J.cellDate=la;J.cellIsAllDay=ra;J.allDayRow=sa;J.allDayBounds=ka;J.getHoverListener=function(){return Ra};J.colContentLeft=qa;J.colContentRight=ha;J.getDaySegmentContainer=function(){return ia};J.getSlotSegmentContainer=function(){return S};J.getMinMinute=function(){return La};J.getMaxMinute=function(){return bb};J.getBodyContent=function(){return H};J.getRowCnt=function(){return 1};J.getColCnt=function(){return Ba};J.getColWidth=function(){return db};J.getSlotHeight=function(){return Xa};J.defaultSelectionEnd=
79
+ q;J.renderDayOverlay=oa;J.renderSelection=K;J.clearSelection=z;J.reportDayClick=N;J.dragStart=U;J.dragStop=ya;Kb.call(J,a,b,e);Lb.call(J);Mb.call(J);sc.call(J);var i=J.opt,C=J.trigger,Q=J.clearEvents,E=J.renderOverlay,B=J.clearOverlays,n=J.reportSelection,Y=J.unselect,X=J.daySelectionMousedown,p=J.slotSegHtml,s=b.formatDate,v,F,r,I,L,k,D,Z,ja,ia,ma,$,w,H,S,W,fa,aa,va,wa,Ga,Wb,Ma,db,vb,Xa,Xb,Ba,tb,Na,Ra,cb,ub={},Wa,Tb,Sb,Ub,Ha,Ia,La,bb,Vb;qb(a.addClass("fc-agenda"));Na=new Nb(function(h,P){function x(eb){return Math.max(Ea,
80
+ Math.min(tc,eb))}var A,ta,za;r.each(function(eb,uc){A=l(uc);ta=A.offset().left;if(eb)za[1]=ta;za=[ta];P[eb]=za});za[1]=ta+A.outerWidth();if(i("allDaySlot")){A=$;ta=A.offset().top;h[0]=[ta,ta+A.outerHeight()]}for(var Da=H.offset().top,Ea=w.offset().top,tc=Ea+w.outerHeight(),fb=0;fb<tb;fb++)h.push([x(Da+Xa*fb),x(Da+Xa*(fb+1))])});Ra=new Ob(Na);cb=new Pb(function(h){return k.eq(h)})}function sc(){function a(p,s){qa(p);var v,F=p.length,r=[],I=[];for(v=0;v<F;v++)p[v].allDay?r.push(p[v]):I.push(p[v]);if(u("allDaySlot")){K(e(r),
81
+ s);la()}g(d(I),s)}function b(){ha();ua().empty();ra().empty()}function e(p){p=ob(nb(p,l.map(p,Ua),o.visStart,o.visEnd));var s,v=p.length,F,r,I,L=[];for(s=0;s<v;s++){F=p[s];for(r=0;r<F.length;r++){I=F[r];I.row=0;I.level=s;L.push(I)}}return L}function d(p){var s=z(),v=ka(),F=da(),r=xa(M(o.visStart),v),I=l.map(p,f),L,k,D,Z,ja,ia,ma=[];for(L=0;L<s;L++){k=ob(nb(p,I,r,xa(M(r),F-v)));vc(k);for(D=0;D<k.length;D++){Z=k[D];for(ja=0;ja<Z.length;ja++){ia=Z[ja];ia.col=L;ia.level=D;ma.push(ia)}}ba(r,1,true)}return ma}
82
+ function f(p){return p.end?M(p.end):xa(M(p.start),u("defaultEventMinutes"))}function g(p,s){var v,F=p.length,r,I,L,k,D,Z,ja,ia,ma,$="",w,H,S={},W={},fa=ra(),aa;v=z();if(w=u("isRTL")){H=-1;aa=v-1}else{H=1;aa=0}for(v=0;v<F;v++){r=p[v];I=r.event;L=sa(r.start,r.start);k=sa(r.start,r.end);D=r.col;Z=r.level;ja=r.forward||0;ia=G(D*H+aa);ma=q(D*H+aa)-ia;ma=Math.min(ma-6,ma*0.95);D=Z?ma/(Z+ja+1):ja?(ma/(ja+1)-6)*2:ma;Z=ia+ma/(Z+ja+1)*Z*H+(w?ma-D:0);r.top=L;r.left=Z;r.outerWidth=D;r.outerHeight=k-L;$+=m(I,
83
+ r)}fa[0].innerHTML=$;w=fa.children();for(v=0;v<F;v++){r=p[v];I=r.event;$=l(w[v]);H=ea("eventRender",I,I,$,o);if(H===false)$.remove();else{if(H&&H!==true){$.remove();$=l(H).css({position:"absolute",top:r.top,left:r.left}).appendTo(fa)}r.element=$;if(I._id===s)t(I,$,r);else $[0]._fci=v;ya(I,$)}}Db(fa,p,t);for(v=0;v<F;v++){r=p[v];if($=r.element){I=S[s=r.key=Ib($[0])];r.vsides=I===na?(S[s]=Sa($,true)):I;I=W[s];r.hsides=I===na?(W[s]=pb($,true)):I;s=$.find("div.fc-event-content");if(s.length)r.contentTop=
84
+ s[0].offsetTop}}for(v=0;v<F;v++){r=p[v];if($=r.element){$[0].style.width=Math.max(0,r.outerWidth-r.hsides)+"px";S=Math.max(0,r.outerHeight-r.vsides);$[0].style.height=S+"px";I=r.event;if(r.contentTop!==na&&S-r.contentTop<10){$.find("div.fc-event-time").text(Y(I.start,u("timeFormat"))+" - "+I.title);$.find("div.fc-event-title").remove()}ea("eventAfterRender",I,I,$)}}}function m(p,s){var v="<",F=p.url,r=Jb(p,u),I=r?" style='"+r+"'":"",L=["fc-event","fc-event-skin","fc-event-vert"];oa(p)&&L.push("fc-event-draggable");
85
+ s.isStart&&L.push("fc-corner-top");s.isEnd&&L.push("fc-corner-bottom");L=L.concat(p.className);if(p.source)L=L.concat(p.source.className||[]);v+=F?"a href='"+Qa(p.url)+"'":"div";v+=" class='"+L.join(" ")+"' style='position:absolute;z-index:8;top:"+s.top+"px;left:"+s.left+"px;"+r+"'><div class='fc-event-inner fc-event-skin'"+I+"><div class='fc-event-head fc-event-skin'"+I+"><div class='fc-event-time'>"+Qa(X(p.start,p.end,u("timeFormat")))+"</div></div><div class='fc-event-content'><div class='fc-event-title'>"+
86
+ Qa(p.title)+"</div></div><div class='fc-event-bg'></div></div>";if(s.isEnd&&ga(p))v+="<div class='ui-resizable-handle ui-resizable-s'>=</div>";v+="</"+(F?"a":"div")+">";return v}function j(p,s,v){oa(p)&&y(p,s,v.isStart);v.isEnd&&ga(p)&&c(p,s,v);ca(p,s)}function t(p,s,v){var F=s.find("div.fc-event-time");oa(p)&&T(p,s,F);v.isEnd&&ga(p)&&R(p,s,F);ca(p,s)}function y(p,s,v){function F(){if(!L){s.width(r).height("").draggable("option","grid",null);L=true}}var r,I,L=true,k,D=u("isRTL")?-1:1,Z=V(),ja=O(),
87
+ ia=N(),ma=ka();s.draggable({zIndex:9,opacity:u("dragOpacity","month"),revertDuration:u("dragRevertDuration"),start:function($,w){ea("eventDragStart",s,p,$,w);i(p,s);r=s.width();Z.start(function(H,S,W,fa){B();if(H){I=false;k=fa*D;if(H.row)if(v){if(L){s.width(ja-10);Eb(s,ia*Math.round((p.end?(p.end-p.start)/wc:u("defaultEventMinutes"))/u("slotMinutes")));s.draggable("option","grid",[ja,1]);L=false}}else I=true;else{E(ba(M(p.start),k),ba(Ua(p),k));F()}I=I||L&&!k}else{F();I=true}s.draggable("option",
88
+ "revert",I)},$,"drag")},stop:function($,w){Z.stop();B();ea("eventDragStop",s,p,$,w);if(I){F();s.css("filter","");J(p,s)}else{var H=0;L||(H=Math.round((s.offset().top-U().offset().top)/ia)*u("slotMinutes")+ma-(p.start.getHours()*60+p.start.getMinutes()));C(this,p,k,H,L,$,w)}}})}function T(p,s,v){function F(H){var S=xa(M(p.start),H),W;if(p.end)W=xa(M(p.end),H);v.text(X(S,W,u("timeFormat")))}function r(){if(L){v.css("display","");s.draggable("option","grid",[$,w]);L=false}}var I,L=false,k,D,Z,ja=u("isRTL")?
89
+ -1:1,ia=V(),ma=z(),$=O(),w=N();s.draggable({zIndex:9,scroll:false,grid:[$,w],axis:ma==1?"y":false,opacity:u("dragOpacity"),revertDuration:u("dragRevertDuration"),start:function(H,S){ea("eventDragStart",s,p,H,S);i(p,s);I=s.position();D=Z=0;ia.start(function(W,fa,aa,va){s.draggable("option","revert",!W);B();if(W){k=va*ja;if(u("allDaySlot")&&!W.row){if(!L){L=true;v.hide();s.draggable("option","grid",null)}E(ba(M(p.start),k),ba(Ua(p),k))}else r()}},H,"drag")},drag:function(H,S){D=Math.round((S.position.top-
90
+ I.top)/w)*u("slotMinutes");if(D!=Z){L||F(D);Z=D}},stop:function(H,S){var W=ia.stop();B();ea("eventDragStop",s,p,H,S);if(W&&(k||D||L))C(this,p,k,L?0:D,L,H,S);else{r();s.css("filter","");s.css(I);F(0);J(p,s)}}})}function R(p,s,v){var F,r,I=N();s.resizable({handles:{s:"div.ui-resizable-s"},grid:I,start:function(L,k){F=r=0;i(p,s);s.css("z-index",9);ea("eventResizeStart",this,p,L,k)},resize:function(L,k){F=Math.round((Math.max(I,s.height())-k.originalSize.height)/I);if(F!=r){v.text(X(p.start,!F&&!p.end?
91
+ null:xa(pa(p),u("slotMinutes")*F),u("timeFormat")));r=F}},stop:function(L,k){ea("eventResizeStop",this,p,L,k);if(F)Q(this,p,0,u("slotMinutes")*F,L,k);else{s.css("z-index",8);J(p,s)}}})}var o=this;o.renderEvents=a;o.compileDaySegs=e;o.clearEvents=b;o.slotSegHtml=m;o.bindDaySeg=j;Qb.call(o);var u=o.opt,ea=o.trigger,oa=o.isEventDraggable,ga=o.isEventResizable,pa=o.eventEnd,qa=o.reportEvents,ha=o.reportEventClear,ca=o.eventElementHandlers,la=o.setHeight,ua=o.getDaySegmentContainer,ra=o.getSlotSegmentContainer,
92
+ V=o.getHoverListener,da=o.getMaxMinute,ka=o.getMinMinute,sa=o.timePosition,G=o.colContentLeft,q=o.colContentRight,K=o.renderDaySegs,c=o.resizableDayEvent,z=o.getColCnt,O=o.getColWidth,N=o.getSlotHeight,U=o.getBodyContent,ya=o.reportEventElement,J=o.showEvents,i=o.hideEvents,C=o.eventDrop,Q=o.eventResize,E=o.renderDayOverlay,B=o.clearOverlays,n=o.calendar,Y=n.formatDate,X=n.formatDates}function vc(a){var b,e,d,f,g,m;for(b=a.length-1;b>0;b--){f=a[b];for(e=0;e<f.length;e++){g=f[e];for(d=0;d<a[b-1].length;d++){m=
93
+ a[b-1][d];if(Cb(g,m))m.forward=Math.max(m.forward||0,(g.forward||0)+1)}}}}function Kb(a,b,e){function d(G,q){G=sa[G];if(typeof G=="object")return jb(G,q||e);return G}function f(G,q){return b.trigger.apply(b,[G,q||ca].concat(Array.prototype.slice.call(arguments,2),[ca]))}function g(G){return j(G)&&!d("disableDragging")}function m(G){return j(G)&&!d("disableResizing")}function j(G){return Ta(G.editable,(G.source||{}).editable,d("editable"))}function t(G){V={};var q,K=G.length,c;for(q=0;q<K;q++){c=G[q];
94
+ if(V[c._id])V[c._id].push(c);else V[c._id]=[c]}}function y(G){return G.end?M(G.end):la(G)}function T(G,q){da.push(q);if(ka[G._id])ka[G._id].push(q);else ka[G._id]=[q]}function R(){da=[];ka={}}function o(G,q){q.click(function(K){if(!q.hasClass("ui-draggable-dragging")&&!q.hasClass("ui-resizable-resizing"))return f("eventClick",this,G,K)}).hover(function(K){f("eventMouseover",this,G,K)},function(K){f("eventMouseout",this,G,K)})}function u(G,q){oa(G,q,"show")}function ea(G,q){oa(G,q,"hide")}function oa(G,
95
+ q,K){G=ka[G._id];var c,z=G.length;for(c=0;c<z;c++)if(!q||G[c][0]!=q[0])G[c][K]()}function ga(G,q,K,c,z,O,N){var U=q.allDay,ya=q._id;qa(V[ya],K,c,z);f("eventDrop",G,q,K,c,z,function(){qa(V[ya],-K,-c,U);ra(ya)},O,N);ra(ya)}function pa(G,q,K,c,z,O){var N=q._id;ha(V[N],K,c);f("eventResize",G,q,K,c,function(){ha(V[N],-K,-c);ra(N)},z,O);ra(N)}function qa(G,q,K,c){K=K||0;for(var z,O=G.length,N=0;N<O;N++){z=G[N];if(c!==na)z.allDay=c;xa(ba(z.start,q,true),K);if(z.end)z.end=xa(ba(z.end,q,true),K);ua(z,sa)}}
96
+ function ha(G,q,K){K=K||0;for(var c,z=G.length,O=0;O<z;O++){c=G[O];c.end=xa(ba(y(c),q,true),K);ua(c,sa)}}var ca=this;ca.element=a;ca.calendar=b;ca.name=e;ca.opt=d;ca.trigger=f;ca.isEventDraggable=g;ca.isEventResizable=m;ca.reportEvents=t;ca.eventEnd=y;ca.reportEventElement=T;ca.reportEventClear=R;ca.eventElementHandlers=o;ca.showEvents=u;ca.hideEvents=ea;ca.eventDrop=ga;ca.eventResize=pa;var la=ca.defaultEventEnd,ua=b.normalizeEvent,ra=b.reportEventChange,V={},da=[],ka={},sa=b.options}function Qb(){function a(i,
97
+ C){var Q=z(),E=ra(),B=V(),n=0,Y,X,p=i.length,s,v;Q[0].innerHTML=e(i);d(i,Q.children());f(i);g(i,Q,C);m(i);j(i);t(i);C=y();for(Q=0;Q<E;Q++){Y=[];for(X=0;X<B;X++)Y[X]=0;for(;n<p&&(s=i[n]).row==Q;){X=Hb(Y.slice(s.startCol,s.endCol));s.top=X;X+=s.outerHeight;for(v=s.startCol;v<s.endCol;v++)Y[v]=X;n++}C[Q].height(Hb(Y))}R(i,T(C))}function b(i,C,Q){var E=l("<div/>"),B=z(),n=i.length,Y;E[0].innerHTML=e(i);E=E.children();B.append(E);d(i,E);m(i);j(i);t(i);R(i,T(y()));E=[];for(B=0;B<n;B++)if(Y=i[B].element){i[B].row===
98
+ C&&Y.css("top",Q);E.push(Y[0])}return l(E)}function e(i){var C=ea("isRTL"),Q,E=i.length,B,n,Y,X;Q=ka();var p=Q.left,s=Q.right,v,F,r,I,L,k="";for(Q=0;Q<E;Q++){B=i[Q];n=B.event;X=["fc-event","fc-event-skin","fc-event-hori"];ga(n)&&X.push("fc-event-draggable");if(C){B.isStart&&X.push("fc-corner-right");B.isEnd&&X.push("fc-corner-left");v=q(B.end.getDay()-1);F=q(B.start.getDay());r=B.isEnd?sa(v):p;I=B.isStart?G(F):s}else{B.isStart&&X.push("fc-corner-left");B.isEnd&&X.push("fc-corner-right");v=q(B.start.getDay());
99
+ F=q(B.end.getDay()-1);r=B.isStart?sa(v):p;I=B.isEnd?G(F):s}X=X.concat(n.className);if(n.source)X=X.concat(n.source.className||[]);Y=n.url;L=Jb(n,ea);k+=Y?"<a href='"+Qa(Y)+"'":"<div";k+=" class='"+X.join(" ")+"' style='position:absolute;z-index:8;left:"+r+"px;"+L+"'><div class='fc-event-inner fc-event-skin'"+(L?" style='"+L+"'":"")+">";if(!n.allDay&&B.isStart)k+="<span class='fc-event-time'>"+Qa(N(n.start,n.end,ea("timeFormat")))+"</span>";k+="<span class='fc-event-title'>"+Qa(n.title)+"</span></div>";
100
+ if(B.isEnd&&pa(n))k+="<div class='ui-resizable-handle ui-resizable-"+(C?"w":"e")+"'>&nbsp;&nbsp;&nbsp;</div>";k+="</"+(Y?"a":"div")+">";B.left=r;B.outerWidth=I-r;B.startCol=v;B.endCol=F+1}return k}function d(i,C){var Q,E=i.length,B,n,Y;for(Q=0;Q<E;Q++){B=i[Q];n=B.event;Y=l(C[Q]);n=oa("eventRender",n,n,Y,u);if(n===false)Y.remove();else{if(n&&n!==true){n=l(n).css({position:"absolute",left:B.left});Y.replaceWith(n);Y=n}B.element=Y}}}function f(i){var C,Q=i.length,E,B;for(C=0;C<Q;C++){E=i[C];(B=E.element)&&
101
+ ha(E.event,B)}}function g(i,C,Q){var E,B=i.length,n,Y,X;for(E=0;E<B;E++){n=i[E];if(Y=n.element){X=n.event;if(X._id===Q)O(X,Y,n);else Y[0]._fci=E}}Db(C,i,O)}function m(i){var C,Q=i.length,E,B,n,Y,X={};for(C=0;C<Q;C++){E=i[C];if(B=E.element){n=E.key=Ib(B[0]);Y=X[n];if(Y===na)Y=X[n]=pb(B,true);E.hsides=Y}}}function j(i){var C,Q=i.length,E,B;for(C=0;C<Q;C++){E=i[C];if(B=E.element)B[0].style.width=Math.max(0,E.outerWidth-E.hsides)+"px"}}function t(i){var C,Q=i.length,E,B,n,Y,X={};for(C=0;C<Q;C++){E=i[C];
102
+ if(B=E.element){n=E.key;Y=X[n];if(Y===na)Y=X[n]=Fb(B);E.outerHeight=B[0].offsetHeight+Y}}}function y(){var i,C=ra(),Q=[];for(i=0;i<C;i++)Q[i]=da(i).find("td:first div.fc-day-content > div");return Q}function T(i){var C,Q=i.length,E=[];for(C=0;C<Q;C++)E[C]=i[C][0].offsetTop;return E}function R(i,C){var Q,E=i.length,B,n;for(Q=0;Q<E;Q++){B=i[Q];if(n=B.element){n[0].style.top=C[B.row]+(B.top||0)+"px";B=B.event;oa("eventAfterRender",B,B,n)}}}function o(i,C,Q){var E=ea("isRTL"),B=E?"w":"e",n=C.find("div.ui-resizable-"+
103
+ B),Y=false;qb(C);C.mousedown(function(X){X.preventDefault()}).click(function(X){if(Y){X.preventDefault();X.stopImmediatePropagation()}});n.mousedown(function(X){function p(ia){oa("eventResizeStop",this,i,ia);l("body").css("cursor","");s.stop();ya();k&&ua(this,i,k,0,ia);setTimeout(function(){Y=false},0)}if(X.which==1){Y=true;var s=u.getHoverListener(),v=ra(),F=V(),r=E?-1:1,I=E?F-1:0,L=C.css("top"),k,D,Z=l.extend({},i),ja=K(i.start);J();l("body").css("cursor",B+"-resize").one("mouseup",p);oa("eventResizeStart",
104
+ this,i,X);s.start(function(ia,ma){if(ia){var $=Math.max(ja.row,ia.row);ia=ia.col;if(v==1)$=0;if($==ja.row)ia=E?Math.min(ja.col,ia):Math.max(ja.col,ia);k=$*7+ia*r+I-(ma.row*7+ma.col*r+I);ma=ba(qa(i),k,true);if(k){Z.end=ma;$=D;D=b(c([Z]),Q.row,L);D.find("*").css("cursor",B+"-resize");$&&$.remove();la(i)}else if(D){ca(i);D.remove();D=null}ya();U(i.start,ba(M(ma),1))}},X)}})}var u=this;u.renderDaySegs=a;u.resizableDayEvent=o;var ea=u.opt,oa=u.trigger,ga=u.isEventDraggable,pa=u.isEventResizable,qa=u.eventEnd,
105
+ ha=u.reportEventElement,ca=u.showEvents,la=u.hideEvents,ua=u.eventResize,ra=u.getRowCnt,V=u.getColCnt,da=u.allDayRow,ka=u.allDayBounds,sa=u.colContentLeft,G=u.colContentRight,q=u.dayOfWeekCol,K=u.dateCell,c=u.compileDaySegs,z=u.getDaySegmentContainer,O=u.bindDaySeg,N=u.calendar.formatDates,U=u.renderDayOverlay,ya=u.clearOverlays,J=u.clearSelection}function Mb(){function a(R,o,u){b();o||(o=j(R,u));t(R,o,u);e(R,o,u)}function b(R){if(T){T=false;y();m("unselect",null,R)}}function e(R,o,u,ea){T=true;m("select",
106
+ null,R,o,u,ea)}function d(R){var o=f.cellDate,u=f.cellIsAllDay,ea=f.getHoverListener(),oa=f.reportDayClick;if(R.which==1&&g("selectable")){b(R);var ga;ea.start(function(pa,qa){y();if(pa&&u(pa)){ga=[o(qa),o(pa)].sort(Gb);t(ga[0],ga[1],true)}else ga=null},R);l(document).one("mouseup",function(pa){ea.stop();if(ga){+ga[0]==+ga[1]&&oa(ga[0],true,pa);e(ga[0],ga[1],true,pa)}})}}var f=this;f.select=a;f.unselect=b;f.reportSelection=e;f.daySelectionMousedown=d;var g=f.opt,m=f.trigger,j=f.defaultSelectionEnd,
107
+ t=f.renderSelection,y=f.clearSelection,T=false;g("selectable")&&g("unselectAuto")&&l(document).mousedown(function(R){var o=g("unselectCancel");if(o)if(l(R.target).parents(o).length)return;b(R)})}function Lb(){function a(g,m){var j=f.shift();j||(j=l("<div class='fc-cell-overlay' style='position:absolute;z-index:3'/>"));j[0].parentNode!=m[0]&&j.appendTo(m);d.push(j.css(g).show());return j}function b(){for(var g;g=d.shift();)f.push(g.hide().unbind())}var e=this;e.renderOverlay=a;e.clearOverlays=b;var d=
108
+ [],f=[]}function Nb(a){var b=this,e,d;b.build=function(){e=[];d=[];a(e,d)};b.cell=function(f,g){var m=e.length,j=d.length,t,y=-1,T=-1;for(t=0;t<m;t++)if(g>=e[t][0]&&g<e[t][1]){y=t;break}for(t=0;t<j;t++)if(f>=d[t][0]&&f<d[t][1]){T=t;break}return y>=0&&T>=0?{row:y,col:T}:null};b.rect=function(f,g,m,j,t){t=t.offset();return{top:e[f][0]-t.top,left:d[g][0]-t.left,width:d[j][1]-d[g][0],height:e[m][1]-e[f][0]}}}function Ob(a){function b(j){xc(j);j=a.cell(j.pageX,j.pageY);if(!j!=!m||j&&(j.row!=m.row||j.col!=
109
+ m.col)){if(j){g||(g=j);f(j,g,j.row-g.row,j.col-g.col)}else f(j,g);m=j}}var e=this,d,f,g,m;e.start=function(j,t,y){f=j;g=m=null;a.build();b(t);d=y||"mousemove";l(document).bind(d,b)};e.stop=function(){l(document).unbind(d,b);return m}}function xc(a){if(a.pageX===na){a.pageX=a.originalEvent.pageX;a.pageY=a.originalEvent.pageY}}function Pb(a){function b(m){return d[m]=d[m]||a(m)}var e=this,d={},f={},g={};e.left=function(m){return f[m]=f[m]===na?b(m).position().left:f[m]};e.right=function(m){return g[m]=
110
+ g[m]===na?e.left(m)+b(m).width():g[m]};e.clear=function(){d={};f={};g={}}}var Ya={defaultView:"month",aspectRatio:1.35,header:{left:"title",center:"",right:"today prev,next"},weekends:true,allDayDefault:true,ignoreTimezone:true,lazyFetching:true,startParam:"start",endParam:"end",titleFormat:{month:"MMMM yyyy",week:"MMM d[ yyyy]{ '&#8212;'[ MMM] d yyyy}",day:"dddd, MMM d, yyyy"},columnFormat:{month:"ddd",week:"ddd M/d",day:"dddd M/d"},timeFormat:{"":"h(:mm)t"},isRTL:false,firstDay:0,monthNames:["January",
111
+ "February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],buttonText:{prev:"&nbsp;&#9668;&nbsp;",next:"&nbsp;&#9658;&nbsp;",prevYear:"&nbsp;&lt;&lt;&nbsp;",nextYear:"&nbsp;&gt;&gt;&nbsp;",today:"today",month:"month",week:"week",
112
+ day:"day"},theme:false,buttonIcons:{prev:"circle-triangle-w",next:"circle-triangle-e"},unselectAuto:true,dropAccept:"*"},yc={header:{left:"next,prev today",center:"",right:"title"},buttonText:{prev:"&nbsp;&#9658;&nbsp;",next:"&nbsp;&#9668;&nbsp;",prevYear:"&nbsp;&gt;&gt;&nbsp;",nextYear:"&nbsp;&lt;&lt;&nbsp;"},buttonIcons:{prev:"circle-triangle-e",next:"circle-triangle-w"}},Aa=l.fullCalendar={version:"1.5.4"},Ja=Aa.views={};l.fn.fullCalendar=function(a){if(typeof a=="string"){var b=Array.prototype.slice.call(arguments,
113
+ 1),e;this.each(function(){var f=l.data(this,"fullCalendar");if(f&&l.isFunction(f[a])){f=f[a].apply(f,b);if(e===na)e=f;a=="destroy"&&l.removeData(this,"fullCalendar")}});if(e!==na)return e;return this}var d=a.eventSources||[];delete a.eventSources;if(a.events){d.push(a.events);delete a.events}a=l.extend(true,{},Ya,a.isRTL||a.isRTL===na&&Ya.isRTL?yc:{},a);this.each(function(f,g){f=l(g);g=new Yb(f,a,d);f.data("fullCalendar",g);g.render()});return this};Aa.sourceNormalizers=[];Aa.sourceFetchers=[];var ac=
114
+ {dataType:"json",cache:false},bc=1;Aa.addDays=ba;Aa.cloneDate=M;Aa.parseDate=kb;Aa.parseISO8601=Bb;Aa.parseTime=mb;Aa.formatDate=Oa;Aa.formatDates=ib;var lc=["sun","mon","tue","wed","thu","fri","sat"],Ab=864E5,cc=36E5,wc=6E4,dc={s:function(a){return a.getSeconds()},ss:function(a){return Pa(a.getSeconds())},m:function(a){return a.getMinutes()},mm:function(a){return Pa(a.getMinutes())},h:function(a){return a.getHours()%12||12},hh:function(a){return Pa(a.getHours()%12||12)},H:function(a){return a.getHours()},
115
+ HH:function(a){return Pa(a.getHours())},d:function(a){return a.getDate()},dd:function(a){return Pa(a.getDate())},ddd:function(a,b){return b.dayNamesShort[a.getDay()]},dddd:function(a,b){return b.dayNames[a.getDay()]},M:function(a){return a.getMonth()+1},MM:function(a){return Pa(a.getMonth()+1)},MMM:function(a,b){return b.monthNamesShort[a.getMonth()]},MMMM:function(a,b){return b.monthNames[a.getMonth()]},yy:function(a){return(a.getFullYear()+"").substring(2)},yyyy:function(a){return a.getFullYear()},
116
+ t:function(a){return a.getHours()<12?"a":"p"},tt:function(a){return a.getHours()<12?"am":"pm"},T:function(a){return a.getHours()<12?"A":"P"},TT:function(a){return a.getHours()<12?"AM":"PM"},u:function(a){return Oa(a,"yyyy-MM-dd'T'HH:mm:ss'Z'")},S:function(a){a=a.getDate();if(a>10&&a<20)return"th";return["st","nd","rd"][a%10-1]||"th"}};Aa.applyAll=$a;Ja.month=mc;Ja.basicWeek=nc;Ja.basicDay=oc;wb({weekMode:"fixed"});Ja.agendaWeek=qc;Ja.agendaDay=rc;wb({allDaySlot:true,allDayText:"all-day",firstHour:6,
117
+ slotMinutes:30,defaultEventMinutes:120,axisFormat:"h(:mm)tt",timeFormat:{agenda:"h:mm{ - h:mm}"},dragOpacity:{agenda:0.5},minTime:0,maxTime:24})})(jQuery);
js/inline-help.js ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ //TODO Needs compressing
2
+ jQuery(document).ready(function($) {
3
+ $('.eo-inline-help').each(function() {
4
+ var id = $(this).attr('id').substr(15);
5
+ $(this).click(function(e){e.preventDefault();});
6
+ $(this).qtip({
7
+ content: {
8
+ text: eoHelp[id].content,
9
+ title: {
10
+ text: eoHelp[id].title
11
+ }
12
+ },
13
+ show: {
14
+ solo: true
15
+ },
16
+ hide: 'unfocus',
17
+ style: {
18
+ classes: 'qtip-wiki qtip-light qtip-shadow'
19
+ },
20
+ position : {
21
+ viewport: $(window)
22
+ }
23
+ });
24
+ });
25
+ });
js/qtip2.js ADDED
@@ -0,0 +1,2 @@
 
 
1
+ /*! qTip2 v2.0.0 | http://craigsworks.com/projects/qtip2/ | Licensed MIT, GPL */
2
+ (function(a){"use strict",typeof define=="function"&&define.amd?define(["jquery"],a):jQuery&&!jQuery.fn.qtip&&a(jQuery)})(function(a){function G(){G.history=G.history||[],G.history.push(arguments);if("object"==typeof console){var a=console[console.warn?"warn":"log"],b=Array.prototype.slice.call(arguments),c;typeof arguments[0]=="string"&&(b[0]="qTip2: "+b[0]),c=a.apply?a.apply(console,b):a(b)}}function H(b){var e=function(a){return a===d||"object"!=typeof a},f=function(b){return!a.isFunction(b)&&(!b&&!b.attr||b.length<1||"object"==typeof b&&!b.jquery)};if(!b||"object"!=typeof b)return c;e(b.metadata)&&(b.metadata={type:b.metadata});if("content"in b){if(e(b.content)||b.content.jquery)b.content={text:b.content};f(b.content.text||c)&&(b.content.text=c),"title"in b.content&&(e(b.content.title)&&(b.content.title={text:b.content.title}),f(b.content.title.text||c)&&(b.content.title.text=c))}return"position"in b&&e(b.position)&&(b.position={my:b.position,at:b.position}),"show"in b&&e(b.show)&&(b.show=b.show.jquery?{target:b.show}:{event:b.show}),"hide"in b&&e(b.hide)&&(b.hide=b.hide.jquery?{target:b.hide}:{event:b.hide}),"style"in b&&e(b.style)&&(b.style={classes:b.style}),a.each(r,function(){this.sanitize&&this.sanitize(b)}),b}function I(e,f,n,o){function N(a){var b=0,c,d=f,e=a.split(".");while(d=d[e[b++]])b<e.length&&(c=d);return[c||f,e.pop()]}function O(){var a=f.style.widget;J.toggleClass(v,a).toggleClass(y,f.style.def&&!a),L.content.toggleClass(v+"-content",a),L.titlebar&&L.titlebar.toggleClass(v+"-header",a),L.button&&L.button.toggleClass(u+"-icon",!a)}function P(a){L.title&&(L.titlebar.remove(),L.titlebar=L.title=L.button=d,a!==c&&p.reposition())}function Q(){var b=f.content.title.button,d=typeof b=="string",e=d?b:"Close tooltip";L.button&&L.button.remove(),b.jquery?L.button=b:L.button=a("<a />",{"class":"ui-state-default ui-tooltip-close "+(f.style.widget?"":u+"-icon"),title:e,"aria-label":e}).prepend(a("<span />",{"class":"ui-icon ui-icon-close",html:"&times;"})),L.button.appendTo(L.titlebar).attr("role","button").click(function(a){return J.hasClass(w)||p.hide(a),c}),p.redraw()}function R(){var c=D+"-title";L.titlebar&&P(),L.titlebar=a("<div />",{"class":u+"-titlebar "+(f.style.widget?"ui-widget-header":"")}).append(L.title=a("<div />",{id:c,"class":u+"-title","aria-atomic":b})).insertBefore(L.content).delegate(".ui-tooltip-close","mousedown keydown mouseup keyup mouseout",function(b){a(this).toggleClass("ui-state-active ui-state-focus",b.type.substr(-4)==="down")}).delegate(".ui-tooltip-close","mouseover mouseout",function(b){a(this).toggleClass("ui-state-hover",b.type==="mouseover")}),f.content.title.button?Q():p.rendered&&p.redraw()}function S(a){var b=L.button,d=L.title;if(!p.rendered)return c;a?(d||R(),Q()):b.remove()}function T(b,d){var f=L.title;if(!p.rendered||!b)return c;a.isFunction(b)&&(b=b.call(e,M.event,p));if(b===c||!b&&b!=="")return P(c);b.jquery&&b.length>0?f.empty().append(b.css({display:"block"})):f.html(b),p.redraw(),d!==c&&p.rendered&&J[0].offsetWidth>0&&p.reposition(M.event)}function U(b,d){function g(b){function h(e){e&&(delete g[e.src],clearTimeout(p.timers.img[e.src]),a(e).unbind(K)),a.isEmptyObject(g)&&(p.redraw(),d!==c&&p.reposition(M.event),b())}var e,g={};if((e=f.find("img[src]:not([height]):not([width])")).length===0)return h();e.each(function(b,c){if(g[c.src]!==undefined)return;var d=0,e=3;(function f(){if(c.height||c.width||d>e)return h(c);d+=1,p.timers.img[c.src]=setTimeout(f,700)})(),a(c).bind("error"+K+" load"+K,function(){h(this)}),g[c.src]=c})}var f=L.content;return!p.rendered||!b?c:(a.isFunction(b)&&(b=b.call(e,M.event,p)||""),b.jquery&&b.length>0?f.empty().append(b.css({display:"block"})):f.html(b),p.rendered<0?J.queue("fx",g):(I=0,g(a.noop)),p)}function V(){function j(a){if(J.hasClass(w))return c;clearTimeout(p.timers.show),clearTimeout(p.timers.hide);var d=function(){p.toggle(b,a)};f.show.delay>0?p.timers.show=setTimeout(d,f.show.delay):d()}function k(b){if(J.hasClass(w)||G||I)return c;var e=a(b.relatedTarget||b.target),h=e.closest(x)[0]===J[0],i=e[0]===g.show[0];clearTimeout(p.timers.show),clearTimeout(p.timers.hide);if(d.target==="mouse"&&h||f.hide.fixed&&/mouse(out|leave|move)/.test(b.type)&&(h||i)){try{b.preventDefault(),b.stopImmediatePropagation()}catch(j){}return}f.hide.delay>0?p.timers.hide=setTimeout(function(){p.hide(b)},f.hide.delay):p.hide(b)}function l(a){if(J.hasClass(w))return c;clearTimeout(p.timers.inactive),p.timers.inactive=setTimeout(function(){p.hide(a)},f.hide.inactive)}function m(a){p.rendered&&J[0].offsetWidth>0&&p.reposition(a)}var d=f.position,g={show:f.show.target,hide:f.hide.target,viewport:a(d.viewport),document:a(document),body:a(document.body),window:a(window)},h={show:a.trim(""+f.show.event).split(" "),hide:a.trim(""+f.hide.event).split(" ")},i=a.browser.msie&&parseInt(a.browser.version,10)===6;J.bind("mouseenter"+K+" mouseleave"+K,function(a){var b=a.type==="mouseenter";b&&p.focus(a),J.toggleClass(A,b)}),/mouse(out|leave)/i.test(f.hide.event)&&f.hide.leave==="window"&&g.window.bind("mouseout"+K+" blur"+K,function(a){!/select|option/.test(a.target.nodeName)&&!a.relatedTarget&&p.hide(a)}),f.hide.fixed?(g.hide=g.hide.add(J),J.bind("mouseover"+K,function(){J.hasClass(w)||clearTimeout(p.timers.hide)})):/mouse(over|enter)/i.test(f.show.event)&&g.hide.bind("mouseleave"+K,function(a){clearTimeout(p.timers.show)}),(""+f.hide.event).indexOf("unfocus")>-1&&d.container.closest("html").bind("mousedown"+K,function(b){var c=a(b.target),d=p.rendered&&!J.hasClass(w)&&J[0].offsetWidth>0,f=c.parents(x).filter(J[0]).length>0;c[0]!==e[0]&&c[0]!==J[0]&&!f&&!e.has(c[0]).length&&!c.attr("disabled")&&p.hide(b)}),"number"==typeof f.hide.inactive&&(g.show.bind("qtip-"+n+"-inactive",l),a.each(q.inactiveEvents,function(a,b){g.hide.add(L.tooltip).bind(b+K+"-inactive",l)})),a.each(h.hide,function(b,c){var d=a.inArray(c,h.show),e=a(g.hide);d>-1&&e.add(g.show).length===e.length||c==="unfocus"?(g.show.bind(c+K,function(a){J[0].offsetWidth>0?k(a):j(a)}),delete h.show[d]):g.hide.bind(c+K,k)}),a.each(h.show,function(a,b){g.show.bind(b+K,j)}),"number"==typeof f.hide.distance&&g.show.add(J).bind("mousemove"+K,function(a){var b=M.origin||{},c=f.hide.distance,d=Math.abs;(d(a.pageX-b.pageX)>=c||d(a.pageY-b.pageY)>=c)&&p.hide(a)}),d.target==="mouse"&&(g.show.bind("mousemove"+K,function(a){s={pageX:a.pageX,pageY:a.pageY,type:"mousemove"}}),d.adjust.mouse&&(f.hide.event&&(J.bind("mouseleave"+K,function(a){(a.relatedTarget||a.target)!==g.show[0]&&p.hide(a)}),L.target.bind("mouseenter"+K+" mouseleave"+K,function(a){M.onTarget=a.type==="mouseenter"})),g.document.bind("mousemove"+K,function(a){p.rendered&&M.onTarget&&!J.hasClass(w)&&J[0].offsetWidth>0&&p.reposition(a||s)}))),(d.adjust.resize||g.viewport.length)&&(a.event.special.resize?g.viewport:g.window).bind("resize"+K,m),(g.viewport.length||i&&J.css("position")==="fixed")&&g.viewport.bind("scroll"+K,m)}function W(){var b=[f.show.target[0],f.hide.target[0],p.rendered&&L.tooltip[0],f.position.container[0],f.position.viewport[0],f.position.container.closest("html")[0],window,document];p.rendered?a([]).pushStack(a.grep(b,function(a){return typeof a=="object"})).unbind(K):f.show.target.unbind(K+"-create")}var p=this,C=document.body,D=u+"-"+n,G=0,I=0,J=a(),K=".qtip-"+n,L,M;p.id=n,p.rendered=c,p.destroyed=c,p.elements=L={target:e},p.timers={img:{}},p.options=f,p.checks={},p.plugins={},p.cache=M={event:{},target:a(),disabled:c,attr:o,onTarget:c,lastClass:""},p.checks.builtin={"^id$":function(d,e,f){var g=f===b?q.nextid:f,h=u+"-"+g;g!==c&&g.length>0&&!a("#"+h).length&&(J[0].id=h,L.content[0].id=h+"-content",L.title[0].id=h+"-title")},"^content.text$":function(a,b,c){U(c)},"^content.title.text$":function(a,b,c){if(!c)return P();!L.title&&c&&R(),T(c)},"^content.title.button$":function(a,b,c){S(c)},"^position.(my|at)$":function(a,b,c){"string"==typeof c&&(a[b]=new r.Corner(c))},"^position.container$":function(a,b,c){p.rendered&&J.appendTo(c)},"^show.ready$":function(){p.rendered?p.toggle(b):p.render(1)},"^style.classes$":function(a,b,c){J.attr("class",u+" qtip ui-helper-reset "+c)},"^style.widget|content.title":O,"^events.(render|show|move|hide|focus|blur)$":function(b,c,d){J[(a.isFunction(d)?"":"un")+"bind"]("tooltip"+c,d)},"^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)":function(){var a=f.position;J.attr("tracking",a.target==="mouse"&&a.adjust.mouse),W(),V()}},a.extend(p,{render:function(d){if(p.rendered)return p;var g=f.content.text,h=f.content.title.text,i=f.position,j=a.Event("tooltiprender");return a.attr(e[0],"aria-describedby",D),J=L.tooltip=a("<div/>",{id:D,"class":u+" qtip ui-helper-reset "+y+" "+f.style.classes+" "+u+"-pos-"+f.position.my.abbrev(),width:f.style.width||"",height:f.style.height||"",tracking:i.target==="mouse"&&i.adjust.mouse,role:"alert","aria-live":"polite","aria-atomic":c,"aria-describedby":D+"-content","aria-hidden":b}).toggleClass(w,M.disabled).data("qtip",p).appendTo(f.position.container).append(L.content=a("<div />",{"class":u+"-content",id:D+"-content","aria-atomic":b})),p.rendered=-1,I=1,G=1,h&&(R(),a.isFunction(h)||T(h,c)),a.isFunction(g)||U(g,c),p.rendered=b,O(),a.each(f.events,function(b,c){a.isFunction(c)&&J.bind(b==="toggle"?"tooltipshow tooltiphide":"tooltip"+b,c)}),a.each(r,function(){this.initialize==="render"&&this(p)}),V(),J.queue("fx",function(a){j.originalEvent=M.event,J.trigger(j,[p]),I=0,G=0,p.redraw(),(f.show.ready||d)&&p.toggle(b,M.event,c),a()}),p},get:function(a){var b,c;switch(a.toLowerCase()){case"dimensions":b={height:J.outerHeight(),width:J.outerWidth()};break;case"offset":b=r.offset(J,f.position.container);break;default:c=N(a.toLowerCase()),b=c[0][c[1]],b=b.precedance?b.string():b}return b},set:function(e,g){function n(a,b){var c,d,e;for(c in l)for(d in l[c])if(e=(new RegExp(d,"i")).exec(a))b.push(e),l[c][d].apply(p,b)}var h=/^position\.(my|at|adjust|target|container)|style|content|show\.ready/i,i=/^content\.(title|attr)|style/i,j=c,k=c,l=p.checks,m;return"string"==typeof e?(m=e,e={},e[m]=g):e=a.extend(b,{},e),a.each(e,function(b,c){var d=N(b.toLowerCase()),f;f=d[0][d[1]],d[0][d[1]]="object"==typeof c&&c.nodeType?a(c):c,e[b]=[d[0],d[1],c,f],j=h.test(b)||j,k=i.test(b)||k}),H(f),G=I=1,a.each(e,n),G=I=0,p.rendered&&J[0].offsetWidth>0&&(j&&p.reposition(f.position.target==="mouse"?d:M.event),k&&p.redraw()),p},toggle:function(e,g){function u(){e?(a.browser.msie&&J[0].style.removeAttribute("filter"),J.css("overflow",""),"string"==typeof i.autofocus&&a(i.autofocus,J).focus(),i.target.trigger("qtip-"+n+"-inactive")):J.css({display:"",visibility:"",opacity:"",left:"",top:""}),t=a.Event("tooltip"+(e?"visible":"hidden")),t.originalEvent=g?M.event:d,J.trigger(t,[p])}if(!p.rendered)return e?p.render(1):p;var h=e?"show":"hide",i=f[h],j=f[e?"hide":"show"],k=f.position,l=f.content,m=J[0].offsetWidth>0,o=e||i.target.length===1,q=!g||i.target.length<2||M.target[0]===g.target,r,t;(typeof e).search("boolean|number")&&(e=!m);if(!J.is(":animated")&&m===e&&q)return p;if(g){if(/over|enter/.test(g.type)&&/out|leave/.test(M.event.type)&&f.show.target.add(g.target).length===f.show.target.length&&J.has(g.relatedTarget).length)return p;M.event=a.extend({},g)}return t=a.Event("tooltip"+h),t.originalEvent=g?M.event:d,J.trigger(t,[p,90]),t.isDefaultPrevented()?p:(a.attr(J[0],"aria-hidden",!e),e?(M.origin=a.extend({},s),p.focus(g),a.isFunction(l.text)&&U(l.text,c),a.isFunction(l.title.text)&&T(l.title.text,c),!F&&k.target==="mouse"&&k.adjust.mouse&&(a(document).bind("mousemove.qtip",function(a){s={pageX:a.pageX,pageY:a.pageY,type:"mousemove"}}),F=b),p.reposition(g,arguments[2]),(t.solo=!!i.solo)&&a(x,i.solo).not(J).qtip("hide",t)):(clearTimeout(p.timers.show),delete M.origin,F&&!a(x+'[tracking="true"]:visible',i.solo).not(J).length&&(a(document).unbind("mousemove.qtip"),F=c),p.blur(g)),i.effect===c||o===c?(J[h](),u.call(J)):a.isFunction(i.effect)?(J.stop(1,1),i.effect.call(J,p),J.queue("fx",function(a){u(),a()})):J.fadeTo(90,e?1:0,u),e&&i.target.trigger("qtip-"+n+"-inactive"),p)},show:function(a){return p.toggle(b,a)},hide:function(a){return p.toggle(c,a)},focus:function(b){if(!p.rendered)return p;var c=a(x),d=parseInt(J[0].style.zIndex,10),e=q.zindex+c.length,f=a.extend({},b),g,h;return J.hasClass(z)||(h=a.Event("tooltipfocus"),h.originalEvent=f,J.trigger(h,[p,e]),h.isDefaultPrevented()||(d!==e&&(c.each(function(){this.style.zIndex>d&&(this.style.zIndex=this.style.zIndex-1)}),c.filter("."+z).qtip("blur",f)),J.addClass(z)[0].style.zIndex=e)),p},blur:function(b){var c=a.extend({},b),d;return J.removeClass(z),d=a.Event("tooltipblur"),d.originalEvent=c,J.trigger(d,[p]),p},reposition:function(b,d){if(!p.rendered||G)return p;G=1;var e=f.position.target,g=f.position,h=g.my,n=g.at,o=g.adjust,q=o.method.split(" "),t=J.outerWidth(),u=J.outerHeight(),v=0,w=0,x=a.Event("tooltipmove"),y=J.css("position")==="fixed",z=g.viewport,A={left:0,top:0},B=g.container,C=J[0].offsetWidth>0,D,E,F;if(a.isArray(e)&&e.length===2)n={x:j,y:i},A={left:e[0],top:e[1]};else if(e==="mouse"&&(b&&b.pageX||M.event.pageX))n={x:j,y:i},b=(b&&(b.type==="resize"||b.type==="scroll")?M.event:b&&b.pageX&&b.type==="mousemove"?b:s&&s.pageX&&(o.mouse||!b||!b.pageX)?{pageX:s.pageX,pageY:s.pageY}:!o.mouse&&M.origin&&M.origin.pageX&&f.show.distance?M.origin:b)||b||M.event||s||{},A={top:b.pageY,left:b.pageX};else{e==="event"&&b&&b.target&&b.type!=="scroll"&&b.type!=="resize"?M.target=a(b.target):e!=="event"&&(M.target=a(e.jquery?e:L.target)),e=M.target,e=a(e).eq(0);if(e.length===0)return p;e[0]===document||e[0]===window?(v=r.iOS?window.innerWidth:e.width(),w=r.iOS?window.innerHeight:e.height(),e[0]===window&&(A={top:(z||e).scrollTop(),left:(z||e).scrollLeft()})):r.imagemap&&e.is("area")?D=r.imagemap(p,e,n,r.viewport?q:c):r.svg&&typeof e[0].xmlbase=="string"?D=r.svg(p,e,n,r.viewport?q:c):(v=e.outerWidth(),w=e.outerHeight(),A=r.offset(e,B)),D&&(v=D.width,w=D.height,E=D.offset,A=D.position);if(r.iOS>3.1&&r.iOS<4.1||r.iOS>=4.3&&r.iOS<4.33||!r.iOS&&y)F=a(window),A.left-=F.scrollLeft(),A.top-=F.scrollTop();A.left+=n.x===l?v:n.x===m?v/2:0,A.top+=n.y===k?w:n.y===m?w/2:0}return A.left+=o.x+(h.x===l?-t:h.x===m?-t/2:0),A.top+=o.y+(h.y===k?-u:h.y===m?-u/2:0),r.viewport?(A.adjusted=r.viewport(p,A,g,v,w,t,u),E&&A.adjusted.left&&(A.left+=E.left),E&&A.adjusted.top&&(A.top+=E.top)):A.adjusted={left:0,top:0},x.originalEvent=a.extend({},b),J.trigger(x,[p,A,z.elem||z]),x.isDefaultPrevented()?p:(delete A.adjusted,d===c||!C||isNaN(A.left)||isNaN(A.top)||e==="mouse"||!a.isFunction(g.effect)?J.css(A):a.isFunction(g.effect)&&(g.effect.call(J,p,a.extend({},A)),J.queue(function(b){a(this).css({opacity:"",height:""}),a.browser.msie&&this.style.removeAttribute("filter"),b()})),G=0,p)},redraw:function(){if(p.rendered<1||I)return p;var a=f.position.container,b,c,d,e;return I=1,f.style.height&&J.css(h,f.style.height),f.style.width?J.css(g,f.style.width):(J.css(g,"").addClass(B),c=J.width()+1,d=J.css("max-width")||"",e=J.css("min-width")||"",b=(d+e).indexOf("%")>-1?a.width()/100:0,d=(d.indexOf("%")>-1?b:1)*parseInt(d,10)||c,e=(e.indexOf("%")>-1?b:1)*parseInt(e,10)||0,c=d+e?Math.min(Math.max(c,e),d):c,J.css(g,Math.round(c)).removeClass(B)),I=0,p},disable:function(b){return"boolean"!=typeof b&&(b=!J.hasClass(w)&&!M.disabled),p.rendered?(J.toggleClass(w,b),a.attr(J[0],"aria-disabled",b)):M.disabled=!!b,p},enable:function(){return p.disable(c)},destroy:function(){var c=e[0],d=a.attr(c,E),g=e.data("qtip");p.destroyed=b,p.rendered&&(J.stop(1,0).remove(),a.each(p.plugins,function(){this.destroy&&this.destroy()})),clearTimeout(p.timers.show),clearTimeout(p.timers.hide),W();if(!g||p===g)a.removeData(c,"qtip"),f.suppress&&d&&(a.attr(c,"title",d),e.removeAttr(E)),e.removeAttr("aria-describedby");return e.unbind(".qtip-"+n),delete t[p.id],e}})}function J(e,f){var g,h,i,j,k,l=a(this),m=a(document.body),n=this===document?m:l,o=l.metadata?l.metadata(f.metadata):d,p=f.metadata.type==="html5"&&o?o[f.metadata.name]:d,s=l.data(f.metadata.name||"qtipopts");try{s=typeof s=="string"?a.parseJSON(s):s}catch(t){G("Unable to parse HTML5 attribute data: "+s)}j=a.extend(b,{},q.defaults,f,typeof s=="object"?H(s):d,H(p||o)),h=j.position,j.id=e;if("boolean"==typeof j.content.text){i=l.attr(j.content.attr);if(j.content.attr!==c&&i)j.content.text=i;else return G("Unable to locate content for tooltip! Aborting render of tooltip on element: ",l),c}h.container.length||(h.container=m),h.target===c&&(h.target=n),j.show.target===c&&(j.show.target=n),j.show.solo===b&&(j.show.solo=h.container.closest("body")),j.hide.target===c&&(j.hide.target=n),j.position.viewport===b&&(j.position.viewport=h.container),h.container=h.container.eq(0),h.at=new r.Corner(h.at),h.my=new r.Corner(h.my);if(a.data(this,"qtip"))if(j.overwrite)l.qtip("destroy");else if(j.overwrite===c)return c;return j.suppress&&(k=a.attr(this,"title"))&&a(this).removeAttr("title").attr(E,k).attr("title",""),g=new I(l,j,e,!!i),a.data(this,"qtip",g),l.bind("remove.qtip-"+e+" removeqtip.qtip-"+e,function(){g.destroy()}),g}function K(d){var e=this,f=d.elements.tooltip,g=d.options.content.ajax,h=q.defaults.content.ajax,i=".qtip-ajax",j=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,k=b,l=c,m;d.checks.ajax={"^content.ajax":function(a,b,c){b==="ajax"&&(g=c),b==="once"?e.init():g&&g.url?e.load():f.unbind(i)}},a.extend(e,{init:function(){return g&&g.url&&f.unbind(i)[g.once?"one":"bind"]("tooltipshow"+i,e.load),e},load:function(f){function r(){var e;if(d.destroyed)return;k=c,p&&(l=b,d.show(f.originalEvent)),(e=h.complete||g.complete)&&a.isFunction(e)&&e.apply(g.context||d,arguments)}function s(b,c,e){var f;if(d.destroyed)return;o&&(b=a("<div/>").append(b.replace(j,"")).find(o)),(f=h.success||g.success)&&a.isFunction(f)?f.call(g.context||d,b,c,e):d.set("content.text",b)}function t(a,b,c){if(d.destroyed||a.status===0)return;d.set("content.text",b+": "+c)}if(l){l=c;return}var i=g.url.indexOf(" "),n=g.url,o,p=!g.loading&&k;if(p)try{f.preventDefault()}catch(q){}else if(f&&f.isDefaultPrevented())return e;m&&m.abort&&m.abort(),i>-1&&(o=n.substr(i),n=n.substr(0,i)),m=a.ajax(a.extend({error:h.error||t,context:d},g,{url:n,success:s,complete:r}))},destroy:function(){m&&m.abort&&m.abort(),d.destroyed=b}}),e.init()}function L(a,b,c){var d=Math.ceil(b/2),e=Math.ceil(c/2),f={bottomright:[[0,0],[b,c],[b,0]],bottomleft:[[0,0],[b,0],[0,c]],topright:[[0,c],[b,0],[b,c]],topleft:[[0,0],[0,c],[b,c]],topcenter:[[0,c],[d,0],[b,c]],bottomcenter:[[0,0],[b,0],[d,c]],rightcenter:[[0,0],[b,e],[0,c]],leftcenter:[[b,0],[b,c],[0,e]]};return f.lefttop=f.bottomright,f.righttop=f.bottomleft,f.leftbottom=f.topright,f.rightbottom=f.topleft,f[a.string()]}function M(n,o){function C(){w.width=s.height,w.height=s.width}function D(){w.width=s.width,w.height=s.height}function E(a,d,g,h){if(!t.tip)return;var o=q.corner.clone(),r=g.adjusted,u=n.options.position.adjust.method.split(" "),w=u[0],x=u[1]||u[0],y={left:c,top:c,x:0,y:0},z,A={},B;q.corner.fixed!==b&&(w===p&&o.precedance===e&&r.left&&o.y!==m?o.precedance=o.precedance===e?f:e:w!==p&&r.left&&(o.x=o.x===m?r.left>0?j:l:o.x===j?l:j),x===p&&o.precedance===f&&r.top&&o.x!==m?o.precedance=o.precedance===f?e:f:x!==p&&r.top&&(o.y=o.y===m?r.top>0?i:k:o.y===i?k:i),o.string()!==v.corner.string()&&(v.top!==r.top||v.left!==r.left)&&q.update(o,c)),z=q.position(o,r),z[o.x]+=F(o,o.x,b),z[o.y]+=F(o,o.y,b),z.right!==undefined&&(z.left=-z.right),z.bottom!==undefined&&(z.top=-z.bottom),z.user=Math.max(0,s.offset);if(y.left=w===p&&!!r.left)o.x===m?A["margin-left"]=y.x=z["margin-left"]-r.left:(B=z.right!==undefined?[r.left,-z.left]:[-r.left,z.left],(y.x=Math.max(B[0],B[1]))>B[0]&&(g.left-=r.left,y.left=c),A[z.right!==undefined?l:j]=y.x);if(y.top=x===p&&!!r.top)o.y===m?A["margin-top"]=y.y=z["margin-top"]-r.top:(B=z.bottom!==undefined?[r.top,-z.top]:[-r.top,z.top],(y.y=Math.max(B[0],B[1]))>B[0]&&(g.top-=r.top,y.top=c),A[z.bottom!==undefined?k:i]=y.y);t.tip.css(A).toggle(!(y.x&&y.y||o.x===m&&y.y||o.y===m&&y.x)),g.left-=z.left.charAt?z.user:w!==p||y.top||!y.left&&!y.top?z.left:0,g.top-=z.top.charAt?z.user:x!==p||y.left||!y.left&&!y.top?z.top:0,v.left=r.left,v.top=r.top,v.corner=o.clone()}function F(a,b,c){b=b?b:a[a.precedance];var d=u.hasClass(B),e=t.titlebar&&a.y===i,f=e?t.titlebar:t.tooltip,g="border-"+b+"-width",h;return u.addClass(B),h=parseInt(f.css(g),10),h=(c?h||parseInt(u.css(g),10):h)||0,u.toggleClass(B,d),h}function G(b){function j(a){return parseInt(d.css(a),10)||parseInt(u.css(a),10)}var c=t.titlebar&&b.y===i,d=c?t.titlebar:t.content,e=a.browser.mozilla,f=e?"-moz-":a.browser.webkit?"-webkit-":"",g="border-radius-"+b.y+b.x,h="border-"+b.y+"-"+b.x+"-radius";return j(h)||j(f+h)||j(f+g)||j(g)||0}function H(a){var b=a.precedance===f,c=w[b?g:h],d=w[b?h:g],e=a.string().indexOf(m)>-1,i=c*(e?.5:1),j=Math.pow,k=Math.round,l,n,o,p=Math.sqrt(j(i,2)+j(d,2)),q=[y/i*p,y/d*p];return q[2]=Math.sqrt(j(q[0],2)-j(y,2)),q[3]=Math.sqrt(j(q[1],2)-j(y,2)),l=p+q[2]+q[3]+(e?0:q[0]),n=l/p,o=[k(n*d),k(n*c)],{height:o[b?0:1],width:o[b?1:0]}}var q=this,s=n.options.style.tip,t=n.elements,u=t.tooltip,v={top:0,left:0},w={width:s.width,height:s.height},x={},y=s.border||0,z=".qtip-tip",A=!!(a("<canvas />")[0]||{}).getContext;q.corner=d,q.mimic=d,q.border=y,q.offset=s.offset,q.size=w,n.checks.tip={"^position.my|style.tip.(corner|mimic|border)$":function(){q.init()||q.destroy(),n.reposition()},"^style.tip.(height|width)$":function(){w={width:s.width,height:s.height},q.create(),q.update(),n.reposition()},"^content.title.text|style.(classes|widget)$":function(){t.tip&&t.tip.length&&q.update()}},a.extend(q,{init:function(){var b=q.detectCorner()&&(A||a.browser.msie);return b&&(q.create(),q.update(),u.unbind(z).bind("tooltipmove"+z,E)),b},detectCorner:function(){var a=s.corner,d=n.options.position,e=d.at,f=d.my.string?d.my.string():d.my;return a===c||f===c&&e===c?c:(a===b?q.corner=new r.Corner(f):a.string||(q.corner=new r.Corner(a),q.corner.fixed=b),v.corner=new r.Corner(q.corner.string()),q.corner.string()!=="centercenter")},detectColours:function(b){var c,d,e,f=t.tip.css("cssText",""),g=b||q.corner,h=g[g.precedance],j="border-"+h+"-color",k="border"+h.charAt(0)+h.substr(1)+"Color",l=/rgba?\(0, 0, 0(, 0)?\)|transparent|#123456/i,n="background-color",o="transparent",p=" !important",r=t.titlebar&&(g.y===i||g.y===m&&f.position().top+w.height/2+s.offset<t.titlebar.outerHeight(1)),v=r?t.titlebar:t.tooltip;u.addClass(B),x.fill=d=f.css(n),x.border=e=f[0].style[k]||f.css(j)||u.css(j);if(!d||l.test(d))x.fill=v.css(n)||o,l.test(x.fill)&&(x.fill=u.css(n)||d);if(!e||l.test(e)||e===a(document.body).css("color")){x.border=v.css(j)||o;if(l.test(x.border)||x.border===v.css("color"))x.border=u.css(j)||u.css(k)||e}a("*",f).add(f).css("cssText",n+":"+o+p+";border:0"+p+";"),u.removeClass(B)},create:function(){var b=w.width,c=w.height,d;t.tip&&t.tip.remove(),t.tip=a("<div />",{"class":"ui-tooltip-tip"}).css({width:b,height:c}).prependTo(u),A?a("<canvas />").appendTo(t.tip)[0].getContext("2d").save():(d='<vml:shape coordorigin="0,0" style="display:inline-block; position:absolute; behavior:url(#default#VML);"></vml:shape>',t.tip.html(d+d),a("*",t.tip).bind("click mousedown",function(a){a.stopPropagation()}))},update:function(g,h){var n=t.tip,o=n.children(),p=w.width,z=w.height,B="px solid ",E="px dashed transparent",G=s.mimic,I=Math.round,J,K,M,N,O;g||(g=v.corner||q.corner),G===c?G=g:(G=new r.Corner(G),G.precedance=g.precedance,G.x==="inherit"?G.x=g.x:G.y==="inherit"?G.y=g.y:G.x===G.y&&(G[g.precedance]=g[g.precedance])),J=G.precedance,g.precedance===e?C():D(),t.tip.css({width:p=w.width,height:z=w.height}),q.detectColours(g),x.border!=="transparent"?(y=F(g,d,b),s.border===0&&y>0&&(x.fill=x.border),q.border=y=s.border!==b?s.border:y):q.border=y=0,M=L(G,p,z),q.size=O=H(g),n.css(O),g.precedance===f?N=[I(G.x===j?y:G.x===l?O.width-p-y:(O.width-p)/2),I(G.y===i?O.height-z:0)]:N=[I(G.x===j?O.width-p:0),I(G.y===i?y:G.y===k?O.height-z-y:(O.height-z)/2)],A?(o.attr(O),K=o[0].getContext("2d"),K.restore(),K.save(),K.clearRect(0,0,3e3,3e3),K.fillStyle=x.fill,K.strokeStyle=x.border,K.lineWidth=y*2,K.lineJoin="miter",K.miterLimit=100,K.translate(N[0],N[1]),K.beginPath(),K.moveTo(M[0][0],M[0][1]),K.lineTo(M[1][0],M[1][1]),K.lineTo(M[2][0],M[2][1]),K.closePath(),y&&(u.css("background-clip")==="border-box"&&(K.strokeStyle=x.fill,K.stroke()),K.strokeStyle=x.border,K.stroke()),K.fill()):(M="m"+M[0][0]+","+M[0][1]+" l"+M[1][0]+","+M[1][1]+" "+M[2][0]+","+M[2][1]+" xe",N[2]=y&&/^(r|b)/i.test(g.string())?parseFloat(a.browser.version,10)===8?2:1:0,o.css({antialias:""+(G.string().indexOf(m)>-1),left:N[0]-N[2]*Number(J===e),top:N[1]-N[2]*Number(J===f),width:p+y,height:z+y}).each(function(b){var c=a(this);c[c.prop?"prop":"attr"]({coordsize:p+y+" "+(z+y),path:M,fillcolor:x.fill,filled:!!b,stroked:!b}).css({display:y||b?"block":"none"}),!b&&c.html()===""&&c.html('<vml:stroke weight="'+y*2+'px" color="'+x.border+'" miterlimit="1000" joinstyle="miter" '+' style="behavior:url(#default#VML); display:inline-block;" />')})),h!==c&&q.position(g)},position:function(b){var d=t.tip,k={},l=Math.max(0,s.offset),n,o,p;return s.corner===c||!d?c:(b=b||q.corner,n=b.precedance,o=H(b),p=[b.x,b.y],n===e&&p.reverse(),a.each(p,function(a,c){var d,e;c===m?(d=n===f?j:i,k[d]="50%",k["margin-"+d]=-Math.round(o[n===f?g:h]/2)+l):(d=F(b,c),e=G(b),k[c]=a?0:l+(e>d?e:-d))}),k[b[n]]-=o[n===e?g:h],d.css({top:"",bottom:"",left:"",right:"",margin:""}).css(k),k)},destroy:function(){t.tip&&t.tip.remove(),t.tip=!1,u.unbind(z)}}),q.init()}function N(d){function q(){o=a(n,h).not("[disabled]").map(function(){return typeof this.focus=="function"?this:null})}function s(a){o.length<1&&a.length?a.not("body").blur():o.first().focus()}function t(b){var d=a(b.target),e=d.closest(".qtip"),f;f=e.length<1?c:parseInt(e[0].style.zIndex,10)>parseInt(h[0].style.zIndex,10),!f&&a(b.target).closest(x)[0]!==h[0]&&s(d)}var e=this,f=d.options.show.modal,g=d.elements,h=g.tooltip,i="#qtip-overlay",j=".qtipmodal",k=j+d.id,l="is-modal-qtip",m=a(document.body),n=r.modal.focusable.join(","),o={},p;d.checks.modal={"^show.modal.(on|blur)$":function(){e.init(),g.overlay.toggle(h.is(":visible"))},"^content.text$":function(){q()}},a.extend(e,{init:function(){return f.on?(p=e.create(),h.attr(l,b).css("z-index",r.modal.zindex+a(x+"["+l+"]").length).unbind(j).unbind(k).bind("tooltipshow"+j+" tooltiphide"+j,function(b,c,d){var f=b.originalEvent;if(b.target===h[0])if(f&&b.type==="tooltiphide"&&/mouse(leave|enter)/.test(f.type)&&a(f.relatedTarget).closest(p[0]).length)try{b.preventDefault()}catch(g){}else(!f||f&&!f.solo)&&e[b.type.replace("tooltip","")](b,d)}).bind("tooltipfocus"+j,function(b){if(b.isDefaultPrevented()||b.target!==h[0])return;var c=a(x).filter("["+l+"]"),d=r.modal.zindex+c.length,e=parseInt(h[0].style.zIndex,10);p[0].style.zIndex=d-2,c.each(function(){this.style.zIndex>e&&(this.style.zIndex-=1)}),c.end().filter("."+z).qtip("blur",b.originalEvent),h.addClass(z)[0].style.zIndex=d;try{b.preventDefault()}catch(f){}}).bind("tooltiphide"+j,function(b){b.target===h[0]&&a("["+l+"]").filter(":visible").not(h).last().qtip("focus",b)}),f.escape&&a(document).unbind(k).bind("keydown"+k,function(a){a.keyCode===27&&h.hasClass(z)&&d.hide(a)}),f.blur&&g.overlay.unbind(k).bind("click"+k,function(a){h.hasClass(z)&&d.hide(a)}),q(),e):e},create:function(){function d(){p.css({height:a(window).height(),width:a(window).width()})}var b=a(i);return b.length?g.overlay=b.insertAfter(a(x).last()):(p=g.overlay=a("<div />",{id:i.substr(1),html:"<div></div>",mousedown:function(){return c}}).hide().insertAfter(a(x).last()),a(window).unbind(j).bind("resize"+j,d),d(),p)},toggle:function(d,g,i){if(d&&d.isDefaultPrevented())return e;var j=f.effect,n=g?"show":"hide",o=p.is(":visible"),q=a("["+l+"]").filter(":visible").not(h),r;return p||(p=e.create()),p.is(":animated")&&o===g||!g&&q.length?e:(g?(p.css({left:0,top:0}),p.toggleClass("blurs",f.blur),f.stealfocus!==c&&(m.bind("focusin"+k,t),s(a("body *")))):m.unbind("focusin"+k),p.stop(b,c),a.isFunction(j)?j.call(p,g):j===c?p[n]():p.fadeTo(parseInt(i,10)||90,g?1:0,function(){g||a(this).hide()}),g||p.queue(function(a){p.css({left:"",top:""}),a()}),e)},show:function(a,c){return e.toggle(a,b,c)},hide:function(a,b){return e.toggle(a,c,b)},destroy:function(){var b=p;return b&&(b=a("["+l+"]").not(h).length<1,b?(g.overlay.remove(),a(document).unbind(j)):g.overlay.unbind(j+d.id),m.undelegate("*","focusin"+k)),h.removeAttr(l).unbind(j)}}),e.init()}function O(b){var c=this,d=b.elements,e=d.tooltip,f=".bgiframe-"+b.id;a.extend(c,{init:function(){d.bgiframe=a('<iframe class="ui-tooltip-bgiframe" frameborder="0" tabindex="-1" src="javascript:\'\';" style="display:block; position:absolute; z-index:-1; filter:alpha(opacity=0); -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";"></iframe>'),d.bgiframe.appendTo(e),e.bind("tooltipmove"+f,c.adjust)},adjust:function(){var a=b.get("dimensions"),c=b.plugins.tip,f=d.tip,g,h;h=parseInt(e.css("border-left-width"),10)||0,h={left:-h,top:-h},c&&f&&(g=c.corner.precedance==="x"?["width","left"]:["height","top"],h[g[1]]-=f[g[0]]()),d.bgiframe.css(h).css(a)},destroy:function(){d.bgiframe.remove(),e.unbind(f)}}),c.init()}"use strict";var b=!0,c=!1,d=null,e="x",f="y",g="width",h="height",i="top",j="left",k="bottom",l="right",m="center",n="flip",o="flipinvert",p="shift",q,r,s,t={},u="ui-tooltip",v="ui-widget",w="ui-state-disabled",x="div.qtip."+u,y=u+"-default",z=u+"-focus",A=u+"-hover",B=u+"-fluid",C="-31000px",D="_replacedByqTip",E="oldtitle",F;q=a.fn.qtip=function(e,f,g){var h=(""+e).toLowerCase(),i=d,j=a.makeArray(arguments).slice(1),k=j[j.length-1],l=this[0]?a.data(this[0],"qtip"):d;if(!arguments.length&&l||h==="api")return l;if("string"==typeof e)return this.each(function(){var d=a.data(this,"qtip");if(!d)return b;k&&k.timeStamp&&(d.cache.event=k);if(h!=="option"&&h!=="options"||!f)d[h]&&d[h].apply(d[h],j);else if(a.isPlainObject(f)||g!==undefined)d.set(f,g);else return i=d.get(f),c}),i!==d?i:this;if("object"==typeof e||!arguments.length)return l=H(a.extend(b,{},e)),q.bind.call(this,l,k)},q.bind=function(d,e){return this.each(function(f){function m(b){function d(){k.render(typeof b=="object"||g.show.ready),h.show.add(h.hide).unbind(j)}if(k.cache.disabled)return c;k.cache.event=a.extend({},b),k.cache.target=b?a(b.target):[undefined],g.show.delay>0?(clearTimeout(k.timers.show),k.timers.show=setTimeout(d,g.show.delay),i.show!==i.hide&&h.hide.bind(i.hide,function(){clearTimeout(k.timers.show)})):d()}var g,h,i,j,k,l;l=a.isArray(d.id)?d.id[f]:d.id,l=!l||l===c||l.length<1||t[l]?q.nextid++:t[l]=l,j=".qtip-"+l+"-create",k=J.call(this,l,d);if(k===c)return b;g=k.options,a.each(r,function(){this.initialize==="initialize"&&this(k)}),h={show:g.show.target,hide:g.hide.target},i={show:a.trim(""+g.show.event).replace(/ /g,j+" ")+j,hide:a.trim(""+g.hide.event).replace(/ /g,j+" ")+j},/mouse(over|enter)/i.test(i.show)&&!/mouse(out|leave)/i.test(i.hide)&&(i.hide+=" mouseleave"+j),h.show.bind("mousemove"+j,function(a){s={pageX:a.pageX,pageY:a.pageY,type:"mousemove"},k.cache.onTarget=b}),h.show.bind(i.show,m),(g.show.ready||g.prerender)&&m(e)})},r=q.plugins={Corner:function(a){a=(""+a).replace(/([A-Z])/," $1").replace(/middle/gi,m).toLowerCase(),this.x=(a.match(/left|right/i)||a.match(/center/)||["inherit"])[0].toLowerCase(),this.y=(a.match(/top|bottom|center/i)||["inherit"])[0].toLowerCase();var b=a.charAt(0);this.precedance=b==="t"||b==="b"?f:e,this.string=function(){return this.precedance===f?this.y+this.x:this.x+this.y},this.abbrev=function(){var a=this.x.substr(0,1),b=this.y.substr(0,1);return a===b?a:this.precedance===f?b+a:a+b},this.invertx=function(a){this.x=this.x===j?l:this.x===l?j:a||this.x},this.inverty=function(a){this.y=this.y===i?k:this.y===k?i:a||this.y},this.clone=function(){return{x:this.x,y:this.y,precedance:this.precedance,string:this.string,abbrev:this.abbrev,clone:this.clone,invertx:this.invertx,inverty:this.inverty}}},offset:function(b,c){function j(a,b){d.left+=b*a.scrollLeft(),d.top+=b*a.scrollTop()}var d=b.offset(),e=b.closest("body")[0],f=c,g,h,i;if(f){do f.css("position")!=="static"&&(h=f.position(),d.left-=h.left+(parseInt(f.css("borderLeftWidth"),10)||0)+(parseInt(f.css("marginLeft"),10)||0),d.top-=h.top+(parseInt(f.css("borderTopWidth"),10)||0)+(parseInt(f.css("marginTop"),10)||0),!g&&(i=f.css("overflow"))!=="hidden"&&i!=="visible"&&(g=f));while((f=a(f[0].offsetParent)).length);g&&g[0]!==e&&j(g,1)}return d},iOS:parseFloat((""+(/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent)||[0,""])[1]).replace("undefined","3_2").replace("_",".").replace("_",""))||c,fn:{attr:function(b,c){if(this.length){var d=this[0],e="title",f=a.data(d,"qtip");if(b===e&&f&&"object"==typeof f&&f.options.suppress)return arguments.length<2?a.attr(d,E):(f&&f.options.content.attr===e&&f.cache.attr&&f.set("content.text",c),this.attr(E,c))}return a.fn["attr"+D].apply(this,arguments)},clone:function(b){var c=a([]),d="title",e=a.fn["clone"+D].apply(this,arguments);return b||e.filter("["+E+"]").attr("title",function(){return a.attr(this,E)}).removeAttr(E),e}}},a.each(r.fn,function(c,d){if(!d||a.fn[c+D])return b;var e=a.fn[c+D]=a.fn[c];a.fn[c]=function(){return d.apply(this,arguments)||e.apply(this,arguments)}}),a.ui||(a["cleanData"+D]=a.cleanData,a.cleanData=function(b){for(var c=0,d;(d=b[c])!==undefined;c++)try{a(d).triggerHandler("removeqtip")}catch(e){}a["cleanData"+D](b)}),q.version="@VERSION",q.nextid=0,q.inactiveEvents="click dblclick mousedown mouseup mousemove mouseleave mouseenter".split(" "),q.zindex=15e3,q.defaults={prerender:c,id:c,overwrite:b,suppress:b,content:{text:b,attr:"title",title:{text:c,button:c}},position:{my:"top left",at:"bottom right",target:c,container:c,viewport:c,adjust:{x:0,y:0,mouse:b,resize:b,method:"flip flip"},effect:function(b,d,e){a(this).animate(d,{duration:200,queue:c})}},show:{target:c,event:"mouseenter",effect:b,delay:90,solo:c,ready:c,autofocus:c},hide:{target:c,event:"mouseleave",effect:b,delay:0,fixed:c,inactive:c,leave:"window",distance:c},style:{classes:"",widget:c,width:c,height:c,def:b},events:{render:d,move:d,show:d,hide:d,toggle:d,visible:d,hidden:d,focus:d,blur:d}},r.svg=function(b,c,d,e){var f=a(document),g=c[0],h={width:0,height:0,position:{top:1e10,left:1e10}},i,j,k,l,m;while(!g.getBBox)g=g.parentNode;if(g.getBBox&&g.parentNode){i=g.getBBox(),j=g.getScreenCTM(),k=g.farthestViewportElement||g;if(!k.createSVGPoint)return h;l=k.createSVGPoint(),l.x=i.x,l.y=i.y,m=l.matrixTransform(j),h.position.left=m.x,h.position.top=m.y,l.x+=i.width,l.y+=i.height,m=l.matrixTransform(j),h.width=m.x-h.position.left,h.height=m.y-h.position.top,h.position.left+=f.scrollLeft(),h.position.top+=f.scrollTop()}return h},r.ajax=function(a){var b=a.plugins.ajax;return"object"==typeof b?b:a.plugins.ajax=new K(a)},r.ajax.initialize="render",r.ajax.sanitize=function(a){var b=a.content,c;b&&"ajax"in b&&(c=b.ajax,typeof c!="object"&&(c=a.content.ajax={url:c}),"boolean"!=typeof c.once&&c.once&&(c.once=!!c.once))},a.extend(b,q.defaults,{content:{ajax:{loading:b,once:b}}}),r.tip=function(a){var b=a.plugins.tip;return"object"==typeof b?b:a.plugins.tip=new M(a)},r.tip.initialize="render",r.tip.sanitize=function(a){var c=a.style,d;c&&"tip"in c&&(d=a.style.tip,typeof d!="object"&&(a.style.tip={corner:d}),/string|boolean/i.test(typeof d.corner)||(d.corner=b),typeof d.width!="number"&&delete d.width,typeof d.height!="number"&&delete d.height,typeof d.border!="number"&&d.border!==b&&delete d.border,typeof d.offset!="number"&&delete d.offset)},a.extend(b,q.defaults,{style:{tip:{corner:b,mimic:c,width:6,height:6,border:b,offset:0}}}),r.modal=function(a){var b=a.plugins.modal;return"object"==typeof b?b:a.plugins.modal=new N(a)},r.modal.initialize="render",r.modal.sanitize=function(a){a.show&&(typeof a.show.modal!="object"?a.show.modal={on:!!a.show.modal}:typeof a.show.modal.on=="undefined"&&(a.show.modal.on=b))},r.modal.zindex=q.zindex-200,r.modal.focusable=["a[href]","area[href]","input","select","textarea","button","iframe","object","embed","[tabindex]","[contenteditable]"],a.extend(b,q.defaults,{show:{modal:{on:c,effect:b,blur:b,stealfocus:b,escape:b}}}),r.viewport=function(a,b,c,d,n,q,r){function J(a,c,d,e,f,g,h,i,j){var k=b[f],l=v[a],n=w[a],q=d===p,r=-C.offset[f]+B.offset[f]+B["scroll"+f],s=l===f?j:l===g?-j:-j/2,t=n===f?i:n===g?-i:-i/2,u=E&&E.size?E.size[h]||0:0,x=E&&E.corner&&E.corner.precedance===a&&!q?u:0,y=r-k+x,z=k+j-B[h]-r+x,A=s-(v.precedance===a||l===v[c]?t:0)-(n===m?i/2:0);return q?(x=E&&E.corner&&E.corner.precedance===c?u:0,A=(l===f?1:-1)*s-x,b[f]+=y>0?y:z>0?-z:0,b[f]=Math.max(-C.offset[f]+B.offset[f]+(x&&E.corner[a]===m?E.offset:0),k-A,Math.min(Math.max(-C.offset[f]+B.offset[f]+B[h],k+A),b[f]))):(e*=d===o?2:0,y>0&&(l!==f||z>0)?(b[f]-=A+e,H["invert"+a](f)):z>0&&(l!==g||y>0)&&(b[f]-=(l===m?-A:A)+e,H["invert"+a](g)),b[f]<r&&-b[f]>z&&(b[f]=k,H=undefined)),b[f]-k}var s=c.target,t=a.elements.tooltip,v=c.my,w=c.at,x=c.adjust,y=x.method.split(" "),z=y[0],A=y[1]||y[0],B=c.viewport,C=c.container,D=a.cache,E=a.plugins.tip,F={left:0,top:0},G,H,I;if(!B.jquery||s[0]===window||s[0]===document.body||x.method==="none")return F;G=t.css("position")==="fixed",B={elem:B,height:B[(B[0]===window?"h":"outerH")+"eight"](),width:B[(B[0]===window?"w":"outerW")+"idth"](),scrollleft:G?0:B.scrollLeft(),scrolltop:G?0:B.scrollTop(),offset:B.offset()||{left:0,top:0}},C={elem:C,scrollLeft:C.scrollLeft(),scrollTop:C.scrollTop(),offset:C.offset()||{left:0,top:0}};if(z!=="shift"||A!=="shift")H=v.clone();return F={left:z!=="none"?J(e,f,z,x.x,j,l,g,d,q):0,top:A!=="none"?J(f,e,A,x.y,i,k,h,n,r):0},H&&D.lastClass!==(I=u+"-pos-"+H.abbrev())&&t.removeClass(a.cache.lastClass).addClass(a.cache.lastClass=I),F},r.imagemap=function(b,c,d,e){function v(a,b,c){var d=0,e=1,f=1,g=0,h=0,n=a.width,o=a.height;while(n>0&&o>0&&e>0&&f>0){n=Math.floor(n/2),o=Math.floor(o/2),c.x===j?e=n:c.x===l?e=a.width-n:e+=Math.floor(n/2),c.y===i?f=o:c.y===k?f=a.height-o:f+=Math.floor(o/2),d=b.length;while(d--){if(b.length<2)break;g=b[d][0]-a.position.left,h=b[d][1]-a.position.top,(c.x===j&&g>=e||c.x===l&&g<=e||c.x===m&&(g<e||g>a.width-e)||c.y===i&&h>=f||c.y===k&&h<=f||c.y===m&&(h<f||h>a.height-f))&&b.splice(d,1)}}return{left:b[0][0],top:b[0][1]}}c.jquery||(c=a(c));var f=b.cache.areas={},g=(c[0].shape||c.attr("shape")).toLowerCase(),h=c[0].coords||c.attr("coords"),n=h.split(","),o=[],p=a('img[usemap="#'+c.parent("map").attr("name")+'"]'),q=p.offset(),r={width:0,height:0,position:{top:1e10,right:0,bottom:0,left:1e10}},s=0,t=0,u;q.left+=Math.ceil((p.outerWidth()-p.width())/2),q.top+=Math.ceil((p.outerHeight()-p.height())/2);if(g==="poly"){s=n.length;while(s--)t=[parseInt(n[--s],10),parseInt(n[s+1],10)],t[0]>r.position.right&&(r.position.right=t[0]),t[0]<r.position.left&&(r.position.left=t[0]),t[1]>r.position.bottom&&(r.position.bottom=t[1]),t[1]<r.position.top&&(r.position.top=t[1]),o.push(t)}else{s=-1;while(s++<n.length)o.push(parseInt(n[s],10))}switch(g){case"rect":r={width:Math.abs(o[2]-o[0]),height:Math.abs(o[3]-o[1]),position:{left:Math.min(o[0],o[2]),top:Math.min(o[1],o[3])}};break;case"circle":r={width:o[2]+2,height:o[2]+2,position:{left:o[0],top:o[1]}};break;case"poly":r.width=Math.abs(r.position.right-r.position.left),r.height=Math.abs(r.position.bottom-r.position.top),d.abbrev()==="c"?r.position={left:r.position.left+r.width/2,top:r.position.top+r.height/2}:(f[d+h]||(r.position=v(r,o.slice(),d),e&&(e[0]==="flip"||e[1]==="flip")&&(r.offset=v(r,o.slice(),{x:d.x===j?l:d.x===l?j:m,y:d.y===i?k:d.y===k?i:m}),r.offset.left-=r.position.left,r.offset.top-=r.position.top),f[d+h]=r),r=f[d+h]),r.width=r.height=0}return r.position.left+=q.left,r.position.top+=q.top,r},r.bgiframe=function(b){var d=a.browser,e=b.plugins.bgiframe;return a("select, object").length<1||!d.msie||(""+d.version).charAt(0)!=="6"?c:"object"==typeof e?e:b.plugins.bgiframe=new O(b)},r.bgiframe.initialize="render"});
js/venues.js ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var map;
2
+ var marker;
3
+ jQuery(document).ready(function () {
4
+ if (typeof EO_Venue != 'undefined') {
5
+ postboxes.add_postbox_toggles(pagenow);
6
+ }
7
+
8
+ if (typeof google !== "undefined") {
9
+ var eo_venue_Lat = jQuery("#eo_venue_Lat").val();
10
+ var eo_venue_Lng = jQuery("#eo_venue_Lng").val();
11
+ if (typeof eo_venue_Lat !== "undefined" && typeof eo_venue_Lng !== "undefined") {
12
+ eo_initialize_map(eo_venue_Lat, eo_venue_Lng);
13
+
14
+ if( eo_venue_Lat == 0 && eo_venue_Lng == 0 ){
15
+ if( typeof EO_Venue != 'undefined'){
16
+ var address = EO_Venue.location.split("/");
17
+ address = address[address.length-1];
18
+ }
19
+ if( typeof address != 'undefined' && address ){
20
+ eventorganiser_code_address(address);
21
+ }else{
22
+ map.setZoom(1);
23
+ }
24
+ }
25
+ jQuery(".eo_addressInput").change(function () {
26
+ var address = [];
27
+ jQuery(".eo_addressInput").each(function () {
28
+ address.push(jQuery(this).val());
29
+ });
30
+ eventorganiser_code_address(address.join(', '))
31
+ })
32
+ }
33
+ }
34
+ });
35
+
36
+ function eo_initialize_map(Lat, Lng) {
37
+
38
+ if (typeof google !== "undefined") {
39
+ var latlng = new google.maps.LatLng(Lat, Lng);
40
+
41
+ var myOptions = {
42
+ zoom: 15,
43
+ center: latlng,
44
+ mapTypeId: google.maps.MapTypeId.ROADMAP
45
+ };
46
+
47
+ map = new google.maps.Map(document.getElementById("venuemap"), myOptions);
48
+
49
+ if (typeof EO_Venue != 'undefined') {
50
+ var draggable = true
51
+ } else {
52
+ var draggable = false
53
+ }
54
+
55
+ marker = new google.maps.Marker({
56
+ position: latlng,
57
+ map: map,
58
+ draggable: draggable
59
+ });
60
+
61
+ if (typeof EO_Venue != 'undefined') {
62
+ google.maps.event.addListener(marker, 'dragend', function (evt) {
63
+ jQuery("#eo_venue_Lat").val(evt.latLng.lat().toFixed(6));
64
+ jQuery("#eo_venue_Lng").val(evt.latLng.lng().toFixed(6));
65
+ map.setCenter(marker.position)
66
+ })
67
+ }
68
+ }
69
+ }
70
+
71
+ function eventorganiser_code_address(addrStr) {
72
+ var geocoder = new google.maps.Geocoder();
73
+ geocoder.geocode({'address': addrStr}, function (results, status) {
74
+ if ( status == google.maps.GeocoderStatus.OK){
75
+
76
+ marker.setMap(null);
77
+ map.setCenter(results[0].geometry.location);
78
+ if (typeof EO_Venue != 'undefined') {
79
+ var draggable = true
80
+ } else {
81
+ var draggable = false
82
+ }
83
+ marker = new google.maps.Marker({
84
+ map: map,
85
+ position: results[0].geometry.location,
86
+ draggable: draggable
87
+ });
88
+ map.setZoom(15);
89
+ if (typeof EO_Venue != 'undefined') {
90
+ google.maps.event.addListener(marker, 'dragend', function (evt) {
91
+ jQuery("#eo_venue_Lat").val(evt.latLng.lat().toFixed(6));
92
+ jQuery("#eo_venue_Lng").val(evt.latLng.lng().toFixed(6));
93
+ map.setCenter(marker.position)
94
+ })
95
+ }
96
+ jQuery("#eo_venue_Lat").val(results[0].geometry.location.lat());
97
+ jQuery("#eo_venue_Lng").val(results[0].geometry.location.lng());
98
+ }
99
+ })
100
+ }
js/venues.min.js ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ var map,marker;
2
+ jQuery(document).ready(function(){typeof EO_Venue!="undefined"&&postboxes.add_postbox_toggles(pagenow);if(typeof google!=="undefined"){var c=jQuery("#eo_venue_Lat").val(),a=jQuery("#eo_venue_Lng").val();if(typeof c!=="undefined"&&typeof a!=="undefined"){eo_initialize_map(c,a);if(c==0&&a==0){if(typeof EO_Venue!="undefined"){var b=EO_Venue.location.split("/");b=b[b.length-1]}typeof b!="undefined"&&b?eventorganiser_code_address(b):map.setZoom(1)}jQuery(".eo_addressInput").change(function(){var d=[];
3
+ jQuery(".eo_addressInput").each(function(){d.push(jQuery(this).val())});eventorganiser_code_address(d.join(", "))})}}});
4
+ function eo_initialize_map(c,a){if(typeof google!=="undefined"){c=new google.maps.LatLng(c,a);a={zoom:15,center:c,mapTypeId:google.maps.MapTypeId.ROADMAP};map=new google.maps.Map(document.getElementById("venuemap"),a);marker=new google.maps.Marker({position:c,map:map,draggable:typeof EO_Venue!="undefined"?true:false});typeof EO_Venue!="undefined"&&google.maps.event.addListener(marker,"dragend",function(b){jQuery("#eo_venue_Lat").val(b.latLng.lat().toFixed(6));jQuery("#eo_venue_Lng").val(b.latLng.lng().toFixed(6));
5
+ map.setCenter(marker.position)})}}
6
+ function eventorganiser_code_address(c){(new google.maps.Geocoder).geocode({address:c},function(a,b){if(b==google.maps.GeocoderStatus.OK){marker.setMap(null);map.setCenter(a[0].geometry.location);marker=new google.maps.Marker({map:map,position:a[0].geometry.location,draggable:typeof EO_Venue!="undefined"?true:false});map.setZoom(15);typeof EO_Venue!="undefined"&&google.maps.event.addListener(marker,"dragend",function(d){jQuery("#eo_venue_Lat").val(d.latLng.lat().toFixed(6));jQuery("#eo_venue_Lng").val(d.latLng.lng().toFixed(6));
7
+ map.setCenter(marker.position)});jQuery("#eo_venue_Lat").val(a[0].geometry.location.lat());jQuery("#eo_venue_Lng").val(a[0].geometry.location.lng())}})};
languages/eventorganiser-da_DK.mo ADDED
Binary file
languages/eventorganiser-da_DK.po ADDED
@@ -0,0 +1,1538 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ msgid ""
2
+ msgstr ""
3
+ "Project-Id-Version: Event Organiser\n"
4
+ "Report-Msgid-Bugs-To: \n"
5
+ "POT-Creation-Date: 2012-04-04 01:40+0100\n"
6
+ "PO-Revision-Date: 2012-09-02 00:57+0100\n"
7
+ "Last-Translator: Intox Studio <jv@intox.dk>\n"
8
+ "Language-Team: Intox Studio <jv@intox.dk>\n"
9
+ "Language: da_DK\n"
10
+ "MIME-Version: 1.0\n"
11
+ "Content-Type: text/plain; charset=UTF-8\n"
12
+ "Content-Transfer-Encoding: 8bit\n"
13
+ "X-Poedit-KeywordsList: __;_e;ngettext;_n\n"
14
+ "X-Poedit-Basepath: .\n"
15
+ "Plural-Forms: nplurals=2; plural=n != 1;\n"
16
+ "X-Poedit-SearchPath-0: ..\n"
17
+
18
+ #: ../event-organiser-manage.php:18 ../includes/event-organiser-cpt.php:130
19
+ msgid "Event"
20
+ msgstr "Begivenhed"
21
+
22
+ #: ../event-organiser-manage.php:22 ../event-organiser-settings.php:180
23
+ #: ../event-organiser-edit.php:27 ../includes/event-organiser-ajax.php:180
24
+ msgid "Organiser"
25
+ msgstr "Arrangør"
26
+
27
+ #: ../event-organiser-manage.php:24 ../event-organiser-venues.php:24
28
+ #: ../event-organiser-edit.php:145
29
+ #: ../classes/class-eo-event-list-widget.php:43
30
+ msgid "Venue"
31
+ msgstr "Sted"
32
+
33
+ #: ../event-organiser-manage.php:25 ../includes/event-organiser-cpt.php:67
34
+ msgid "Categories"
35
+ msgstr "Kategorier"
36
+
37
+ #: ../event-organiser-manage.php:26 ../event-organiser-edit.php:69
38
+ msgid "Start Date/Time"
39
+ msgstr "Startdato/-tid"
40
+
41
+ #: ../event-organiser-manage.php:27 ../event-organiser-edit.php:76
42
+ msgid "End Date/Time"
43
+ msgstr "Slutdato/-tid"
44
+
45
+ #: ../event-organiser-manage.php:28
46
+ msgid "Reoccurrence"
47
+ msgstr "Gentagelse"
48
+
49
+ #: ../event-organiser-manage.php:109 ../event-organiser-calendar.php:48
50
+ #: ../includes/event-organiser-register.php:26
51
+ msgid "View all categories"
52
+ msgstr "Vis alle kategorier"
53
+
54
+ #: ../event-organiser-manage.php:123 ../event-organiser-calendar.php:49
55
+ #: ../includes/event-organiser-register.php:27
56
+ msgid "View all venues"
57
+ msgstr "Vis alle steder"
58
+
59
+ #: ../event-organiser-manage.php:136
60
+ msgid "View all events"
61
+ msgstr "Vis alle begivenheder"
62
+
63
+ #: ../event-organiser-manage.php:137
64
+ msgid "Future events"
65
+ msgstr "Fremtidige begivenheder"
66
+
67
+ #: ../event-organiser-manage.php:138
68
+ msgid "Expired events"
69
+ msgstr "Udløbne begivenheder"
70
+
71
+ #: ../event-organiser-manage.php:139
72
+ msgid "Events within 24 hours"
73
+ msgstr "Begivenheder inden for 24 timer"
74
+
75
+ #: ../event-organiser-manage.php:140
76
+ msgid "Events within 1 week"
77
+ msgstr "Begivenheder inden for 1 uge"
78
+
79
+ #: ../event-organiser-manage.php:141
80
+ #, php-format
81
+ msgid "Events within %d weeks"
82
+ msgstr "Begivenheder inden for %d uger"
83
+
84
+ #: ../event-organiser-manage.php:142
85
+ msgid "Events within 1 month"
86
+ msgstr "Begivenheder inden for 1 måned"
87
+
88
+ #: ../event-organiser-manage.php:143
89
+ #, php-format
90
+ msgid "Events within %d months"
91
+ msgstr "Begivenheder inden for %d måneder"
92
+
93
+ #: ../event-organiser-manage.php:144
94
+ msgid "Events within 1 year"
95
+ msgstr "Begivenheder inden for 1 år"
96
+
97
+ #: ../event-organiser-manage.php:189
98
+ msgid "&mdash; No Change &mdash;"
99
+ msgstr "&mdash; Ingen ændringer &mdash;"
100
+
101
+ #: ../event-organiser.php:51 ../event-organiser-settings.php:182
102
+ msgid "Edit Events"
103
+ msgstr "Rediger begivenheder"
104
+
105
+ #: ../event-organiser.php:52 ../event-organiser-settings.php:183
106
+ msgid "Publish Events"
107
+ msgstr "Udgiv begivenheder"
108
+
109
+ #: ../event-organiser.php:53 ../event-organiser-settings.php:184
110
+ msgid "Delete Events"
111
+ msgstr "Slet begivenheder"
112
+
113
+ #: ../event-organiser.php:54 ../event-organiser-settings.php:185
114
+ msgid "Edit Others' Events"
115
+ msgstr "Rediger andres begivenheder"
116
+
117
+ #: ../event-organiser.php:55 ../event-organiser-settings.php:186
118
+ msgid "Delete Other's Events"
119
+ msgstr "Slet andres begivenheder"
120
+
121
+ #: ../event-organiser.php:56 ../event-organiser-settings.php:187
122
+ msgid "Read Private Events"
123
+ msgstr "Læs private begivenheder"
124
+
125
+ #: ../event-organiser.php:57 ../event-organiser-settings.php:188
126
+ msgid "Manage Venues"
127
+ msgstr "Håndter steder"
128
+
129
+ #: ../event-organiser.php:58 ../event-organiser-settings.php:189
130
+ msgid "Manage Event Categories & Tags"
131
+ msgstr "Håndter begivenhedskategorier og -tags"
132
+
133
+ #: ../event-organiser-venues.php:10 ../event-organiser-venues.php:11
134
+ #: ../event-organiser-venues.php:80 ../event-organiser-venues.php:136
135
+ #: ../event-organiser-settings.php:426
136
+ msgid "Venues"
137
+ msgstr "Steder"
138
+
139
+ #: ../event-organiser-venues.php:25 ../event-organiser-venues.php:204
140
+ msgid "Address"
141
+ msgstr "Adresse"
142
+
143
+ #: ../event-organiser-venues.php:26 ../event-organiser-venues.php:208
144
+ msgid "Post Code"
145
+ msgstr "Postnummer"
146
+
147
+ #: ../event-organiser-venues.php:27 ../event-organiser-venues.php:212
148
+ msgid "Country"
149
+ msgstr "Land"
150
+
151
+ #: ../event-organiser-venues.php:28
152
+ msgid "Slug"
153
+ msgstr "Kort titel"
154
+
155
+ #: ../event-organiser-venues.php:29 ../event-organiser-settings.php:102
156
+ #: ../event-organiser-settings.php:313 ../event-organiser-settings.php:419
157
+ #: ../includes/event-organiser-install.php:63
158
+ #: ../includes/event-organiser-install.php:146
159
+ #: ../includes/event-organiser-cpt.php:129
160
+ #: ../includes/event-organiser-cpt.php:141
161
+ #: ../classes/class-eo-event-list-widget.php:23
162
+ msgid "Events"
163
+ msgstr "Begivenheder"
164
+
165
+ #: ../event-organiser-venues.php:49
166
+ msgid "You do not have permission to manage venues"
167
+ msgstr "Du har ikke rettigheder til at håndtere steder"
168
+
169
+ #: ../event-organiser-venues.php:54 ../event-organiser-venues.php:61
170
+ msgid "You do not have permission to edit this venue."
171
+ msgstr "Du har ikke rettigheder til at redigere dette sted."
172
+
173
+ #: ../event-organiser-venues.php:73
174
+ msgid "You do not have permission to delete this venue"
175
+ msgstr "Du har ikke rettigheder til at slette dette sted"
176
+
177
+ #: ../event-organiser-venues.php:110 ../includes/event-organiser-cpt.php:30
178
+ msgid "Edit Venue"
179
+ msgstr "Rediger sted"
180
+
181
+ #: ../event-organiser-venues.php:114 ../includes/event-organiser-cpt.php:32
182
+ msgid "Add New Venue"
183
+ msgstr "Tilføj nyt sted"
184
+
185
+ #: ../event-organiser-venues.php:139
186
+ #, php-format
187
+ msgid "Search results for &#8220;%s&#8221;"
188
+ msgstr "Søgeresultater for &#8220;%s&#8221;"
189
+
190
+ #: ../event-organiser-venues.php:149 ../includes/event-organiser-cpt.php:28
191
+ msgid "Search Venues"
192
+ msgstr "Søg steder"
193
+
194
+ #: ../event-organiser-venues.php:180
195
+ msgid "Venue name"
196
+ msgstr "Stednavn"
197
+
198
+ #: ../event-organiser-venues.php:185
199
+ msgid "Permalink:"
200
+ msgstr "Permanent link:"
201
+
202
+ #: ../event-organiser-venues.php:192
203
+ msgid "Get Link"
204
+ msgstr "Vis link"
205
+
206
+ #: ../event-organiser-venues.php:193
207
+ msgid "View Venue"
208
+ msgstr "Vis sted"
209
+
210
+ #: ../event-organiser-venues.php:200
211
+ msgid "Venue Location"
212
+ msgstr "Stedlokation"
213
+
214
+ #: ../event-organiser-venues.php:229 ../includes/event-organiser-cpt.php:31
215
+ msgid "Update Venue"
216
+ msgstr "Opdater sted"
217
+
218
+ #: ../event-organiser-venues.php:229
219
+ msgid "Add Venue"
220
+ msgstr "Tilføj sted"
221
+
222
+ #: ../event-organiser-settings.php:16
223
+ msgid "Event Organiser Settings"
224
+ msgstr "Indstillinger for begivenhedsarrangør"
225
+
226
+ #: ../event-organiser-settings.php:28 ../event-organiser-settings.php:326
227
+ msgid "dd-mm-yyyy"
228
+ msgstr "dd-mm-yyyy"
229
+
230
+ #: ../event-organiser-settings.php:29 ../event-organiser-settings.php:327
231
+ msgid "mm-dd-yyyy"
232
+ msgstr "mm-dd-yyyy"
233
+
234
+ #: ../event-organiser-settings.php:41 ../event-organiser-settings.php:353
235
+ msgid "Yes"
236
+ msgstr "Ja"
237
+
238
+ #: ../event-organiser-settings.php:42 ../event-organiser-settings.php:355
239
+ msgid "No"
240
+ msgstr "Nej"
241
+
242
+ #: ../event-organiser-settings.php:180
243
+ msgid "Author"
244
+ msgstr "Forfatter"
245
+
246
+ #: ../event-organiser-settings.php:180
247
+ msgid "Thumbnail"
248
+ msgstr "Thumbnail"
249
+
250
+ #: ../event-organiser-settings.php:180
251
+ msgid "Excerpt"
252
+ msgstr "Uddrag"
253
+
254
+ #: ../event-organiser-settings.php:180
255
+ msgid "Custom Fields"
256
+ msgstr "Egne felter"
257
+
258
+ #: ../event-organiser-settings.php:180
259
+ msgid "Comments"
260
+ msgstr "Kommentarer"
261
+
262
+ #: ../event-organiser-settings.php:180
263
+ msgid "Revisions"
264
+ msgstr "Ændringer"
265
+
266
+ #: ../event-organiser-settings.php:200
267
+ msgid "Event Settings"
268
+ msgstr "Begivenhedsindstillinger"
269
+
270
+ #: ../event-organiser-settings.php:201
271
+ msgid "General"
272
+ msgstr "Generelt"
273
+
274
+ #: ../event-organiser-settings.php:202
275
+ msgid "Permissions"
276
+ msgstr "Rettigheder"
277
+
278
+ #: ../event-organiser-settings.php:203
279
+ msgid "Permalinks"
280
+ msgstr "Permanente links"
281
+
282
+ #: ../event-organiser-settings.php:204
283
+ msgid "Import"
284
+ msgstr "Importer"
285
+
286
+ #: ../event-organiser-settings.php:204
287
+ msgid "Export"
288
+ msgstr "Eksporter"
289
+
290
+ #: ../event-organiser-settings.php:234
291
+ msgid "Save Changes"
292
+ msgstr "Gem ændringer"
293
+
294
+ #: ../event-organiser-settings.php:242
295
+ msgid "Set permissions for events and venue management"
296
+ msgstr "Opsæt rettigheder for håndtering af begivenheder og steder"
297
+
298
+ #: ../event-organiser-settings.php:247
299
+ msgid "Role"
300
+ msgstr "Rolle"
301
+
302
+ #: ../event-organiser-settings.php:258
303
+ msgid "None"
304
+ msgstr "Ingen"
305
+
306
+ #: ../event-organiser-settings.php:279
307
+ msgid "Select which features events should support"
308
+ msgstr "Vælg hvilke egenskaber begivenheder skal understøtte"
309
+
310
+ #: ../event-organiser-settings.php:295 ../event-organiser-settings.php:440
311
+ #: ../includes/event-organiser-cpt.php:88
312
+ msgid "Event Tags"
313
+ msgstr "Begivenhedstags"
314
+
315
+ #: ../event-organiser-settings.php:301
316
+ msgid "Add an 'events' link to the navigation menu:"
317
+ msgstr "Tilføj et link for 'begivenheder' til menuen:"
318
+
319
+ #: ../event-organiser-settings.php:306
320
+ msgid "Do not add to menu"
321
+ msgstr "Tilføj ikke til menu"
322
+
323
+ #: ../event-organiser-settings.php:310
324
+ msgid "Page list (fallback)"
325
+ msgstr "Sideliste (tilbagefald)"
326
+
327
+ #: ../event-organiser-settings.php:318
328
+ msgid "(This may not work with some themes):"
329
+ msgstr "(Dette virker muligvis ikke med nogle temaer):"
330
+
331
+ #: ../event-organiser-settings.php:322
332
+ msgid "Date Format:"
333
+ msgstr "Dato-format:"
334
+
335
+ #: ../event-organiser-settings.php:329
336
+ msgid "This alters the default format for inputting dates."
337
+ msgstr "Dette ændrer standardformatet for at indtaste datoer."
338
+
339
+ #: ../event-organiser-settings.php:334
340
+ msgid "Show past events:"
341
+ msgstr "Vis tidligere begivenheder:"
342
+
343
+ #: ../event-organiser-settings.php:337
344
+ msgid ""
345
+ "Display past events on calendars, event lists and archives (this can be over-"
346
+ "ridden by shortcode attributes and widget options)."
347
+ msgstr ""
348
+ "Vis overståede begivenheder i kalendere, lister og arkiver (dette kan "
349
+ "tilsidesættes af shortcode-attributter og widget-indstillinger)."
350
+
351
+ #: ../event-organiser-settings.php:341
352
+ #: ../classes/class-eo-event-list-widget.php:69
353
+ msgid "Group occurrences"
354
+ msgstr "Gruppér gentagelser"
355
+
356
+ #: ../event-organiser-settings.php:345
357
+ msgid ""
358
+ "If selected only one occurrence of an event will be displayed on event lists "
359
+ "and archives (this can be over-ridden by shortcode attributes and widget "
360
+ "options)."
361
+ msgstr ""
362
+ "Hvis valgt vil kun én forekomst af en begivenhed blive vist på "
363
+ "begivenhedslister og -arkiver (dette kan tilsidesættes af shortcode-"
364
+ "attributter og widget-indstillinger)."
365
+
366
+ #: ../event-organiser-settings.php:349
367
+ msgid "Are current events past?"
368
+ msgstr "Er igangværende begivenheder overståede?"
369
+
370
+ #: ../event-organiser-settings.php:356
371
+ msgid ""
372
+ "If 'no' is selected, an occurrence of an event is only past when it has "
373
+ "finished. Otherwise, an occurrence is considered 'past' as soon as it starts."
374
+ msgstr ""
375
+ "Hvis 'nej' er valgt, er en forekomst af en begivenhed først overstået, når "
376
+ "den er slut. Ellers vil en begivenhed betragtes som 'overstået', så snart "
377
+ "den starter."
378
+
379
+ #: ../event-organiser-settings.php:360
380
+ msgid "Delete expired events:"
381
+ msgstr "Slet udløbne begivenheder:"
382
+
383
+ #: ../event-organiser-settings.php:363
384
+ msgid ""
385
+ "If selected the event will be automatically trashed 24 hours after the last "
386
+ "occurrence finishes."
387
+ msgstr ""
388
+ "Hvis valgt, vil begivenheden automatisk blive lagt i papirkurven 24 timer "
389
+ "efter den sidste gentagelse slutter."
390
+
391
+ #: ../event-organiser-settings.php:367
392
+ msgid "Enable events ICAL feed:"
393
+ msgstr "Aktiver ICAL-feed for begivenheder:"
394
+
395
+ #: ../event-organiser-settings.php:370
396
+ #, php-format
397
+ msgid "If selected, visitors can subscribe to your events with the url: %s"
398
+ msgstr "Hvis valgt, kan besøgende abonnere på dine begivenheder med url'en: %s"
399
+
400
+ #: ../event-organiser-settings.php:375
401
+ msgid "Exclude events from searches:"
402
+ msgstr "Ekskluder begivenheder fra søgninger:"
403
+
404
+ #: ../event-organiser-settings.php:381
405
+ msgid "Enable templates:"
406
+ msgstr "Aktiver skabeloner:"
407
+
408
+ #: ../event-organiser-settings.php:383
409
+ msgid ""
410
+ "For each of the pages, the corresponding template is used. To use your own "
411
+ "template simply give it the same name and store in your theme folder. By "
412
+ "default, if Event Organiser cannot find a template in your theme directory, "
413
+ "it will use its own default template. To prevent this, uncheck this option. "
414
+ "WordPress will then decide which template from your theme's folder to use."
415
+ msgstr ""
416
+ "For hver side benyttes den tilsvarende skabelon. For at benytte din egen "
417
+ "skabelon, skal du navngive den det samme navn og gemme den i din tema-mappe. "
418
+ "Som standard, hvis Event Organiser ikke kan finde en skabelon i dit tema, "
419
+ "vil en standardskabelon benyttes i stedet. For at undgå dette skal fluebenet "
420
+ "fjernes fra denne indstilling. WordPress vil herefter afgøre hvilken "
421
+ "skabelon der skal benyttes fra din tema-mappe."
422
+
423
+ #: ../event-organiser-settings.php:385
424
+ msgid "Events archives:"
425
+ msgstr "Side til begivenhedsarkiver:"
426
+
427
+ #: ../event-organiser-settings.php:388
428
+ msgid "Event page:"
429
+ msgstr "Side til begivenhed:"
430
+
431
+ #: ../event-organiser-settings.php:391
432
+ msgid "Venue page:"
433
+ msgstr "Side til sted:"
434
+
435
+ #: ../event-organiser-settings.php:394
436
+ msgid "Events Category page:"
437
+ msgstr "Side til begivenhedskategori:"
438
+
439
+ #: ../event-organiser-settings.php:407
440
+ msgid ""
441
+ "Choose a custom permalink structure for events, venues, event categories and "
442
+ "event tags."
443
+ msgstr ""
444
+ "Vælg en brugerdefineret struktur til permalinks for begivenheder, steder, "
445
+ "begivenhedskategorier og -tags."
446
+
447
+ #: ../event-organiser-settings.php:409
448
+ msgid ""
449
+ "Please note to enable these structures you must first have pretty permalinks "
450
+ "enabled on WordPress in Settings > Permalinks."
451
+ msgstr ""
452
+ "Bemærk at dette kræver, at du har aktiveret pæne permalinks for WordPress "
453
+ "under Indstillinger > Permanente links."
454
+
455
+ #: ../event-organiser-settings.php:414
456
+ msgid "Enable event pretty permalinks:"
457
+ msgstr "Aktiver pæne permalinks til begivenheder"
458
+
459
+ #: ../event-organiser-settings.php:416
460
+ msgid ""
461
+ "If you have pretty permalinks enabled, select to have pretty premalinks for "
462
+ "events."
463
+ msgstr ""
464
+ "Hvis du har aktiveret pæne permalinks, vælg dette for også at aktivere det "
465
+ "til begivenheder."
466
+
467
+ #: ../event-organiser-settings.php:433 ../includes/event-organiser-cpt.php:56
468
+ msgid "Event Categories"
469
+ msgstr "Begivenhedskategorier"
470
+
471
+ #: ../event-organiser-settings.php:447
472
+ msgid ""
473
+ "Please note that you will need go to WordPress Settings > Permalinks and "
474
+ "click 'Save Changes' before any changes will take effect."
475
+ msgstr ""
476
+ "Bemærk at du skal gå til Indstillinger > Permanente links og klikke 'Gem "
477
+ "ændringer' før eventuelle ændringer vil træde i kraft."
478
+
479
+ #: ../event-organiser-edit.php:16
480
+ msgid "Event Details"
481
+ msgstr "Begivenhedsdetaljer"
482
+
483
+ #: ../event-organiser-edit.php:59
484
+ msgid "This is a reoccurring event"
485
+ msgstr "Dette er en gentagende begivenhed"
486
+
487
+ #: ../event-organiser-edit.php:61
488
+ msgid "Check to edit this event and its reoccurrences"
489
+ msgstr "Marker for at redigere denne begivenhed og dens gentagelser"
490
+
491
+ #: ../event-organiser-edit.php:66
492
+ #, php-format
493
+ msgid ""
494
+ "Ensure dates are entered in %1$s format and times in %2$s (24 hour) format"
495
+ msgstr ""
496
+ "Dobbelttjek at datoer er indtastet i formatet %1$s og tider i formatet %2$s "
497
+ "(24 timer)"
498
+
499
+ #: ../event-organiser-edit.php:82
500
+ msgid "All day"
501
+ msgstr "Hele dagen"
502
+
503
+ #: ../event-organiser-edit.php:88
504
+ msgid "Reoccurence:"
505
+ msgstr "Gentagelse:"
506
+
507
+ #: ../event-organiser-edit.php:92
508
+ msgid "once"
509
+ msgstr "ingen"
510
+
511
+ #: ../event-organiser-edit.php:93
512
+ msgid "daily"
513
+ msgstr "dagligt"
514
+
515
+ #: ../event-organiser-edit.php:94
516
+ msgid "weekly"
517
+ msgstr "ugentligt"
518
+
519
+ #: ../event-organiser-edit.php:95
520
+ msgid "monthly"
521
+ msgstr "månedligt"
522
+
523
+ #: ../event-organiser-edit.php:96
524
+ msgid "yearly"
525
+ msgstr "årligt"
526
+
527
+ #: ../event-organiser-edit.php:109
528
+ msgid "Repeat every "
529
+ msgstr "Gentag hver"
530
+
531
+ #: ../event-organiser-edit.php:115
532
+ #: ../classes/class-eo-event-list-widget.php:116
533
+ msgid "on"
534
+ msgstr "på"
535
+
536
+ #: ../event-organiser-edit.php:129
537
+ msgid "day of month"
538
+ msgstr "dag i måneden"
539
+
540
+ #: ../event-organiser-edit.php:131
541
+ msgid "day of week"
542
+ msgstr "dag i ugen"
543
+
544
+ #: ../event-organiser-edit.php:135
545
+ #: ../includes/event-organiser-register.php:132
546
+ #: ../includes/event-organiser-event-functions.php:536
547
+ msgid "until"
548
+ msgstr "indtil"
549
+
550
+ #: ../event-organiser-edit.php:158
551
+ #: ../includes/event-organiser-venue-functions.php:436
552
+ msgid "Select a venue"
553
+ msgstr "Vælg et sted"
554
+
555
+ #: ../event-organiser-edit.php:163
556
+ msgid "Search for a venue. To add a venues go to the venue page."
557
+ msgstr "Søg efter et sted. For at tilføje steder, gå til Steder-vinduet."
558
+
559
+ #: ../event-organiser-edit.php:317
560
+ msgid "You are not allowed to delete events."
561
+ msgstr "Du må ikke slette begivenheder."
562
+
563
+ #: ../event-organiser-calendar.php:10 ../event-organiser-calendar.php:11
564
+ msgid "Calendar View"
565
+ msgstr "Kalendervisning"
566
+
567
+ #: ../event-organiser-calendar.php:43
568
+ #: ../includes/event-organiser-register.php:21
569
+ msgid "today"
570
+ msgstr "i dag"
571
+
572
+ #: ../event-organiser-calendar.php:44
573
+ #: ../includes/event-organiser-register.php:22
574
+ #: ../includes/event-organiser-register.php:115
575
+ msgid "day"
576
+ msgstr "dag"
577
+
578
+ #: ../event-organiser-calendar.php:45
579
+ #: ../includes/event-organiser-register.php:23
580
+ #: ../includes/event-organiser-register.php:117
581
+ msgid "week"
582
+ msgstr "uge"
583
+
584
+ #: ../event-organiser-calendar.php:46
585
+ #: ../includes/event-organiser-register.php:24
586
+ #: ../includes/event-organiser-register.php:119
587
+ msgid "month"
588
+ msgstr "måned"
589
+
590
+ #: ../event-organiser-calendar.php:47
591
+ #: ../includes/event-organiser-register.php:25
592
+ msgid "go to date"
593
+ msgstr "gå til dato"
594
+
595
+ #: ../event-organiser-calendar.php:82
596
+ msgid "You do not have sufficient permissions to create events"
597
+ msgstr "Du har ikke rettigheder til at oprette begivenheder"
598
+
599
+ #: ../event-organiser-calendar.php:139
600
+ msgid "You do not have sufficient permissions to edit this event"
601
+ msgstr "Du har ikke rettigheder til at redigere denne begivenhed"
602
+
603
+ #: ../event-organiser-calendar.php:221
604
+ msgid "You do not have sufficient permissions to delete this event"
605
+ msgstr "Du har ikke rettigheder til at slette denne begivenhed"
606
+
607
+ #: ../event-organiser-calendar.php:227
608
+ msgid "Occurrence deleted."
609
+ msgstr "Gentagelse slettet."
610
+
611
+ #: ../event-organiser-calendar.php:241
612
+ msgid "Events Calendar"
613
+ msgstr "Begivenhedskalender"
614
+
615
+ #: ../event-organiser-calendar.php:244
616
+ msgid "Loading&#8230;"
617
+ msgstr "Indlæser&#8230;"
618
+
619
+ #: ../event-organiser-calendar.php:245
620
+ msgid "Day"
621
+ msgstr "Dag"
622
+
623
+ #: ../event-organiser-calendar.php:246
624
+ msgid "Week"
625
+ msgstr "Uge"
626
+
627
+ #: ../event-organiser-calendar.php:247
628
+ msgid "Month"
629
+ msgstr "Måned"
630
+
631
+ #: ../event-organiser-calendar.php:250
632
+ msgid "Current date/time"
633
+ msgstr "Nuværende tidspunkt"
634
+
635
+ #: ../event-organiser-calendar.php:258
636
+ msgid "When"
637
+ msgstr "Hvornår"
638
+
639
+ #: ../event-organiser-calendar.php:262
640
+ msgid "Event Title"
641
+ msgstr "Begivenhedstitel"
642
+
643
+ #: ../event-organiser-calendar.php:266
644
+ #: ../includes/event-organiser-ajax.php:193
645
+ msgid "Where"
646
+ msgstr "Hvor"
647
+
648
+ #: ../event-organiser-calendar.php:291
649
+ msgid "Save Draft"
650
+ msgstr "Gem kladde"
651
+
652
+ #: ../event-organiser-calendar.php:293
653
+ msgid "Publish Event"
654
+ msgstr "Udgiv begivenhed"
655
+
656
+ #: ../event-organiser-calendar.php:299
657
+ msgid "Cancel"
658
+ msgstr "Annuller"
659
+
660
+ #: ../event-organiser-calendar.php:300
661
+ msgid "Submit for Review"
662
+ msgstr "Indsend til gennemgang"
663
+
664
+ #: ../includes/event-organiser-register.php:113
665
+ msgid "Hour"
666
+ msgstr "Time"
667
+
668
+ #: ../includes/event-organiser-register.php:114
669
+ msgid "Minute"
670
+ msgstr "Minut"
671
+
672
+ #: ../includes/event-organiser-register.php:116
673
+ msgid "days"
674
+ msgstr "dage"
675
+
676
+ #: ../includes/event-organiser-register.php:118
677
+ msgid "weeks"
678
+ msgstr "uger"
679
+
680
+ #: ../includes/event-organiser-register.php:120
681
+ msgid "months"
682
+ msgstr "måneder"
683
+
684
+ #: ../includes/event-organiser-register.php:121
685
+ msgid "year"
686
+ msgstr "år"
687
+
688
+ #: ../includes/event-organiser-register.php:122
689
+ msgid "years"
690
+ msgstr "år"
691
+
692
+ #: ../includes/event-organiser-register.php:123
693
+ #: ../includes/event-organiser-event-functions.php:475
694
+ msgid "every day"
695
+ msgstr "hver dag"
696
+
697
+ #: ../includes/event-organiser-register.php:124
698
+ #: ../includes/event-organiser-event-functions.php:477
699
+ #, php-format
700
+ msgid "every %d days"
701
+ msgstr "hver %d. dag"
702
+
703
+ #: ../includes/event-organiser-register.php:125
704
+ #: ../includes/event-organiser-event-functions.php:483
705
+ msgid "every week on"
706
+ msgstr "hver uge på en"
707
+
708
+ #: ../includes/event-organiser-register.php:126
709
+ #: ../includes/event-organiser-event-functions.php:485
710
+ #, php-format
711
+ msgid "every %d weeks on"
712
+ msgstr "hver %d. uge på en"
713
+
714
+ #: ../includes/event-organiser-register.php:127
715
+ #: ../includes/event-organiser-event-functions.php:497
716
+ msgid "every month on the"
717
+ msgstr "hver måned på den"
718
+
719
+ #: ../includes/event-organiser-register.php:128
720
+ #: ../includes/event-organiser-event-functions.php:499
721
+ #, php-format
722
+ msgid "every %d months on the"
723
+ msgstr "hver %d. måned på den"
724
+
725
+ #: ../includes/event-organiser-register.php:129
726
+ msgid "every year on the"
727
+ msgstr "hvert år på den"
728
+
729
+ #: ../includes/event-organiser-register.php:130
730
+ #, php-format
731
+ msgid "every %d years on the"
732
+ msgstr "hvert %d. år på den"
733
+
734
+ #: ../includes/event-organiser-register.php:131
735
+ msgid "This event will repeat"
736
+ msgstr "Denne begivenhed vil gentages"
737
+
738
+ #: ../includes/event-organiser-register.php:133
739
+ #: ../includes/event-organiser-event-functions.php:458
740
+ msgid "first"
741
+ msgstr "første"
742
+
743
+ #: ../includes/event-organiser-register.php:133
744
+ #: ../includes/event-organiser-event-functions.php:458
745
+ msgid "second"
746
+ msgstr "anden"
747
+
748
+ #: ../includes/event-organiser-register.php:133
749
+ #: ../includes/event-organiser-event-functions.php:458
750
+ msgid "third"
751
+ msgstr "tredje"
752
+
753
+ #: ../includes/event-organiser-register.php:133
754
+ #: ../includes/event-organiser-event-functions.php:458
755
+ msgid "fourth"
756
+ msgstr "fjerde"
757
+
758
+ #: ../includes/event-organiser-register.php:133
759
+ #: ../includes/event-organiser-event-functions.php:458
760
+ msgid "last"
761
+ msgstr "sidste"
762
+
763
+ #: ../includes/event-organiser-event-functions.php:468
764
+ msgid "one time only"
765
+ msgstr "ingen"
766
+
767
+ #: ../includes/event-organiser-event-functions.php:528
768
+ msgid "every year"
769
+ msgstr "hvert år"
770
+
771
+ #: ../includes/event-organiser-event-functions.php:530
772
+ #, php-format
773
+ msgid "every %d years"
774
+ msgstr "hvert %d. år"
775
+
776
+ #: ../includes/class-event-organiser-im-export.php:60
777
+ #, php-format
778
+ msgid "File Error encountered: %d"
779
+ msgstr "Fejl i filupload: %d"
780
+
781
+ #: ../includes/class-event-organiser-im-export.php:66
782
+ msgid "No file detected."
783
+ msgstr "Ingen fil registreret."
784
+
785
+ #: ../includes/class-event-organiser-im-export.php:69
786
+ msgid ""
787
+ "Invalid file uploaded. The file must be a ics calendar file, no larger than "
788
+ "2MB."
789
+ msgstr ""
790
+ "Ugyldig fil uploadet. Filen skal være en kalenderfil af typen ics og ikke "
791
+ "større end 2MB."
792
+
793
+ #: ../includes/class-event-organiser-im-export.php:86
794
+ msgid "Export Events"
795
+ msgstr "Eksporter begivenheder"
796
+
797
+ #: ../includes/class-event-organiser-im-export.php:87
798
+ msgid ""
799
+ "The export button below generates an ICS file of your events that can be "
800
+ "imported in to other calendar applications such as Google Calendar."
801
+ msgstr ""
802
+ "Eksport-knappen nedenfor genererer en ICS-fil af dine begivenheder, som kan "
803
+ "importeres af andre kalender-applikationer såsom Google Calendar."
804
+
805
+ #: ../includes/class-event-organiser-im-export.php:92
806
+ msgid "Download Export File"
807
+ msgstr "Download eksportfil"
808
+
809
+ #: ../includes/class-event-organiser-im-export.php:97
810
+ msgid "Import Events"
811
+ msgstr "Importer begivenheder"
812
+
813
+ #: ../includes/class-event-organiser-im-export.php:99
814
+ msgid "Import an ICS file."
815
+ msgstr "Importer en ICS-fil."
816
+
817
+ #: ../includes/class-event-organiser-im-export.php:102
818
+ msgid "Import venues"
819
+ msgstr "Importer steder"
820
+
821
+ #: ../includes/class-event-organiser-im-export.php:103
822
+ msgid "Import categories"
823
+ msgstr "Importer kategorier"
824
+
825
+ #: ../includes/class-event-organiser-im-export.php:106
826
+ msgid "Upload ICS file"
827
+ msgstr "Upload ICS-fil"
828
+
829
+ #: ../includes/class-event-organiser-im-export.php:299
830
+ msgid "You do not have sufficient permissions to import events."
831
+ msgstr "Du har ikke rettigheder til at importere begivenheder."
832
+
833
+ #: ../includes/class-event-organiser-im-export.php:438
834
+ msgid "No events were imported."
835
+ msgstr "Ingen begivenheder blev importeret."
836
+
837
+ #: ../includes/class-event-organiser-im-export.php:444
838
+ msgid "1 event was successfully imported"
839
+ msgstr "1 begivenhed blev importeret"
840
+
841
+ #: ../includes/class-event-organiser-im-export.php:446
842
+ #, php-format
843
+ msgid "%d events were successfully imported"
844
+ msgstr "%d begivenheder blev importeret"
845
+
846
+ #: ../includes/class-event-organiser-im-export.php:449
847
+ msgid "1 venue was created"
848
+ msgstr "1 sted blev oprettet"
849
+
850
+ #: ../includes/class-event-organiser-im-export.php:451
851
+ #, php-format
852
+ msgid "%d venues were created"
853
+ msgstr "%d steder blev oprettet"
854
+
855
+ #: ../includes/class-event-organiser-im-export.php:455
856
+ msgid "1 category was created"
857
+ msgstr "1 kategori blev oprettet"
858
+
859
+ #: ../includes/class-event-organiser-im-export.php:457
860
+ #, php-format
861
+ msgid "%d categories were created"
862
+ msgstr "%d kategorier blev oprettet"
863
+
864
+ #: ../includes/event-organiser-cpt.php:26
865
+ msgid "Event Venues"
866
+ msgstr "Begivenhedssted