1<?php defined('SYSPATH') OR die('No direct access allowed.'); 2/** 3 * Calendar creation library. 4 * 5 * $Id: Calendar.php 3769 2008-12-15 00:48:56Z zombor $ 6 * 7 * @package Calendar 8 * @author Kohana Team 9 * @copyright (c) 2007-2008 Kohana Team 10 * @license http://kohanaphp.com/license.html 11 */ 12class Calendar_Core extends Event_Subject { 13 14 // Start the calendar on Sunday by default 15 public static $start_monday = FALSE; 16 17 // Month and year to use for calendaring 18 protected $month; 19 protected $year; 20 21 // Week starts on Sunday 22 protected $week_start = 0; 23 24 // Observed data 25 protected $observed_data; 26 27 /** 28 * Returns an array of the names of the days, using the current locale. 29 * 30 * @param integer left of day names 31 * @return array 32 */ 33 public static function days($length = TRUE) 34 { 35 // strftime day format 36 $format = ($length > 3) ? '%A' : '%a'; 37 38 // Days of the week 39 $days = array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'); 40 41 if (Calendar::$start_monday === TRUE) 42 { 43 // Push Sunday to the end of the days 44 array_push($days, array_shift($days)); 45 } 46 47 if (strpos(Kohana::config('locale.language.0'), 'en') !== 0) 48 { 49 // This is a bit awkward, but it works properly and is reliable 50 foreach ($days as $i => $day) 51 { 52 // Convert the English names to i18n names 53 $days[$i] = strftime($format, strtotime($day)); 54 } 55 } 56 57 if (is_int($length) OR ctype_digit($length)) 58 { 59 foreach ($days as $i => $day) 60 { 61 // Shorten the days to the expected length 62 $days[$i] = utf8::substr($day, 0, $length); 63 } 64 } 65 66 return $days; 67 } 68 69 /** 70 * Create a new Calendar instance. A month and year can be specified. 71 * By default, the current month and year are used. 72 * 73 * @param integer month number 74 * @param integer year number 75 * @return object 76 */ 77 public static function factory($month = NULL, $year = NULL) 78 { 79 return new Calendar($month, $year); 80 } 81 82 /** 83 * Create a new Calendar instance. A month and year can be specified. 84 * By default, the current month and year are used. 85 * 86 * @param integer month number 87 * @param integer year number 88 * @return void 89 */ 90 public function __construct($month = NULL, $year = NULL) 91 { 92 empty($month) and $month = date('n'); // Current month 93 empty($year) and $year = date('Y'); // Current year 94 95 // Set the month and year 96 $this->month = (int) $month; 97 $this->year = (int) $year; 98 99 if (Calendar::$start_monday === TRUE) 100 { 101 // Week starts on Monday 102 $this->week_start = 1; 103 } 104 } 105 106 /** 107 * Allows fetching the current month and year. 108 * 109 * @param string key to get 110 * @return mixed 111 */ 112 public function __get($key) 113 { 114 if ($key === 'month' OR $key === 'year') 115 { 116 return $this->$key; 117 } 118 } 119 120 /** 121 * Calendar_Event factory method. 122 * 123 * @param string unique name for the event 124 * @return object Calendar_Event 125 */ 126 public function event($name = NULL) 127 { 128 return new Calendar_Event($this); 129 } 130 131 /** 132 * Calendar_Event factory method. 133 * 134 * @chainable 135 * @param string standard event type 136 * @return object 137 */ 138 public function standard($name) 139 { 140 switch ($name) 141 { 142 case 'today': 143 // Add an event for the current day 144 $this->attach($this->event()->condition('timestamp', strtotime('today'))->add_class('today')); 145 break; 146 case 'prev-next': 147 // Add an event for padding days 148 $this->attach($this->event()->condition('current', FALSE)->add_class('prev-next')); 149 break; 150 case 'holidays': 151 // Base event 152 $event = $this->event()->condition('current', TRUE)->add_class('holiday'); 153 154 // Attach New Years 155 $holiday = clone $event; 156 $this->attach($holiday->condition('month', 1)->condition('day', 1)); 157 158 // Attach Valentine's Day 159 $holiday = clone $event; 160 $this->attach($holiday->condition('month', 2)->condition('day', 14)); 161 162 // Attach St. Patrick's Day 163 $holiday = clone $event; 164 $this->attach($holiday->condition('month', 3)->condition('day', 17)); 165 166 // Attach Easter 167 $holiday = clone $event; 168 $this->attach($holiday->condition('easter', TRUE)); 169 170 // Attach Memorial Day 171 $holiday = clone $event; 172 $this->attach($holiday->condition('month', 5)->condition('day_of_week', 1)->condition('last_occurrence', TRUE)); 173 174 // Attach Independance Day 175 $holiday = clone $event; 176 $this->attach($holiday->condition('month', 7)->condition('day', 4)); 177 178 // Attach Labor Day 179 $holiday = clone $event; 180 $this->attach($holiday->condition('month', 9)->condition('day_of_week', 1)->condition('occurrence', 1)); 181 182 // Attach Halloween 183 $holiday = clone $event; 184 $this->attach($holiday->condition('month', 10)->condition('day', 31)); 185 186 // Attach Thanksgiving 187 $holiday = clone $event; 188 $this->attach($holiday->condition('month', 11)->condition('day_of_week', 4)->condition('occurrence', 4)); 189 190 // Attach Christmas 191 $holiday = clone $event; 192 $this->attach($holiday->condition('month', 12)->condition('day', 25)); 193 break; 194 case 'weekends': 195 // Weekend events 196 $this->attach($this->event()->condition('weekend', TRUE)->add_class('weekend')); 197 break; 198 } 199 200 return $this; 201 } 202 203 /** 204 * Returns an array for use with a view. The array contains an array for 205 * each week. Each week contains 7 arrays, with a day number and status: 206 * TRUE if the day is in the month, FALSE if it is padding. 207 * 208 * @return array 209 */ 210 public function weeks() 211 { 212 // First day of the month as a timestamp 213 $first = mktime(1, 0, 0, $this->month, 1, $this->year); 214 215 // Total number of days in this month 216 $total = (int) date('t', $first); 217 218 // Last day of the month as a timestamp 219 $last = mktime(1, 0, 0, $this->month, $total, $this->year); 220 221 // Make the month and week empty arrays 222 $month = $week = array(); 223 224 // Number of days added. When this reaches 7, start a new week 225 $days = 0; 226 $week_number = 1; 227 228 if (($w = (int) date('w', $first) - $this->week_start) < 0) 229 { 230 $w = 6; 231 } 232 233 if ($w > 0) 234 { 235 // Number of days in the previous month 236 $n = (int) date('t', mktime(1, 0, 0, $this->month - 1, 1, $this->year)); 237 238 // i = number of day, t = number of days to pad 239 for ($i = $n - $w + 1, $t = $w; $t > 0; $t--, $i++) 240 { 241 // Notify the listeners 242 $this->notify(array($this->month - 1, $i, $this->year, $week_number, FALSE)); 243 244 // Add previous month padding days 245 $week[] = array($i, FALSE, $this->observed_data); 246 $days++; 247 } 248 } 249 250 // i = number of day 251 for ($i = 1; $i <= $total; $i++) 252 { 253 if ($days % 7 === 0) 254 { 255 // Start a new week 256 $month[] = $week; 257 $week = array(); 258 259 $week_number++; 260 } 261 262 // Notify the listeners 263 $this->notify(array($this->month, $i, $this->year, $week_number, TRUE)); 264 265 // Add days to this month 266 $week[] = array($i, TRUE, $this->observed_data); 267 $days++; 268 } 269 270 if (($w = (int) date('w', $last) - $this->week_start) < 0) 271 { 272 $w = 6; 273 } 274 275 if ($w >= 0) 276 { 277 // i = number of day, t = number of days to pad 278 for ($i = 1, $t = 6 - $w; $t > 0; $t--, $i++) 279 { 280 // Notify the listeners 281 $this->notify(array($this->month + 1, $i, $this->year, $week_number, FALSE)); 282 283 // Add next month padding days 284 $week[] = array($i, FALSE, $this->observed_data); 285 } 286 } 287 288 if ( ! empty($week)) 289 { 290 // Append the remaining days 291 $month[] = $week; 292 } 293 294 return $month; 295 } 296 297 /** 298 * Adds new data from an observer. All event data contains and array of CSS 299 * classes and an array of output messages. 300 * 301 * @param array observer data. 302 * @return void 303 */ 304 public function add_data(array $data) 305 { 306 // Add new classes 307 $this->observed_data['classes'] += $data['classes']; 308 309 if ( ! empty($data['output'])) 310 { 311 // Only add output if it's not empty 312 $this->observed_data['output'][] = $data['output']; 313 } 314 } 315 316 /** 317 * Resets the observed data and sends a notify to all attached events. 318 * 319 * @param array UNIX timestamp 320 * @return void 321 */ 322 public function notify($data) 323 { 324 // Reset observed data 325 $this->observed_data = array 326 ( 327 'classes' => array(), 328 'output' => array(), 329 ); 330 331 // Send a notify 332 parent::notify($data); 333 } 334 335 /** 336 * Convert the calendar to HTML using the kohana_calendar view. 337 * 338 * @return string 339 */ 340 public function render() 341 { 342 $view = new View('kohana_calendar', array 343 ( 344 'month' => $this->month, 345 'year' => $this->year, 346 'weeks' => $this->weeks(), 347 )); 348 349 return $view->render(); 350 } 351 352 /** 353 * Magically convert this object to a string, the rendered calendar. 354 * 355 * @return string 356 */ 357 public function __toString() 358 { 359 return $this->render(); 360 } 361 362} // End Calendar