Initial Kohana install
[speedfreak] / Server / system / libraries / Calendar_Event.php
1 <?php defined('SYSPATH') OR die('No direct access allowed.');
2 /**
3  * Calendar event observer class.
4  *
5  * $Id: Calendar_Event.php 4129 2009-03-27 17:47:03Z zombor $
6  *
7  * @package    Calendar
8  * @author     Kohana Team
9  * @copyright  (c) 2007-2008 Kohana Team
10  * @license    http://kohanaphp.com/license.html
11  */
12 class Calendar_Event_Core extends Event_Observer {
13
14         // Boolean conditions
15         protected $booleans = array
16         (
17                 'current',
18                 'weekend',
19                 'first_day',
20                 'last_day',
21                 'last_occurrence',
22                 'easter',
23         );
24
25         // Rendering conditions
26         protected $conditions = array();
27
28         // Cell classes
29         protected $classes = array();
30
31         // Cell output
32         protected $output = '';
33
34         /**
35          * Adds a condition to the event. The condition can be one of the following:
36          *
37          * timestamp       - UNIX timestamp
38          * day             - day number (1-31)
39          * week            - week number (1-5)
40          * month           - month number (1-12)
41          * year            - year number (4 digits)
42          * day_of_week     - day of week (1-7)
43          * current         - active month (boolean) (only show data for the month being rendered)
44          * weekend         - weekend day (boolean)
45          * first_day       - first day of month (boolean)
46          * last_day        - last day of month (boolean)
47          * occurrence      - occurrence of the week day (1-5) (use with "day_of_week")
48          * last_occurrence - last occurrence of week day (boolean) (use with "day_of_week")
49          * easter          - Easter day (boolean)
50          * callback        - callback test (boolean)
51          *
52          * To unset a condition, call condition with a value of NULL.
53          *
54          * @chainable
55          * @param   string  condition key
56          * @param   mixed   condition value
57          * @return  object
58          */
59         public function condition($key, $value)
60         {
61                 if ($value === NULL)
62                 {
63                         unset($this->conditions[$key]);
64                 }
65                 else
66                 {
67                         if ($key === 'callback')
68                         {
69                                 // Do nothing
70                         }
71                         elseif (in_array($key, $this->booleans))
72                         {
73                                 // Make the value boolean
74                                 $value = (bool) $value;
75                         }
76                         else
77                         {
78                                 // Make the value an int
79                                 $value = (int) $value;
80                         }
81
82                         $this->conditions[$key] = $value;
83                 }
84
85                 return $this;
86         }
87
88         /**
89          * Add a CSS class for this event. This can be called multiple times.
90          *
91          * @chainable
92          * @param   string  CSS class name
93          * @return  object
94          */
95         public function add_class($class)
96         {
97                 $this->classes[$class] = $class;
98
99                 return $this;
100         }
101
102         /**
103          * Remove a CSS class for this event. This can be called multiple times.
104          *
105          * @chainable
106          * @param   string  CSS class name
107          * @return  object
108          */
109         public function remove_class($class)
110         {
111                 unset($this->classes[$class]);
112
113                 return $this;
114         }
115
116         /**
117          * Set HTML output for this event.
118          *
119          * @chainable
120          * @param   string  HTML output
121          * @return  object
122          */
123         public function output($str)
124         {
125                 $this->output = $str;
126
127                 return $this;
128         }
129
130         /**
131          * Add a CSS class for this event. This can be called multiple times.
132          *
133          * @chainable
134          * @param   string  CSS class name
135          * @return  object
136          */
137         public function notify($data)
138         {
139                 // Split the date and current status
140                 list ($month, $day, $year, $week, $current) = $data;
141
142                 // Get a timestamp for the day
143                 $timestamp = mktime(0, 0, 0, $month, $day, $year);
144
145                 // Date conditionals
146                 $condition = array
147                 (
148                         'timestamp'   => (int) $timestamp,
149                         'day'         => (int) date('j', $timestamp),
150                         'week'        => (int) $week,
151                         'month'       => (int) date('n', $timestamp),
152                         'year'        => (int) date('Y', $timestamp),
153                         'day_of_week' => (int) date('w', $timestamp),
154                         'current'     => (bool) $current,
155                 );
156
157                 // Tested conditions
158                 $tested = array();
159
160                 foreach ($condition as $key => $value)
161                 {
162                         // Timestamps need to be handled carefully
163                         if($key === 'timestamp' AND isset($this->conditions['timestamp']))
164                         {
165                                 // This adds 23 hours, 59 minutes and 59 seconds to today's timestamp, as 24 hours
166                                 // is classed as a new day
167                                 $next_day = $timestamp + 86399;
168                                 
169                                 if($this->conditions['timestamp'] < $timestamp OR $this->conditions['timestamp'] > $next_day)
170                                         return FALSE;
171                         }
172                         // Test basic conditions first
173                         elseif (isset($this->conditions[$key]) AND $this->conditions[$key] !== $value)
174                                 return FALSE;
175
176                         // Condition has been tested
177                         $tested[$key] = TRUE;
178                 }
179
180                 if (isset($this->conditions['weekend']))
181                 {
182                         // Weekday vs Weekend
183                         $condition['weekend'] = ($condition['day_of_week'] === 0 OR $condition['day_of_week'] === 6);
184                 }
185
186                 if (isset($this->conditions['first_day']))
187                 {
188                         // First day of month
189                         $condition['first_day'] = ($condition['day'] === 1);
190                 }
191
192                 if (isset($this->conditions['last_day']))
193                 {
194                         // Last day of month
195                         $condition['last_day'] = ($condition['day'] === (int) date('t', $timestamp));
196                 }
197
198                 if (isset($this->conditions['occurrence']))
199                 {
200                         // Get the occurance of the current day
201                         $condition['occurrence'] = $this->day_occurrence($timestamp);
202                 }
203
204                 if (isset($this->conditions['last_occurrence']))
205                 {
206                         // Test if the next occurance of this date is next month
207                         $condition['last_occurrence'] = ((int) date('n', $timestamp + 604800) !== $condition['month']);
208                 }
209
210                 if (isset($this->conditions['easter']))
211                 {
212                         if ($condition['month'] === 3 OR $condition['month'] === 4)
213                         {
214                                 // This algorithm is from Practical Astronomy With Your Calculator, 2nd Edition by Peter
215                                 // Duffett-Smith. It was originally from Butcher's Ecclesiastical Calendar, published in
216                                 // 1876. This algorithm has also been published in the 1922 book General Astronomy by
217                                 // Spencer Jones; in The Journal of the British Astronomical Association (Vol.88, page
218                                 // 91, December 1977); and in Astronomical Algorithms (1991) by Jean Meeus.
219
220                                 $a = $condition['year'] % 19;
221                                 $b = (int) ($condition['year'] / 100);
222                                 $c = $condition['year'] % 100;
223                                 $d = (int) ($b / 4);
224                                 $e = $b % 4;
225                                 $f = (int) (($b + 8) / 25);
226                                 $g = (int) (($b - $f + 1) / 3);
227                                 $h = (19 * $a + $b - $d - $g + 15) % 30;
228                                 $i = (int) ($c / 4);
229                                 $k = $c % 4;
230                                 $l = (32 + 2 * $e + 2 * $i - $h - $k) % 7;
231                                 $m = (int) (($a + 11 * $h + 22 * $l) / 451);
232                                 $p = ($h + $l - 7 * $m + 114) % 31;
233
234                                 $month = (int) (($h + $l - 7 * $m + 114) / 31);
235                                 $day = $p + 1;
236
237                                 $condition['easter'] = ($condition['month'] === $month AND $condition['day'] === $day);
238                         }
239                         else
240                         {
241                                 // Easter can only happen in March or April
242                                 $condition['easter'] = FALSE;
243                         }
244                 }
245
246                 if (isset($this->conditions['callback']))
247                 {
248                         // Use a callback to determine validity
249                         $condition['callback'] = call_user_func($this->conditions['callback'], $condition, $this);
250                 }
251
252                 $conditions = array_diff_key($this->conditions, $tested);
253
254                 foreach ($conditions as $key => $value)
255                 {
256                         if ($key === 'callback')
257                         {
258                                 // Callbacks are tested on a TRUE/FALSE basis
259                                 $value = TRUE;
260                         }
261
262                         // Test advanced conditions
263                         if ($condition[$key] !== $value)
264                                 return FALSE;
265                 }
266
267                 $this->caller->add_data(array
268                 (
269                         'classes' => $this->classes,
270                         'output'  => $this->output,
271                 ));
272         }
273
274         /**
275          * Find the week day occurrence for a specific timestamp. The occurrence is
276          * relative to the current month. For example, the second Saturday of any
277          * given month will return "2" as the occurrence. This is used in combination
278          * with the "occurrence" condition.
279          *
280          * @param   integer  UNIX timestamp
281          * @return  integer
282          */
283         protected function day_occurrence($timestamp)
284         {
285                 // Get the current month for the timestamp
286                 $month = date('m', $timestamp);
287
288                 // Default occurrence is one
289                 $occurrence = 1;
290
291                 // Reduce the timestamp by one week for each loop. This has the added
292                 // benefit of preventing an infinite loop.
293                 while ($timestamp -= 604800)
294                 {
295                         if (date('m', $timestamp) !== $month)
296                         {
297                                 // Once the timestamp has gone into the previous month, the
298                                 // proper occurrence has been found.
299                                 return $occurrence;
300                         }
301
302                         // Increment the occurrence
303                         $occurrence++;
304                 }
305         }
306
307 } // End Calendar Event